Descripción general de las transmisiones

Las aplicaciones de Android pueden enviar o recibir mensajes del sistema Android y otras apps para Android, al igual que la publicar-suscribirse patrón de diseño. Estas emisiones se envían cuando ocurre un evento de interés. Por ejemplo, el sistema Android envía emisiones cuando varios eventos del sistema como cuando se inicia el sistema o cuando el dispositivo comienza a cargarse. Aplicaciones también pueden enviar transmisiones personalizadas, por ejemplo, para notificar a otras apps sobre algo que podría interesarle (por ejemplo, algunos datos nuevos tienen que se haya descargado).

El sistema optimiza la entrega de transmisiones para mantener un buen estado óptimo del sistema. Por lo tanto, los tiempos de entrega de las transmisiones no son garantizada. Las apps que necesitan comunicación entre procesos de baja latencia considera usar servicios vinculados.

Las apps pueden registrarse para recibir emisiones específicas. Cuando se envía una transmisión, el sistema enruta automáticamente las transmisiones a las apps que se suscribieron recibir ese tipo particular de transmisión.

En términos generales, las emisiones pueden usarse como un sistema de mensajería entre apps. y fuera del flujo de usuarios normal. Sin embargo, debes tener cuidado de no abusar la oportunidad de responder a transmisiones y ejecutar trabajos en segundo plano que puede contribuir a un rendimiento lento del sistema.

Acerca de las emisiones del sistema

El sistema envía transmisiones automáticamente cuando ocurren varios eventos del sistema como cuando el sistema activa y desactiva el modo de avión. Sistema se envían a todas las apps que están suscritas para recibir el para cada evento.

El mensaje de emisión está envuelto en un Intent. objeto cuya cadena de acción identifica el evento que ocurrió (por ejemplo, android.intent.action.AIRPLANE_MODE). La intención también puede incluir información adicional agrupada en su campo adicional. Por ejemplo, el avión incluye un valor booleano adicional que indica si el El modo está activado.

Para obtener más información sobre cómo leer intents y obtener la cadena de acción de un intent, consulta Cómo funcionan los intents Filtros.

Para obtener una lista completa de las acciones de emisión del sistema, consulta la BROADCAST_ACTIONS.TXT en el SDK de Android. Cada acción de transmisión tiene un constante asociado a ella. Por ejemplo, el valor de la constante ACTION_AIRPLANE_MODE_CHANGED es android.intent.action.AIRPLANE_MODE Documentación para cada acción de transmisión está disponible en su campo constante asociado.

Cambios en las emisiones del sistema

A medida que la plataforma de Android evoluciona, cambia periódicamente la forma en que las transmisiones y cómo se comportan. Ten en cuenta los siguientes cambios para brindar compatibilidad con todas las versiones de Android.

Android 14

Mientras las aplicaciones se encuentren en un almacenamiento estado, la entrega de transmisiones es están optimizados para el estado del sistema. Por ejemplo, las transmisiones menos importantes del sistema, como ya que ACTION_SCREEN_ON son se pospone mientras la app se encuentra en estado almacenado en caché. Cuando la app pase del estado de estado en un proceso activo ciclo de vida, el sistema entrega las transmisiones diferidas.

Las transmisiones importantes que se declaran en el del contenedor quita temporalmente las apps del almacenamiento para la entrega.

Android 9

A partir de Android 9 (nivel de API 28), el NETWORK_STATE_CHANGED_ACTION no recibe información sobre la ubicación del usuario ni datos identificables.

Además, si tu app está instalada en un dispositivo con Android 9 o versiones posteriores, las transmisiones del sistema desde Wi-Fi no contienen SSID, BSSID, conexiones o analizar los resultados. Para obtener esta información, llama a getConnectionInfo() en su lugar.

Android 8.0

A partir de Android 8.0 (nivel de API 26), el sistema impone restricciones restricciones en receptores declarados en el manifiesto.

