2011-08-08 93 views
33

我通过the description and samples for USB host at developer.android.com工作来检测连接和分离的USB设备。Android 3.1 USB主机 - BroadcastReceiver未收到USB_DEVICE_ATTACHED

如果我在清单文件中使用意图过滤器来启动我的应用程序,当一个设备连接时,它工作得很好:插入,检测到设备,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:Eclipse的3.7与Android插件

设备:宏碁ICONIA TAB A500

安卓3.1

在此先感谢

回答

4

创建内的广播接收器应用程序,而不是清单,允许您的应用程序仅处理分离的事件在运行时。这样,分离的事件只发送到当前正在运行的应用程序,而不是向所有应用程序广播。

34

啊哈!我想到了。我遇到了完全相同的问题。

它的要点是 - 如果你有你的应用程序启动时自动设备中(使用清单文件)被插入,那么它会在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,NOT 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" /> 

您将有一个device_filter.xml文件在/ RES/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> 

然后你的onCreate看起来是这样的(当然你需要的任何厂商ID和产品ID):

@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的launchmode设置为“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> 

无论如何,我希望这可以帮助别人!我很惊讶,我无法找到解决方案已经!

+4

尝试覆盖onNewIntent() - 当您使用android:launchMode =“singleTop”时它会被调用,并可能很好地解决您的情况。之后总是调用onResume()。 – Gusdor

+0

你能解释一下你的第一段“它的要点”吗?它看起来很困惑。 –

+0

谢谢,我一直在想同样的事情。很高兴知道这是不起作用的。闻起来像一个Android的bug对我来说。 – Brian

6

只是为了从@ Gusdor的精辟评论(+1)遵循:我在onNewIntent()实施的检查,作为@Gusdor指出的,是当你的活动launchMode设置为singleTasksingleTop调用。然后,只需使用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开销。