2011-08-08 74 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開銷。

相關問題