Si tu app está orientada a Android 8.0 o versiones posteriores, no podrás usar el manifiesto para lo siguiente: declarar un receptor para la mayoría de las transmisiones implícitas (las que no se orientan específicamente en tu aplicación). Aún puedes usar una receptor registrado en el contexto cuando el usuario usa activamente la app.

Android 7.0

Android 7.0 (nivel de API 24) y las versiones posteriores no envían el siguiente sistema transmisiones:

Además, las apps orientadas a Android 7.0 y versiones posteriores deben registrar la transmisión de CONNECTIVITY_ACTION usando registerReceiver(BroadcastReceiver, IntentFilter). La declaración de un receptor en el manifiesto no funciona.

Cómo recibir emisiones

Las apps pueden recibir transmisiones de dos maneras: a través de receptores declarados en el manifiesto y receptores registrados en el contexto.

Receptores declarados en el manifiesto

Si declaras un receptor de emisión en tu manifiesto, el sistema inicia app (si aún no se está ejecutando) cuando se envía la transmisión.

Para declarar un receptor de emisión en el manifiesto, realiza los siguientes pasos:

  1. Especifica el <receiver>. en el manifiesto de tu app.

    <!-- If this receiver listens for broadcasts sent from the system or from
         other apps, even other apps that you own, set android:exported to "true". -->
    <receiver android:name=".MyBroadcastReceiver" android:exported="false">
        <intent-filter>
            <action android:name="APP_SPECIFIC_BROADCAST" />
        </intent-filter>
    </receiver>
    

    Los filtros de intents especifican las acciones de emisión a las que se suscribe tu receptor.

  2. Crea la subclase BroadcastReceiver y, luego, implementa onReceive(Context, Intent). El el receptor de emisión en el siguiente ejemplo registra y muestra el contenido de la transmisión:

    Kotlin

    private const val TAG = "MyBroadcastReceiver"
    
    class MyBroadcastReceiver : BroadcastReceiver() {
    
        override fun onReceive(context: Context, intent: Intent) {
            StringBuilder().apply {
                append("Action: ${intent.action}\n")
                append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
                toString().also { log ->
                    Log.d(TAG, log)
    
                    val binding = ActivityNameBinding.inflate(layoutInflater)
                    val view = binding.root
                    setContentView(view)
    
                    Snackbar.make(view, log, Snackbar.LENGTH_LONG).show()
                }
            }
        }
    }
    

    Java

    public class MyBroadcastReceiver extends BroadcastReceiver {
            private static final String TAG = "MyBroadcastReceiver";
            @Override
            public void onReceive(Context context, Intent intent) {
                StringBuilder sb = new StringBuilder();
                sb.append("Action: " + intent.getAction() + "\n");
                sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
                String log = sb.toString();
                Log.d(TAG, log);
    
                ActivityNameBinding binding =
                        ActivityNameBinding.inflate(layoutInflater);
                val view = binding.root;
                setContentView(view);
    
                Snackbar.make(view, log, Snackbar.LENGTH_LONG).show();
            }
        }
    

    Para habilitar la vinculación de vistas, configura viewBinding en el archivo. build.gradle.

El administrador de paquetes del sistema registra el receptor cuando se instala la app. El receptor se convierte en un punto de entrada separado a tu app. que el sistema pueda iniciar la app y entregar la transmisión si esta no en ejecución.

El sistema crea un nuevo componente BroadcastReceiver. para manejar cada emisión que recibe. Este objeto solo es válido durante la llamada a onReceive(Context, Intent). Una vez que el código que devuelve este método, el sistema considera que el componente ya no activo.

Receptores registrados en el contexto

Los receptores registrados en el contexto reciben transmisiones siempre que su contexto es válido. Por ejemplo, si te registras dentro de un Activity en otro contexto, recibirás transmisiones siempre que no se destruya la actividad. Si te registres con el contexto de la Aplicación, recibirás transmisiones siempre que la app se esté ejecutando.

