Ekran Ostatnie

Ekran Ostatnie, zwany też ekranem przeglądu, listą ostatnich zadań lub ekranem ostatnich aplikacji, to interfejs systemu, który zawiera listę ostatnio używanych aktywnościzadań. Użytkownik może poruszać się po liście, wybierać zadania do wznowienia lub usuwać je z listy, przesuwając palcem.

Ekran Ostatnie używa modelu opartego na dokumentach, który został wprowadzony w Androidzie 5.0 (API na poziomie 21). W tym modelu wiele instancji tej samej aktywności zawierających różne dokumenty może pojawiać się na ekranie Ostatnie jako zadania. Na przykład Dysk Google może mieć zadanie dla każdego z kilku dokumentów Google. Każdy dokument jest wyświetlany jako zadanie na ekranie Ostatnie:

Ekran Ostatnie z 2 dokumentami z Dysku Google, z których każdy jest przedstawiony jako osobne zadanie.

Innym typowym przykładem jest sytuacja, w której użytkownik korzysta z przeglądarki i klika Udostępnij > Gmail. Wyświetli się ekran Utwórz w aplikacji Gmail. Kliknięcie przycisku Ostatnie w tym momencie spowoduje wyświetlenie Chrome i Gmaila jako osobnych zadań:

Ekran Ostatnie z Chrome i Gmailem działającymi jako osobne zadania.

Zwykle to system określa, jak zadania i aktywności są reprezentowane na ekranie Ostatnie. Nie musisz modyfikować tego działania. Aplikacja może jednak określać, jak i kiedy aktywności będą wyświetlane na ekranie Ostatnie.

Klasa ActivityManager.AppTask umożliwia zarządzanie zadaniami, a flagi aktywności klasy Intent pozwalają określać, kiedy aktywność jest dodawana lub usuwana z ekranu Ostatnie. Atrybuty <activity> umożliwiają też ustawienie zachowania w pliku manifestu.

Dodawanie zadań do ekranu Ostatnie

Używanie flag klasy Intent do dodawania zadania daje większą kontrolę nad tym, kiedy i jak dokument jest otwierany lub ponownie otwierany na ekranie Ostatnie. Gdy używasz atrybutów <activity>, możesz wybrać, czy dokument ma być zawsze otwierany w nowym zadaniu, czy w istniejącym zadaniu.

Dodawanie zadania za pomocą flagi Intent

Gdy tworzysz nowy dokument dla swojej aktywności, wywołujesz metodę startActivity() i przekazujesz do niej intencję, która uruchamia aktywność. Aby wstawić przerwę logiczną, dzięki której system będzie traktować Twoją aktywność jako nowe zadanie na ekranie Ostatnie, przekaż flagę FLAG_ACTIVITY_NEW_DOCUMENT w metodzie addFlags() interfejsu Intent, która uruchamia aktywność.

Jeśli podczas tworzenia nowego dokumentu ustawisz flagę FLAG_ACTIVITY_MULTIPLE_TASK, system zawsze utworzy nowe zadanie z aktywnością docelową jako elementem głównym. To ustawienie umożliwia otwieranie tego samego dokumentu w więcej niż jednym zadaniu. Poniższy kod pokazuje, jak główna aktywność to robi i uruchamia nową aktywność z funkcji kompozycyjnej:

private fun newDocumentIntent(context: Context): Intent =
    Intent(context, NewDocumentActivity::class.java).apply {
        addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)
        putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, documentCounter++)
    }

@Composable
fun CreateDocumentButton() {
    val context = LocalContext.current
    Button(
        onClick = {
            val intent = newDocumentIntent(context)
            // Add FLAG_ACTIVITY_MULTIPLE_TASK if needed based on state
            context.startActivity(intent)
        }
    ) {
        Text("Create New Document")
    }
}

Gdy główna aktywność uruchamia nową aktywność, system przeszukuje istniejące zadania w poszukiwaniu takiego, którego intencja pasuje do nazwy komponentu intencji i danych intencji aktywności. Jeśli zadanie nie zostanie znalezione lub intencja zawiera flagę FLAG_ACTIVITY_MULTIPLE_TASK, zostanie utworzone nowe zadanie, którego elementem głównym będzie aktywność.

Jeśli system znajdzie zadanie, którego intencja pasuje do nazwy komponentu intencji i danych intencji, przeniesie to zadanie na pierwszy plan i przekaże nową intencję do onNewIntent(). Nowa aktywność otrzymuje intencję i tworzy nowy dokument na ekranie Ostatnie, jak pokazano w tym przykładzie:

class DocumentCentricActivity : ComponentActivity() {
    private var documentState by mutableStateOf(
        DocumentState(
            count = 0,
            textResId = R.string.hello_new_document_counter
        )
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val initialCount = intent.getIntExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0)

        documentState = documentState.copy(count = initialCount)

        setContent {
            MaterialTheme {
                DocumentScreen(
                    count = documentState.count,
                    textResId = documentState.textResId
                )
            }
        }
    }

    override fun onNewIntent(newIntent: Intent) {
        super.onNewIntent(newIntent)
        // If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this Activity is reused.
        documentState = documentState.copy(
            textResId = R.string.reusing_document_counter
        )
    }

    data class DocumentState(val count: Int, @StringRes val textResId: Int)

    companion object {
        const val KEY_EXTRA_NEW_DOCUMENT_COUNTER = "KEY_EXTRA_NEW_DOCUMENT_COUNTER"
    }
}

@Composable
fun DocumentScreen(count: Int, @StringRes textResId: Int) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center
    ) {
        // UI reacts to whichever string resource ID was passed down
        Text(text = stringResource(id = textResId))
        Spacer(modifier = Modifier.height(8.dp))
        Text(text = "Counter: $count")
    }
}

