Màn hình Gần đây

Màn hình Gần đây (còn gọi là màn hình Tổng quan, danh sách tác vụ gần đây hoặc màn hình ứng dụng gần đây) là một giao diện người dùng ở cấp hệ thống, liệt kê các hoạt động được truy cập gần đây tác vụ. Người dùng có thể di chuyển qua danh sách, chọn một việc cần làm để tiếp tục hoặc xoá một việc cần làm khỏi danh sách bằng cách vuốt để loại bỏ việc cần làm đó.

Màn hình Gần đây sử dụng mô hình tập trung vào tài liệu (ra mắt trong Android 5.0 (cấp độ API cấp 21)), trong đó nhiều thực thể của cùng một hoạt động chứa các tài liệu khác nhau có thể xuất hiện dưới dạng tác vụ trên màn hình Gần đây. Ví dụ: Google Drive có thể có một việc cần làm cho từng tài liệu trong số một vài tài liệu trên Google. Mỗi tài liệu xuất hiện dưới dạng một tác vụ trên màn hình Gần đây:

Màn hình Gần đây hiển thị 2 tài liệu trên Google Drive, mỗi tài liệu được biểu thị dưới dạng một tác vụ riêng biệt.

Một ví dụ phổ biến khác là khi người dùng đang sử dụng trình duyệt và họ nhấn vào Chia sẻ > Gmail. Màn hình Soạn thư của ứng dụng Gmail sẽ xuất hiện. Khi đó, việc nhấn vào nút Gần đây sẽ hiển thị Chrome và Gmail đang chạy dưới dạng các tác vụ riêng biệt:

Màn hình Gần đây hiển thị Chrome và Gmail đang chạy dưới dạng các tác vụ riêng biệt.

Thông thường, bạn sẽ để hệ thống xác định cách các tác vụ và hoạt động của bạn được biểu thị trên màn hình Gần đây. Bạn không cần sửa đổi hành vi này. Tuy nhiên, ứng dụng của bạn có thể xác định cách và thời điểm các hoạt động xuất hiện trên màn hình Gần đây.

Lớp ActivityManager.AppTask cho phép bạn quản lý các tác vụ và cờ hoạt động của lớp Intent cho phép bạn chỉ định thời điểm thêm hoặc xoá một hoạt động khỏi màn hình Gần đây. Ngoài ra, các thuộc tính <activity> cho phép bạn đặt hành vi trong tệp kê khai.

Thêm tác vụ vào màn hình Gần đây

Việc sử dụng các cờ của lớp Intent để thêm một tác vụ giúp bạn kiểm soát tốt hơn thời điểm và cách một tài liệu được mở hoặc mở lại trên màn hình Gần đây. Khi sử dụng các thuộc tính <activity>, bạn có thể chọn luôn mở tài liệu trong một tác vụ mới hoặc sử dụng lại một tác vụ hiện có cho tài liệu đó.

Sử dụng cờ Ý định để thêm một tác vụ

Khi tạo một tài liệu mới cho hoạt động, bạn sẽ gọi phương thức startActivity(), truyền ý định khởi chạy hoạt động đó. Để chèn một dấu ngắt logic sao cho hệ thống coi hoạt động của bạn là một tác vụ mới trên màn hình Gần đây, hãy truyền cờ FLAG_ACTIVITY_NEW_DOCUMENT trong phương thức addFlags() của Intent khởi chạy hoạt động đó.

Nếu bạn đặt FLAG_ACTIVITY_MULTIPLE_TASK cờ khi tạo tài liệu mới, hệ thống sẽ luôn tạo một tác vụ mới với hoạt động mục tiêu làm gốc. Chế độ cài đặt này cho phép mở cùng một tài liệu trong nhiều tác vụ. Đoạn mã sau đây minh hoạ cách hoạt động chính thực hiện việc này và bắt đầu Hoạt động mới từ thành phần kết hợp:

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

Khi hoạt động chính khởi chạy một hoạt động mới, hệ thống sẽ tìm kiếm trong các tác vụ hiện có một tác vụ có ý định khớp với tên thành phần ý định và dữ liệu ý định cho hoạt động đó. Nếu không tìm thấy tác vụ hoặc ý định chứa cờ FLAG_ACTIVITY_MULTIPLE_TASK, thì một tác vụ mới sẽ được tạo với hoạt động làm gốc.

Nếu hệ thống tìm thấy một tác vụ có ý định khớp với tên thành phần ý định và dữ liệu ý định, thì hệ thống sẽ đưa tác vụ đó lên phía trước và truyền ý định mới đến onNewIntent(). Hoạt động mới sẽ nhận được ý định và tạo một tài liệu mới trên màn hình Gần đây, như minh hoạ trong ví dụ sau:

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

Trong đoạn mã trước đó, Hoạt động xử lý việc định tuyến ở cấp hệ điều hành (onCreateonNewIntent), trong khi hàm @Composable chỉ chịu trách nhiệm hiển thị giao diện người dùng dựa trên trạng thái được cung cấp.