Para registrar un receptor con un contexto, realiza los siguientes pasos:

  1. En el archivo de compilación de nivel de módulo de tu app, incluye la versión 1.9.0 o posterior de la biblioteca de AndroidX Core:

    Groovy

    dependencies {
        def core_version = "1.15.0"
    
        // Java language implementation
        implementation "androidx.core:core:$core_version"
        // Kotlin
        implementation "androidx.core:core-ktx:$core_version"
    
        // To use RoleManagerCompat
        implementation "androidx.core:core-role:1.0.0"
    
        // To use the Animator APIs
        implementation "androidx.core:core-animation:1.0.0"
        // To test the Animator APIs
        androidTestImplementation "androidx.core:core-animation-testing:1.0.0"
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation "androidx.core:core-performance:1.0.0"
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation "androidx.core:core-google-shortcuts:1.1.0"
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation "androidx.core:core-remoteviews:1.1.0"
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation "androidx.core:core-splashscreen:1.2.0-alpha02"
    }

    Kotlin

    dependencies {
        val core_version = "1.15.0"
    
        // Java language implementation
        implementation("androidx.core:core:$core_version")
        // Kotlin
        implementation("androidx.core:core-ktx:$core_version")
    
        // To use RoleManagerCompat
        implementation("androidx.core:core-role:1.0.0")
    
        // To use the Animator APIs
        implementation("androidx.core:core-animation:1.0.0")
        // To test the Animator APIs
        androidTestImplementation("androidx.core:core-animation-testing:1.0.0")
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation("androidx.core:core-performance:1.0.0")
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation("androidx.core:core-google-shortcuts:1.1.0")
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation("androidx.core:core-remoteviews:1.1.0")
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation("androidx.core:core-splashscreen:1.2.0-alpha02")
    }
  2. Crea una instancia de BroadcastReceiver:

    Kotlin

    val br: BroadcastReceiver = MyBroadcastReceiver()
    

    Java

    BroadcastReceiver br = new MyBroadcastReceiver();
    
  3. Crea una instancia de IntentFilter:

    Kotlin

    val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
    

    Java

    IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);
    
  4. Elige si el receptor de emisión se debe exportar y si es visible para otras apps en el dispositivo. Si este receptor está escuchando transmisiones enviadas desde el sistema o desde otras aplicaciones (incluso otras de tu propiedad), puedes utilizar la RECEIVER_EXPORTED. Si, en cambio, el receptor solo está escuchando transmitidas por tu app, usa la marca RECEIVER_NOT_EXPORTED.

    Kotlin

    val listenToBroadcastsFromOtherApps = false
    val receiverFlags = if (listenToBroadcastsFromOtherApps) {
        ContextCompat.RECEIVER_EXPORTED
    } else {
        ContextCompat.RECEIVER_NOT_EXPORTED
    }
    

    Java

    boolean listenToBroadcastsFromOtherApps = false;
    if (listenToBroadcastsFromOtherApps) {
        receiverFlags = ContextCompat.RECEIVER_EXPORTED;
    } else {
        receiverFlags = ContextCompat.RECEIVER_NOT_EXPORTED;
    }
    
  5. Registra el receptor llamando registerReceiver():

    Kotlin

    ContextCompat.registerReceiver(context, br, filter, receiverFlags)
    

    Java

    ContextCompat.registerReceiver(context, br, filter, receiverFlags);
    
  6. Para dejar de recibir emisiones, llama a unregisterReceiver(android.content.BroadcastReceiver). Asegúrate de cancelar el registro del receptor cuando ya no lo necesites o el contexto ya no es válido.

    Ten cuidado con dónde registrar y cancelar el registro del receptor, por Por ejemplo, si registras un receptor en onCreate(Bundle) usando el contexto de la actividad, debes cancelarlo en onDestroy() para evitar que se filtre el receptor fuera del contexto de la actividad. Si te registras un receptor en onResume(), deberías cancela el registro en onPause() para evitar registrándolo varias veces (si no quieres recibir emisiones cuando se pausa, lo que puede reducir la sobrecarga innecesaria del sistema). Qué no debes hacer cancelar el registro en onSaveInstanceState(Bundle), porque no se llama si el usuario retrocede en la pila del historial.

