我通过the description and samples for USB host at developer.android.com检测连接和分离的USB设备。
如果我在清单文件中使用intent-filter在连接设备时启动我的应用程序,它可以正常工作:插入,检测到设备,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
}
我不明白为什么相同的intent-filter工作,如果我在一个活动上使用它们,但是如果它们应用于接收器则不行?即使我设置了接收器并在代码中进行过滤,也无法识别附件。
我的工作环境: IDE:带有Android插件的Eclipse 3.7
设备:Acer Iconia Tab A500
Android:3.1
提前致谢
答案 0 :(得分:36)
啊哈!我想到了。我遇到了同样的问题。
它的要点是 - 如果您在插入设备时自动启动应用程序(使用清单文件),则表明Android 系统获取ACTION_USB_DEVICE_ATTACHED意图,然后它知道你的应用程序想要在那种情况下运行,它实际上会向你的应用程序发送android.intent.action.MAIN意图。它永远不会将ACTION_USB_DEVICE_ATTACHED操作发送到您的应用程序,因为它认为它已经知道您的应用程序在这种情况下想要做什么。
我刚刚发现了问题,我想我有一个解决方案,但我可以告诉你我发现了什么:
即使您的应用正在运行并且在前台,当您插入USB设备并且Android系统获得ACTION_USB_DEVICE_ATTACHED意图时,它将在您的活动中调用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
}
}
因为意图会以android.intent.action.MAIN的形式返回,而不是ACTION_USB_DEVICE_ATTACHED。
令人讨厌的是,如果您离开应用程序,还获取android.intent.action.MAIN,但不要拔掉USB。我想将设备置于睡眠状态并将其唤醒将会做同样的事情。
所以从我发现的,你无法直接得到意图,但看起来你可以依赖于在插入USB设备时调用onResume(),所以解决方案是只检查看看如果每次获得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是否已连接,因为我实际上还没有深入研究过。我正在使用一个只能连接的库,所以对于我的应用程序,我可以启动那个循环而且我很好。
将清单中活动的启动模式设置为“singleTask”以防止它在已经运行时再次运行也很重要,否则插入USB设备只会启动应用程序的第二个实例! / p>
所以我的清单中的整个活动标签看起来像这样:
<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>
无论如何,我希望这有助于某人!我很惊讶我无法找到解决方案!
答案 1 :(得分:8)
接下来是来自@ Gusdor的富有洞察力的评论(+1):我在onNewIntent()
中实施了一项检查,正如@Gusdor指出的那样,当您的活动launchMode
设置为{{ 1}}或singleTask
。然后,不要像接受的答案所示检查布尔标志,而只需使用singleTop
将意图传递给USB广播接收器。例如,
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);
}
}
你可以发送另一个系统广播而不是本地广播,但我认为你不能使用动作@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
}
(系统会将其视为潜在的安全风险),所以你会必须定义自己的行动。没什么大不了的,但为什么要这么麻烦,特别是因为本地广播没有IPC开销。
答案 2 :(得分:4)
在应用程序中创建广播接收器,而不是清单,允许应用程序仅在处理运行时处理分离事件。这样,分离事件仅发送到当前正在运行的应用程序,而不是广播到所有应用程序。