web-dev-qa-db-ja.com

Android 3.1 USBホスト-BroadcastReceiverがUSB_DEVICE_ATTACHEDを受信しない

私は developer.Android.comでのUSBホストの説明とサンプル を介して、接続されたUSBデバイスと分離されたUSBデバイスを検出しました。

デバイスが接続されているときにマニフェストファイルでインテントフィルターを使用してアプリケーションを起動すると、完全に正常に動作します:プラグイン、デバイスが検出されました、Androidはアプリケーションを起動する許可を求めます、デバイス情報がテーブルに表示されます。

開発中のアプリケーションは、デバイスが接続または切断された場合(データ管理目的など)にのみ開始/終了しないでください。また、アプリが既に実行されている場合は、オープンダイアログがポップアップ表示されないようにします。そのため、デバイスが接続されている場合は直接アクティビティを開始せず、デバイスが着脱された場合にアクティビティに通知する(後で)BroadcastReceiverを登録することにしました。このレシーバーは、detachアクションを正しく認識しますが、attachアクションは認識しません。

権限やデータ属性などが不足していますか?チュートリアルとサンプルは、追加の必要な属性について何も述べていません。

これがマニフェストファイルです。

<?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 Plugin

デバイス:Acer Iconia Tab A500

Android:3.1

前もって感謝します

35
miffi

ああ!私はそれを考え出した。私はまったく同じ問題を抱えていました。

その要点は-デバイスが接続されたときに(マニフェストファイルを使用して)アプリケーションを自動的に起動する場合、Android systemが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ではありません。

厄介なことに、アプリを離れただけでUSBのプラグを抜かない場合は、also Android.intent.action.MAINを取得します。デバイスをスリープ状態にして復帰させると、同じことができると思います。

したがって、私が見つけたものから、インテントを直接取得することはできませんが、USBデバイスが接続されているときに呼び出されているonResume()に依存している可能性があるため、解決策は、USBが接続されているかどうかを確認することですonResumeを取得するたびに接続されます。 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デバイスを接続すると、アプリケーションの2番目のインスタンスが起動されます。

したがって、マニフェストのアクティビティタグ全体は次のようになります。

    <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>

とにかく、これが誰かを助けることを願っています!私はこれに対する解決策をすでに見つけることができなかったことに驚きました!

40

@Gusdorの洞察に満ちたコメント(+1)からフォローするだけです:onNewIntent()にチェックを実装しました。これは、@ Gusdorが指摘するように、アクティビティlaunchModesingleTaskまたはsingleTopとして設定されたときに呼び出されます。次に、受け入れられた回答が示唆するようにブールフラグをチェックするのではなく、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オーバーヘッドがローカルブロードキャストにないため、煩わしい。

8
James B

マニフェストではなく、アプリケーション内にブロードキャストレシーバーを作成すると、アプリケーションは、実行中の分離イベントのみを処理できます。このようにして、切り離されたイベントは、現在実行中のアプリケーションにのみ送信され、すべてのアプリケーションにブロードキャストされません。

5
soreal