Sử dụng thuộc tính hoạt động để thêm một tác vụ

Một hoạt động cũng có thể chỉ định trong tệp kê khai rằng hoạt động đó luôn khởi chạy vào một tác vụ mới bằng cách sử dụng thuộc tính <activity> android:documentLaunchMode. Thuộc tính này có 4 giá trị tạo ra các hiệu ứng sau đây khi người dùng mở một tài liệu bằng ứng dụng:

intoExisting
Hoạt động sử dụng lại một tác vụ hiện có cho tài liệu. Việc này cũng giống như việc đặt cờ FLAG_ACTIVITY_NEW_DOCUMENT mà không đặt cờ FLAG_ACTIVITY_MULTIPLE_TASK, như mô tả trong phần Sử dụng cờ Ý định để thêm một tác vụ.
always
Hoạt động tạo ra một tác vụ mới cho tài liệu, ngay cả khi tài liệu đó đã được mở. Việc sử dụng giá trị này cũng giống như việc đặt cả hai cờ FLAG_ACTIVITY_NEW_DOCUMENTFLAG_ACTIVITY_MULTIPLE_TASK.
none
Hoạt động không tạo ra tác vụ mới cho tài liệu. Màn hình Gần đây sẽ xử lý hoạt động theo mặc định. Màn hình này hiển thị một tác vụ duy nhất cho ứng dụng và sẽ tiếp tục từ bất kỳ hoạt động nào mà người dùng thực hiện gần đây nhất.
never
Hoạt động không tạo ra tác vụ mới cho tài liệu. Việc đặt giá trị này sẽ ghi đè hành vi của cờ FLAG_ACTIVITY_NEW_DOCUMENTFLAG_ACTIVITY_MULTIPLE_TASK. Nếu một trong hai cờ này được đặt trong ý định và màn hình Gần đây hiển thị một tác vụ duy nhất cho ứng dụng, thì tác vụ đó sẽ tiếp tục từ bất kỳ hoạt động nào mà người dùng thực hiện gần đây nhất.

Xóa công việc

Theo mặc định, một tác vụ tài liệu sẽ tự động thoát khỏi màn hình Gần đây khi hoạt động của tác vụ đó kết thúc. Bạn có thể ghi đè hành vi này bằng lớp ActivityManager.AppTask , bằng cờ Intent hoặc bằng thuộc tính <activity>.

Bạn luôn có thể loại trừ hoàn toàn một tác vụ khỏi màn hình Gần đây bằng cách đặt thuộc tính <activity> android:excludeFromRecents thành true.

Bạn có thể đặt số lượng tác vụ tối đa mà ứng dụng có thể đưa vào màn hình Gần đây bằng cách đặt thuộc tính <activity> android:maxRecents thành một giá trị số nguyên. Khi đạt đến số lượng tác vụ tối đa, tác vụ được sử dụng gần đây nhất sẽ biến mất khỏi màn hình Gần đây. Giá trị mặc định là 16 và giá trị tối đa là 50 (25 trên các thiết bị có bộ nhớ thấp). Các giá trị nhỏ hơn 1 là không hợp lệ.

Sử dụng lớp AppTask để xoá tác vụ

Trong hoạt động tạo một tác vụ mới trên màn hình Gần đây, bạn có thể chỉ định thời điểm xoá tác vụ và hoàn tất tất cả các hoạt động được liên kết với tác vụ đó bằng cách gọi phương thức 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")
    }
}

Giữ lại các tác vụ đã hoàn tất

Nếu bạn muốn giữ lại một tác vụ trên màn hình Gần đây, ngay cả khi hoạt động của tác vụ đó đã hoàn tất, hãy truyền cờ FLAG_ACTIVITY_RETAIN_IN_RECENTS trong phương thức addFlags() của ý định khởi chạy hoạt động đó.

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

Để đạt được hiệu quả tương tự, hãy đặt thuộc tính <activity> android:autoRemoveFromRecents thành false. Giá trị mặc định là true đối với các hoạt động tài liệu và false đối với các hoạt động thông thường. Việc sử dụng thuộc tính này sẽ ghi đè cờ FLAG_ACTIVITY_RETAIN_IN_RECENTS.

Bật tính năng chia sẻ URL gần đây (chỉ dành cho Pixel)

Trên các thiết bị Pixel chạy Android 12 trở lên, người dùng có thể chia sẻ đường liên kết đến nội dung web được xem gần đây trực tiếp từ màn hình Gần đây. Sau khi truy cập vào nội dung trong một ứng dụng, người dùng có thể vuốt sang màn hình Gần đây và tìm ứng dụng mà họ đã xem nội dung đó, sau đó nhấn vào nút đường liên kết để sao chép hoặc chia sẻ URL.

Màn hình Gần đây có đường liên kết để chia sẻ nội dung trên web được xem gần đây.

Bất kỳ ứng dụng nào cũng có thể bật tính năng liên kết Gần đây cho người dùng bằng cách cung cấp giao diện người dùng web và ghi đè onProvideAssistContent(), như minh hoạ trong ví dụ sau:

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