最近的畫面

「最近使用」畫面也稱為「總覽」畫面、最近任務清單或最近使用的應用程式畫面,是系統層級的 UI,會列出最近存取的活動工作。使用者可以瀏覽清單、選取要繼續執行的工作,或滑動工作來從清單中移除。

「最近使用」畫面採用以文件為中心的模型 (Android 5.0 (API 級別 21) 中推出),其中包含不同文件的相同活動的多個執行個體,可做為工作顯示在「最近使用」畫面上。舉例來說,Google 雲端硬碟可能為多個 Google 文件各建立一項工作。每份文件都會以工作形式顯示在「最近」畫面中:

「最近」畫面顯示兩個 Google 雲端硬碟文件,每個文件都代表一項獨立工作。

另一個常見的例子是使用者透過瀏覽器輕觸「分享」 >「Gmail」,Gmail 應用程式的「撰寫」畫面隨即顯示。這時輕觸「最近」按鈕,會顯示 Chrome 和 Gmail 分別以獨立工作執行的畫面:

「最近」畫面,顯示 Chrome 和 Gmail 分別以獨立工作執行。

一般來說,您會讓系統定義工作和活動在「最近」畫面中的顯示方式。您不需要修改這項行為。不過,應用程式可以決定活動在「最近」畫面中的顯示方式和時間。

ActivityManager.AppTask 類別可讓您管理工作,而 Intent 類別的活動旗標則可讓您指定活動新增至「最近」畫面或從該畫面移除的時間。此外,您也可以使用 <activity> 屬性在資訊清單中設定行為。

在「最近使用」畫面中新增工作

使用 Intent 類別的旗標新增工作,可進一步控管文件在「最近」畫面中開啟或重新開啟的時間和方式。使用 <activity> 屬性時,您可以選擇一律在新工作開啟文件,或是重複使用文件的現有工作。

使用 Intent 旗標新增工作

為活動建立新文件時,請呼叫 startActivity() 方法,並將啟動活動的意圖傳遞至該方法。如要插入邏輯中斷點,讓系統將活動視為「最近」畫面中的新工作,請在啟動活動的 IntentaddFlags() 方法中,傳遞 FLAG_ACTIVITY_NEW_DOCUMENT 旗標。

如果您在建立新文件時設定 FLAG_ACTIVITY_MULTIPLE_TASK 標記,系統一律會建立新工作,並將目標活動做為根層級。這項設定可讓您在多個工作開啟同一份文件。下列程式碼示範主要活動如何執行這項操作,並從可組合函式啟動新活動:

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")
    }
}

當主要活動啟動新活動時,系統會搜尋現有工作,找出意圖與活動的意圖元件名稱和意圖資料相符的工作。如果找不到工作,或意圖包含 FLAG_ACTIVITY_MULTIPLE_TASK 旗標,系統會建立新工作,並將活動設為根層級。

如果系統找到意圖與意圖元件名稱和意圖資料相符的任務,就會將該任務帶到前景,並將新意圖傳遞至 onNewIntent()。新活動會取得意圖,並在「最近」畫面中建立新文件,如下列範例所示:

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")
    }
}

在上述程式碼中,Activity 會處理 OS 層級的路由 (onCreateonNewIntent),而 @Composable 函式只負責根據提供的狀態算繪 UI。

使用活動屬性新增工作

活動也可以在資訊清單中指定一律在新工作啟動,方法是使用 <activity> 屬性 android:documentLaunchMode。這個屬性有四個值,當使用者透過應用程式開啟文件時,會產生以下效果:

intoExisting
活動會重複使用文件的現有工作。這與設定 FLAG_ACTIVITY_MULTIPLE_TASK 旗標,但設定 FLAG_ACTIVITY_NEW_DOCUMENT 旗標的效果相同,如「使用 Intent 旗標新增工作」一節所述。
always
即使文件已開啟,活動仍會為文件建立新的工作。使用這個值與同時設定 FLAG_ACTIVITY_NEW_DOCUMENTFLAG_ACTIVITY_MULTIPLE_TASK 旗標的效果相同。
none
活動不會為文件建立新任務。「最近使用」畫面會以預設方式處理活動。也就是為應用程式顯示單一任務,並從使用者上次叫用的任意活動繼續執行。
never
活動不會為文件建立新任務。設定這個值會覆寫 FLAG_ACTIVITY_NEW_DOCUMENTFLAG_ACTIVITY_MULTIPLE_TASK 標記的行為。如果意圖中已設定這類標記,且「最近使用」畫面會顯示應用程式的單一任務,無論使用者上次叫用哪一項活動,都能繼續執行該任務。

移除工作

根據預設,文件工作會在活動結束時自動從「最近使用」畫面中退出。您可以使用 ActivityManager.AppTask 類別、Intent 旗標或 <activity> 屬性覆寫這個行為。

您隨時可以設定 <activity> 屬性 android:excludeFromRecentstrue,完全從「最近使用」畫面中排除工作。

您可以將 <activity> 屬性 android:maxRecents 設為整數值,藉此設定應用程式可在「最近使用」畫面中顯示的任務數量上限。達到任務數量上限時,最近最少使用的任務會從「最近使用」畫面消失。預設值為 16,最大值為 50 (低記憶體裝置為 25)。值不得小於 1。

使用 AppTask 類別移除工作

在「最近」畫面中建立新工作時,您可以呼叫 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")
    }
}

保留完成的工作

如果您想在「最近使用」畫面中保留任務,即使活動已完成,請在啟動活動的意圖的 addFlags() 方法中,傳遞 FLAG_ACTIVITY_RETAIN_IN_RECENTS 旗標。

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())
        }

如要達到相同效果,請將 <activity> 屬性 android:autoRemoveFromRecents 設為 false。文件活動的預設值為 true,一般活動則為 false。使用這項屬性會覆寫 FLAG_ACTIVITY_RETAIN_IN_RECENTS 旗標。

啟用最近瀏覽的網址分享功能 (僅限 Pixel)

在搭載 Android 12 以上版本的 Pixel 裝置上,使用者可以直接從「最近使用」畫面分享最近瀏覽的網頁內容連結。使用者在應用程式中瀏覽內容後,可以滑動至「最近使用」畫面,找出瀏覽內容的應用程式,然後輕觸連結按鈕複製或分享網址。

「最近」畫面,可分享最近瀏覽的網頁內容。

任何應用程式都能提供網頁 UI 並覆寫 onProvideAssistContent(),為使用者啟用「最近」連結,如下例所示:

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