Efectos en el estado del proceso

Si tu BroadcastReceiver funciona o no afecta al proceso contenido, lo que puede alterar su de matar el sistema. Un proceso en primer plano ejecuta el método onReceive() de un receptor. El el sistema ejecuta el proceso, excepto bajo una presión de memoria extrema.

BroadcastReceiver se desactiva después de onReceive(). El host del receptor es tan importante como los componentes de su aplicación. Si ese proceso solo aloja un receptor declarado en el manifiesto (un caso frecuente para las apps que el usuario nunca o no haya interactuado recientemente), es posible que el sistema lo cierre después de onReceive() para que recursos disponibles para otros procesos más críticos.

Por lo tanto, los receptores de emisión no deben iniciar subprocesos en segundo plano de larga duración. El sistema puede detener el proceso en cualquier momento después de onReceive() para la recuperación. memoria y finaliza el subproceso creado. Para mantener activo el proceso, programa una JobService del receptor con JobScheduler para que el sistema sepa que el proceso aún funciona. En Descripción general del trabajo en segundo plano encontrarás más detalles.

Cómo enviar emisiones

Android ofrece tres maneras para que las apps envíen emisiones:

  • El sendOrderedBroadcast(Intent, String) envía transmisiones a un receptor a la vez. A medida que se ejecuta cada receptor puede propagar un resultado al siguiente receptor, o puede anular por completo la transmisión para que no pase a otra receptores de llamadas de Google. El orden en el que se ejecutan los receptores puede controlarse con el Atributo android:priority del filtro de intents coincidente. con el la misma prioridad se ejecutará en un orden arbitrario.
  • El método sendBroadcast(Intent) envía a todos los receptores en un orden indefinido. Esto se llama Normal Transmitir. Esto es más eficiente, pero significa que los receptores no pueden leer resultados de otros receptores, propagar datos recibidos de la transmisión o anular la transmisión.

El siguiente fragmento de código demuestra cómo enviar una transmisión creando un Intent y llamada a sendBroadcast(Intent).

Kotlin

Intent().also { intent ->
    intent.setAction("com.example.broadcast.MY_NOTIFICATION")
    intent.putExtra("data", "Nothing to see here, move along.")
    sendBroadcast(intent)
}

Java

Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data", "Nothing to see here, move along.");
sendBroadcast(intent);

El mensaje de emisión está envuelto en un objeto Intent. La cadena de acción del intent debe proporcionar la sintaxis del nombre del paquete Java de la app y identificar de manera exclusiva el evento de transmisión. Puedes adjuntar información adicional al intent con putExtra(String, Bundle). También puedes limitar una transmisión a un conjunto de aplicaciones de la misma organización al llamando a setPackage(String) en el intent.

Cómo restringir emisiones con permisos

Los permisos te permiten restringir emisiones a un conjunto de apps que tengan ciertos permisos. Puedes imponer restricciones en el remitente receptor de una transmisión.

Cómo enviar emisiones con permisos

Cuando llames a sendBroadcast(Intent, String) o sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle), puedes especificar un permiso. Solo los destinatarios que solicitaron ese permiso con la etiqueta en su manifiesto (y posteriormente se le otorgó el permiso si es peligroso) pueden recibir la transmisión. Por ejemplo, el El siguiente código envía una transmisión:

Kotlin

sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND),
              Manifest.permission.BLUETOOTH_CONNECT)

Java

sendBroadcast(new Intent(BluetoothDevice.ACTION_FOUND),
              Manifest.permission.BLUETOOTH_CONNECT)

Para recibir la transmisión, la aplicación receptora debe solicitar el permiso como como se muestra a continuación:

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

Puedes especificar un permiso del sistema existente, como BLUETOOTH_CONNECT o definir un permiso personalizado con el <permission>. Para información sobre permisos y seguridad en general, consulta el Sistema de Permisos.

