Android 3.1 USB-Host - BroadcastReceiver 不接收 USB_DEVICE_ATTACHED
Posted
技术标签:
【中文标题】Android 3.1 USB-Host - BroadcastReceiver 不接收 USB_DEVICE_ATTACHED【英文标题】:Android 3.1 USB-Host - BroadcastReceiver does not receive USB_DEVICE_ATTACHED 【发布时间】:2011-10-22 08:14:21 【问题描述】:我通过the description and samples for USB host at developer.android.com 检测连接和分离的 USB 设备。
如果我在连接设备时在清单文件中使用意图过滤器来启动我的应用程序,它可以正常工作:插入,检测到设备,android 请求启动应用程序的权限,设备信息显示在一张桌子。
我正在开发的应用程序不应仅在连接/分离设备时启动/完成(例如数据管理目的)。如果应用程序已经在运行,我也不希望打开对话框弹出。因此,我决定在连接设备时不直接启动活动,而是注册一个 BroadcastReceiver,它(稍后)应该在设备处于/分离状态时通知活动。这个接收器可以很好地识别分离动作,但不能识别附加动作。
我是否缺少权限或数据属性或类似的东西?教程和示例没有说明额外的必要属性。
这是清单文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="de.visira.smartfdr"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="12" />
<uses-feature android:name="android.hardware.usb.host" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<receiver android:name=".usb.Detector">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_DETACHED"
android:resource="@xml/device_filter" />
</receiver>
</application>
接收者:
public class FDRDetector extends BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
String action = intent.getAction();
Toast.makeText(context, "Action: " + action, 3).show();
// pops up only if action == DETACHED
我不明白为什么相同的意图过滤器可以工作,如果我在活动上使用它们,但如果它们应用于接收器则不行?即使我在代码中设置了接收器和过滤器,也无法识别附件。
我的工作环境: IDE:带有 Android 插件的 Eclipse 3.7
设备:Acer Iconia Tab A500
安卓:3.1
提前致谢
【问题讨论】:
【参考方案1】:啊哈!我想到了。我遇到了完全相同的问题。
它的要点是 - 如果您在插入设备时自动启动应用程序(使用清单文件),那么 Android 系统 似乎获得了 ACTION_USB_DEVICE_ATTACHED 意图,然后因为它知道您的应用程序想要在这种情况下运行,它实际上向您的应用程序发送 android.intent.action.MAIN 意图。它从不向您的应用程序发送 ACTION_USB_DEVICE_ATTACHED 操作,因为它认为它已经知道您的应用程序在这种情况下想要做什么。
我刚刚发现了问题,我想我有一个解决方案,但我可以告诉你我发现了什么:
即使您的应用程序正在运行并且在前台,当您插入 USB 设备并且 Android 系统获取 ACTION_USB_DEVICE_ATTACHED Intent 时,它也会在您的活动中调用 onResume()。
很遗憾,您不能这样做:
@Override
public void onResume()
super.onResume();
Intent intent = getIntent();
Log.d(TAG, "intent: " + intent);
String action = intent.getAction();
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action))
//do something
因为 Intent 将以 android.intent.action.MAIN 的形式返回,而不是 ACTION_USB_DEVICE_ATTACHED。
令人讨厌的是,如果您只是离开应用程序,但不拔掉 USB,您也会得到 android.intent.action.MAIN。我想让设备进入睡眠状态并重新唤醒它会做同样的事情。
因此,根据我的发现,您无法直接获得意图,但似乎您可以依赖 onResume() 在插入 USB 设备时被调用,因此解决方案是检查以查看如果每次收到 onResume 时都连接了 USB。您还可以在 USB 断开连接时设置一个标志,因为 USB 断开连接意图当然可以正常触发。
所以总的来说,您的广播接收器可能如下所示:
// BroadcastReceiver when remove the device USB plug from a USB port
BroadcastReceiver mUsbReceiver = new BroadcastReceiver()
public void onReceive(Context context, Intent intent)
String action = intent.getAction();
if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action))
usbConnected=false;
;
你会在 onCreate 里面有这个:
// listen for new devices
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(mUsbReceiver, filter);
这位于清单中的活动标签内:
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
您的 /res/xml/ 文件夹中将有一个 device_filter.xml 文件,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="1027" product-id="24577" />
<usb-device vendor-id="1118" product-id="688" />
</resources>
(当然可以使用您需要的任何供应商 ID 和产品 ID)
然后你的 onCreate 看起来像这样:
@Override
public void onResume()
super.onResume();
Intent intent = getIntent();
Log.d(TAG, "intent: " + intent);
String action = intent.getAction();
if (usbConnected==false )
//check to see if USB is now connected
我没有用于检查 USB 是否已连接的特定代码,因为我实际上还没有深入研究。我正在使用一个可以连接的库,所以对于我的应用程序,我可以启动那个循环,我很好。
将清单中 Activity 的启动模式设置为“singleTask”也很重要,以防止它在已经运行时再次运行,否则插入 USB 设备只会启动应用程序的第二个实例!
所以我的清单中的整个活动标签如下所示:
<activity
android:label="@string/app_name"
android:name="com.awitness.common.TorqueTablet"
android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTask"
>
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
无论如何,我希望这对某人有所帮助!我很惊讶我已经无法找到解决方案!
【讨论】:
尝试覆盖 onNewIntent() - 当您使用 android:launchMode="singleTop" 时会调用它,并且可能会很好地解决您的情况。 onResume() 总是在之后调用。 你能解释一下你的第一段“它的要点是”吗?我看起来很困惑。 谢谢,我一直在想同样的事情。很高兴知道这是行不通的。对我来说闻起来像一个 Android 错误。 在你的activity的onCreate和onResume中,如果你注册USB_DEVICE_ATTACHED
intent,你可以检查intent看它是否有设备作为parecelable extra:UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) Log.d(TAG, "USB Attached: " + device); //do something
好吧,如果我不为我的应用程序使用活动并且简单地只有一个服务和一个广播接收器怎么办?广播接收者如何获得 USB.DEVICE_ATTACHED 意图?【参考方案2】:
继续 @Gusdor 的深刻评论 (+1):我在 @Gusdor 指出的onNewIntent()
中实施了检查,当您的活动 launchMode
设置为 singleTask
或 @987654324 时调用@。然后,不要像接受的答案建议的那样检查布尔标志,只需使用LocalBroadcastManager
将意图传递给您的 USB 广播接收器。例如,
@Override
protected void onNewIntent(Intent intent)
super.onNewIntent(intent);
if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(intent.getAction()))
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
然后,无论您在哪里注册现有(系统)USB 广播接收器,只需向本地广播管理器实例注册相同的接收器,即,
@Override
protected void onResume()
super.onResume();
myContext.registerReceiver(myUsbBroadcastReceiver, myIntent); // system receiver
LocalBroadcastManager.getInstance(myContext).registerReceiver(myUsbBroadcastReceiver, intent); // local receiver
@Override
protected void onPause()
super.onResume();
myContext.unregisterReceiver(myUsbBroadcastReceiver); // system receiver
LocalBroadcastManager.getInstance(myContext).unregisterReceiver(myUsbBroadcastReceiver); // local receiver
您可以发送另一个系统广播而不是本地广播,但我认为您无法使用操作 UsbManager.ACTION_USB_ACCESSORY_ATTACHED
(系统会将其视为潜在的安全风险),因此您必须定义自己的行动。没什么大不了的,但为什么要麻烦,尤其是本地广播没有 IPC 开销。
【讨论】:
【参考方案3】:在应用程序中创建广播接收器,而不是清单,允许您的应用程序仅在运行时处理分离的事件。这样,分离的事件只会发送到当前正在运行的应用程序,而不是广播到所有应用程序。
【讨论】:
以上是关于Android 3.1 USB-Host - BroadcastReceiver 不接收 USB_DEVICE_ATTACHED的主要内容,如果未能解决你的问题,请参考以下文章