2011-08-08 13 views
33

Trabajé a través de the description and samples for USB host at developer.android.com para detectar dispositivos USB conectados y desconectados.Android 3.1 USB-Host - BroadcastReceiver no recibe USB_DEVICE_ATTACHED

Si utilizo un filtro de intención en el archivo de manifiesto para iniciar mi aplicación cuando se conecta un dispositivo, funciona perfectamente bien: Enchúfalo, se detecta el dispositivo, Android solicita permiso para iniciar la aplicación, se muestra la información del dispositivo en una mesa

La aplicación que estoy desarrollando no debe iniciarse/finalizarse solo si un dispositivo está conectado/desconectado (por ejemplo, con fines de gestión de datos). Además, no quiero que aparezca el cuadro de diálogo abierto si la aplicación ya se está ejecutando. Así que decidí no iniciar la actividad directamente si un dispositivo está conectado, sino registrar un BroadcastReceiver, que (más tarde) se supone que notifica la actividad si un dispositivo está conectado/desconectado. Este receptor reconoce la acción de separación muy bien, pero no la acción de adjuntar.

¿Echo de menos un permiso o atributo de datos o algo así? El tutorial y las muestras no dicen nada sobre los atributos adicionales necesarios.

Aquí está el archivo de manifiesto:

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

y el receptor:

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 
} 

No entiendo por qué los mismos trabajos de filtro intención, si los uso en una actividad, pero no si se aplican a un receptor? Incluso si configuro el receptor y filtro en el código, los archivos adjuntos no son reconocidos.

Mi ambiente de trabajo: IDE: Eclipse 3.7 con Android Plugin

Dispositivo: Acer Iconia Tab A500

Android: 3.1

Gracias de antemano

Respuesta

4

Crear el receptor de radiodifusión dentro de la aplicación, y no el manifiesto, permite que su aplicación solo maneje eventos separados mientras se está ejecutando. De esta forma, los eventos separados solo se envían a la aplicación que se está ejecutando actualmente y no se difunde a todas las aplicaciones.

34

Aha! Me lo imaginé. Estaba teniendo exactamente el mismo problema.

El quid de la cuestión es - si usted tiene su inicio de la aplicación automáticamente cuando un dispositivo está conectado (mediante el archivo de manifiesto), parece que el sistema Android obtiene la intención ACTION_USB_DEVICE_ATTACHED, y luego, ya que sabe que su solicitud quiere correr en esa situación, en realidad envía a su aplicación el intento android.intent.action.MAIN. Nunca envía la acción ACTION_USB_DEVICE_ATTACHED a su aplicación porque cree que ya sabe lo que su aplicación quiere hacer en esa situación.

acabo ahora se identifica el problema, y ​​creo que tengo una solución, pero te puedo decir lo que he encontrado:

Incluso si su aplicación se está ejecutando y en primer plano, cuando se conecte en el dispositivo USB y el sistema Android obtiene el intento ACTION_USB_DEVICE_ATTACHED, invocará aResume() en su actividad.

Por desgracia, no se puede simplemente hacer esto:

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

Debido a que la intención va a volver como android.intent.action.MAIN, NO ACTION_USB_DEVICE_ATTACHED.

Un molesto, que también get android.intent.action.MAIN si acaba de salir de la aplicación, pero no desenchufar el USB. Me imagino que poner el dispositivo a dormir y despertarlo nuevamente hará lo mismo.

Por lo que he encontrado, no se puede obtener la intención directamente, pero parece que puede confiar en que se llame a OnResume() cuando se conecta un dispositivo USB, por lo que la solución es simplemente verificar mira si el USB está conectado cada vez que obtienes un onResume. También puede establecer un indicador cuando el USB está desconectado, porque por supuesto el intento de desconexión del USB dispara muy bien.

Así que en total, el receptor de radiodifusión podría tener este aspecto:

// 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;    
     } 
    } 
}; 

Tendrías esto dentro de onCreate:

// listen for new devices 
IntentFilter filter = new IntentFilter(); 
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); 
registerReceiver(mUsbReceiver, filter); 

Esto va dentro de la etiqueta de la actividad en su manifiesto:

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

Vas a tener un archivo en su device_filter.xml/res/xml/carpeta que tiene este aspecto :

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

(por supuesto con los ID de los proveedores y los ID de producto que necesita)

Y entonces su onCreate es como la siguiente:

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

que no tienen código específico para el control de para ver si el USB está conectado ya que aún no me he adentrado en eso. Estoy usando una biblioteca que se conectará solo si puede, así que para mi aplicación puedo comenzar ese ciclo y estoy bien.

También es probablemente importante establecer el modo de inicio de su actividad en el manifiesto a "singleTask" para evitar que se ejecute nuevamente cuando ya está en ejecución, o si conecta un dispositivo USB, ¡se iniciará una segunda instancia de su aplicación!

Así que mi etiqueta de actividad entero en mis manifiesta es así:

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

De todos modos, espero que esto ayude a alguien! ¡Me sorprendió que ya no pudiera encontrar una solución para esto!

+4

Intenta anular onNewIntent() - se invoca cuando utiliza android: launchMode = "singleTop" y probablemente resuelva su situación bastante bien. onResume() siempre se llama después. – Gusdor

+0

¿Puedes explicar tu primer párrafo "La esencia de esto es"? Me parece muy confundido. –

+0

Gracias, me he estado preguntando lo mismo. Es bueno saber que es algo que simplemente no funciona. Huele como un error de Android para mí. – Brian

6

sólo para seguir el de @ perspicaz comentario de Gusdor (1): He implementado un cheque en onNewIntent() que, como señala @Gusdor, se llama cuando su actividad launchMode se establece como singleTask o singleTop. Luego, en lugar de buscar indicadores booleanos como lo sugiere la respuesta aceptada, simplemente transmita la intención a su receptor de difusión USB usando un LocalBroadcastManager.Por ejemplo,

@Override 
protected void onNewIntent(Intent intent) { 
    super.onNewIntent(intent); 
    if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(intent.getAction())) { 
     LocalBroadcastManager.getInstance(this).sendBroadcast(intent); 
    } 
} 

Entonces, donde quiera que el registro de su (sistema) receptor de radiodifusión USB existente, simplemente registrar el mismo receptor con una instancia del gestor de difusión local, es decir,

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

Usted puede enviar otro sistema de transmisión en lugar de una transmisión local, pero no creo que puedas usar la acción UsbManager.ACTION_USB_ACCESSORY_ATTACHED (el sistema lo consideraría un riesgo de seguridad potencial), por lo que tendrías que definir tu propia acción. No es gran cosa, pero por qué molestarse, especialmente porque no hay gastos indirectos del IPC con transmisiones locales.

Cuestiones relacionadas