Cómo recibir emisiones con permisos

Si especificas un parámetro de permiso cuando registras un receptor de emisión (ya sea con registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) o en <receiver> en tu manifiesto), solo podrán hacerlo las emisoras que hayan solicitado el permiso mediante Etiqueta <uses-permission> en su manifiesto (y posteriormente se le otorga el permiso si se peligroso) pueden enviar un intent al receptor.

Por ejemplo, supongamos que tu app receptora tiene un receptor declarado en el manifiesto como como se muestra a continuación:

<receiver android:name=".MyBroadcastReceiver"
          android:permission="android.permission.BLUETOOTH_CONNECT">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_FOUND"/>
    </intent-filter>
</receiver>

O bien la app receptora tiene un receptor registrado en el contexto, de la siguiente manera:

Kotlin

var filter = IntentFilter(Intent.ACTION_FOUND)
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null )

Java

IntentFilter filter = new IntentFilter(Intent.ACTION_FOUND);
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null );

Luego, para poder enviar emisiones a esos receptores, la app que las envía solicita el permiso como se muestra a continuación:

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

Consideraciones de seguridad y prácticas recomendadas

Estas son algunas consideraciones de seguridad y prácticas recomendadas para enviar recepción de emisiones:

  • Si se registraron muchas aplicaciones para recibir la misma transmisión en su puede provocar que el sistema inicie muchas apps, con lo cual un impacto significativo en el rendimiento del dispositivo y la experiencia del usuario. Para evitar es preferible usar el registro de contexto en lugar de la declaración en el manifiesto. A veces, el propio sistema Android impone el uso de registros receptores de llamadas de Google. Por ejemplo, se envía la transmisión CONNECTIVITY_ACTION solo a receptores registrados en el contexto.

  • No emitas información sensible mediante un intent implícito, El cualquier app que se registre para recibir la transmisión puede leer la información. Existen tres maneras de controlar quién puede recibir tus emisiones:

    • Puedes especificar un permiso cuando envías una emisión.
    • En Android 4.0 y versiones posteriores, puedes especificar un package con setPackage(String) cuando se envía un transmisión. El sistema restringe la transmisión al conjunto de apps que coincide con el paquete.
  • Cuando registras un receptor, cualquier app puede enviar al receptor de tu app. Existen varias formas de limitar el transmisiones que recibe tu app:

    • Puedes especificar un permiso cuando registras un receptor de emisión.
    • Para los receptores declarados en el manifiesto, puedes configurar el android:exportado atributo como "false" en el manifiesto. El receptor no recibe de fuentes externas a la app.
  • El espacio de nombres para las acciones de emisión es global. Asegúrate de que los nombres de las acciones y otras cadenas se escriben en un espacio de nombres propio; de lo contrario, involuntariamente, entran en conflicto con otras aplicaciones.

  • Debido a que el método onReceive(Context, Intent) de un receptor se ejecuta en el subproceso principal, debería ejecutarse y mostrarse rápidamente. Si necesitas realizar trabajos de larga duración, ten cuidado de no generar hilos o empezar servicios en segundo plano porque el sistema puede finalizar todo el proceso después Se muestra onReceive(). Para obtener más información, consulte Efectos en el proceso Para realizar un trabajo de larga duración, recomendar:

    • Se llamará a goAsync() en tu onReceive() del receptor y pasa el BroadcastReceiver.PendingResult a un subproceso en segundo plano. De esta manera, la emisión se mantiene activa luego de que se muestra desde onReceive(). Sin embargo, incluso con este enfoque, el sistema espera que termines la transmisión muy rápido (menos de 10 segundos). Te permite mover en otro subproceso para evitar que se produzca un error en el subproceso principal.
    • Programa una tarea con JobScheduler. Para ver más información, consulta Tareas inteligentes Programación
  • No inicies actividades desde receptores de emisión es molesto; especialmente si hay más de un receptor. En cambio, considera mostrar una notificación