W powyższym kodzie aktywność obsługuje routing na poziomie systemu operacyjnego (onCreateonNewIntent), a funkcja @Composable odpowiada tylko za renderowanie interfejsu na podstawie podanego stanu.

Używanie atrybutu aktywności do dodawania zadania

Aktywność może też określić w pliku manifestu, że zawsze uruchamia się w nowym zadaniu, za pomocą atrybutu <activity> android:documentLaunchMode. Ten atrybut ma 4 wartości, które wywołują następujące efekty, gdy użytkownik otwiera dokument w aplikacji:

intoExisting
Aktywność ponownie wykorzystuje istniejące zadanie dotyczące dokumentu. Jest to równoznaczne z ustawieniem flagi FLAG_ACTIVITY_NEW_DOCUMENT bez ustawiania flagi FLAG_ACTIVITY_MULTIPLE_TASK, co opisano w sekcji Używanie flagi Intent do dodawania zadania.
always
Działanie tworzy nowe zadanie dla dokumentu, nawet jeśli jest on już otwarty. Użycie tej wartości jest równoznaczne z ustawieniem flag FLAG_ACTIVITY_NEW_DOCUMENTFLAG_ACTIVITY_MULTIPLE_TASK.
none
Aktywność nie tworzy nowego zadania dla dokumentu. Ekran Ostatnie traktuje aktywność tak, jak domyślnie. Wyświetla jedno zadanie dla aplikacji, które jest kontynuowane od ostatniej aktywności użytkownika.
never
Aktywność nie tworzy nowego zadania dla dokumentu. Ustawienie tej wartości zastępuje działanie flag FLAG_ACTIVITY_NEW_DOCUMENTFLAG_ACTIVITY_MULTIPLE_TASK. Jeśli któryś z tych elementów jest ustawiony w intencji, a na ekranie Ostatnie wyświetla się jedno zadanie dla aplikacji, wznawia się ona od ostatniego działania wywołanego przez użytkownika.

Usuwanie zadań

Domyślnie zadanie związane z dokumentem jest automatycznie zamykane na ekranie Ostatnie, gdy jego aktywność się zakończy. Możesz zmienić ten sposób działania za pomocą klasy ActivityManager.AppTask, flagi Intent lub atrybutu <activity>.

Zadanie możesz całkowicie wykluczyć z ekranu Ostatnie, ustawiając atrybut <activity>android:excludeFromRecents na true.

Możesz ustawić maksymalną liczbę zadań, które aplikacja może zawierać na ekranie Ostatnie, ustawiając atrybut <activity>android:maxRecents na wartość całkowitą. Gdy osiągniesz maksymalną liczbę zadań, najrzadziej używane zadanie zniknie z ekranu Ostatnie. Wartość domyślna to 16, a maksymalna to 50 (25 na urządzeniach z małą ilością pamięci). Wartości mniejsze niż 1 są nieprawidłowe.

Usuwanie zadań za pomocą klasy AppTask

W aktywności, która tworzy nowe zadanie na ekranie Ostatnie, możesz określić, kiedy usunąć zadanie i zakończyć wszystkie powiązane z nim aktywności, wywołując metodę finishAndRemoveTask():

@Composable
fun RemoveTaskButton() {
    val context = LocalContext.current
    Button(
        onClick = {
            // It is good practice to remove a document from the overview stack if not needed anymore.
            (context as? Activity)?.finishAndRemoveTask()
        }
    ) {
        Text("Remove from Recents")
    }
}

Zachowywanie ukończonych zadań

Jeśli chcesz zachować zadanie na ekranie Ostatnie, nawet jeśli jego aktywność została zakończona, przekaż flagę FLAG_ACTIVITY_RETAIN_IN_RECENTS w metodzie addFlags() intencji, która uruchamia aktywność.

private fun newDocumentIntent() =
        Intent(this, NewDocumentActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
                    android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)
            putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, getAndIncrement())
        }

Aby osiągnąć ten sam efekt, ustaw atrybut <activity>android:autoRemoveFromRecents na false. Wartość domyślna to true w przypadku aktywności związanych z dokumentami i false w przypadku zwykłych aktywności. Użycie tego atrybutu zastępuje flagę FLAG_ACTIVITY_RETAIN_IN_RECENTS.

Włącz udostępnianie ostatnio używanych adresów URL (tylko Pixel)

Na urządzeniach Pixel z Androidem 12 lub nowszym użytkownicy mogą udostępniać linki do ostatnio wyświetlanych treści internetowych bezpośrednio z ekranu Ostatnie. Po wyświetleniu treści w aplikacji użytkownik może przesunąć palcem do ekranu Ostatnie i znaleźć aplikację, w której wyświetlił treść, a następnie kliknąć przycisk linku, aby skopiować lub udostępnić adres URL.

Ekran Ostatnie z linkiem do udostępniania ostatnio wyświetlanych treści internetowych.

Każda aplikacja może włączyć linkowanie do ostatnio używanych aplikacji, udostępniając interfejs internetowy i zastępując onProvideAssistContent(), jak pokazano w tym przykładzie:

class MainActivity : ComponentActivity() {

    // Track the current URL as state so the UI can update it during navigation
    private var currentWebUri by mutableStateOf("https://example.com/home")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AppTheme {
                // Pass a lambda to your Compose UI so it can update the URL state
                // as the user navigates through your app.
                MainScreen(
                    onPageChanged = { newUrl -> currentWebUri = newUrl }
                )
            }
        }
    }

    override fun onProvideAssistContent(outContent: AssistContent) {
        super.onProvideAssistContent(outContent)

        // The system calls this when the user enters the Recents screen.
        // Provide the active URI tracked by the Compose state.
        outContent.webUri = Uri.parse(currentWebUri)
    }
}