Cómo iniciar una actividad desde una notificación

Cuando inicias una actividad desde una notificación, debes conservar la experiencia de navegación esperada del usuario. Cuando el usuario presiona el botón Atrás, debe regresar a la pantalla principal mediante el flujo de trabajo normal de la app, y, al abrir la pantalla Recientes, se debe mostrar la actividad como una tarea separada. Para preservar esta experiencia de navegación, inicia la actividad en una tarea nueva.

El enfoque básico para configurar el comportamiento de presión de tu notificación se describe en Cómo crear una notificación básica. En esta página, se describe cómo configurar un PendingIntent para la acción de tu notificación, de modo que se cree una tarea y una pila de actividades nueva. La forma de hacerlo depende del tipo de actividad que inicias:

Actividad normal
Corresponde a una actividad que forma parte del flujo normal de la experiencia del usuario de tu app. Cuando el usuario llega a la actividad desde la notificación, la tarea nueva debe incluir una pila de actividades completa, lo que le permite al usuario presionar el botón Atrás para navegar hacia arriba en la jerarquía de la app.
Actividad especial
El usuario solamente ve esta actividad si se inicia desde una notificación. En cierto sentido, esta actividad extiende la IU de la notificación, ya que proporciona información que es difícil de mostrar en la notificación en sí. Esta actividad no necesita una pila de actividades.

Cómo configurar un PendingIntent de actividad normal

Para iniciar una actividad normal desde tu notificación, configura PendingIntent con TaskStackBuilder para que cree una nueva pila de actividades de la siguiente manera.

Cómo definir la jerarquía de actividades de tu app

Para definir la jerarquía natural de tus actividades, agrega el atributo android:parentActivityName a cada elemento <activity> del archivo de manifiesto de tu app. Observa el siguiente ejemplo:

<activity
    android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<!-- MainActivity is the parent for ResultActivity. -->
<activity
    android:name=".ResultActivity"
    android:parentActivityName=".MainActivity" />
    ...
</activity>

Cómo compilar un PendingIntent con una pila de actividades

Para iniciar una actividad que incluya una pila de actividades, crea una instancia de TaskStackBuilder, llama a addNextIntentWithParentStack() y pásale el objeto Intent de la actividad que desees iniciar.

Siempre que definas la actividad superior para cada actividad como se describió anteriormente, puedes llamar a getPendingIntent() para recibir un PendingIntent que incluya toda la pila de actividades.

Kotlin

// Create an Intent for the activity you want to start.
val resultIntent = Intent(this, ResultActivity::class.java)
// Create the TaskStackBuilder.
val resultPendingIntent: PendingIntent? = TaskStackBuilder.create(this).run {
    // Add the intent, which inflates the back stack.
    addNextIntentWithParentStack(resultIntent)
    // Get the PendingIntent containing the entire back stack.
    getPendingIntent(0,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
}

Java

// Create an Intent for the activity you want to start.
Intent resultIntent = new Intent(this, ResultActivity.class);
// Create the TaskStackBuilder and add the intent, which inflates the back
// stack.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntentWithParentStack(resultIntent);
// Get the PendingIntent containing the entire back stack.
PendingIntent resultPendingIntent =
        stackBuilder.getPendingIntent(0,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

Si es necesario, puedes agregar argumentos a los objetos Intent de la pila llamando a TaskStackBuilder.editIntentAt(). A veces, esto es necesario para garantizar que una actividad de la pila de actividades muestre datos significativos cuando el usuario navegue hacia ella.

Luego, puedes pasar el PendingIntent a la notificación como lo haces normalmente:

Kotlin

val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
    setContentIntent(resultPendingIntent)
    ...
}
with(NotificationManagerCompat.from(this)) {
    notify(NOTIFICATION_ID, builder.build())
}

Java

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
builder.setContentIntent(resultPendingIntent);
...
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(NOTIFICATION_ID, builder.build());

Cómo configurar un PendingIntent de actividad especial

Como una actividad especial que comienza desde una notificación no necesita una pila de actividades, puedes llamar a getActivity() para crear el PendingIntent. Sin embargo, debes definir las opciones de tareas apropiadas en el manifiesto.

  1. En tu manifiesto, agrega los siguientes atributos al elemento <activity>.
    android:taskAffinity=""
    Junto con la marca FLAG_ACTIVITY_NEW_TASK que usas en el código, establece este atributo en blanco para garantizar que esta actividad no se incluya en la tarea predeterminada de la app. Las tareas existentes que tengan la afinidad predeterminada de la app no se verán afectadas.
    android:excludeFromRecents="true"
    Excluye la tarea nueva de la pantalla Recientes para que el usuario no pueda regresar a ella por accidente.

    Esto se muestra en el siguiente ejemplo:

    <activity
        android:name=".ResultActivity"
        android:launchMode="singleTask"
        android:taskAffinity=""
        android:excludeFromRecents="true">
    </activity>
    
  2. Compila y emite la notificación:
    1. Crea un Intent que inicie el Activity.
    2. Configura la Activity para iniciar una tarea nueva vacía llamando a setFlags() con las marcas FLAG_ACTIVITY_NEW_TASK y FLAG_ACTIVITY_CLEAR_TASK.
    3. Llama a getActivity() para crear un PendingIntent.

    Esto se muestra en el siguiente ejemplo:

    Kotlin

    val notifyIntent = Intent(this, ResultActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    }
    val notifyPendingIntent = PendingIntent.getActivity(
            this, 0, notifyIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )
    

    Java

    Intent notifyIntent = new Intent(this, ResultActivity.class);
    // Set the Activity to start in a new, empty task.
    notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    // Create the PendingIntent.
    PendingIntent notifyPendingIntent = PendingIntent.getActivity(
            this, 0, notifyIntent,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
    );
    
  3. Pasa el PendingIntent a la notificación como lo haces normalmente:

    Kotlin

    val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
        setContentIntent(notifyPendingIntent)
        ...
    }
    with(NotificationManagerCompat.from(this)) {
        notify(NOTIFICATION_ID, builder.build())
    }
    

    Java

    NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
    builder.setContentIntent(notifyPendingIntent);
    ...
    NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
    notificationManager.notify(NOTIFICATION_ID, builder.build());
    

Para obtener más información sobre las distintas opciones de tareas y cómo funciona la pila de actividades, consulta Tareas y la pila de actividades.