Hỗ trợ tính liên tục trên nhiều thiết bị trên Googlebook

Người dùng thường bắt đầu các tác vụ trên điện thoại Android (chẳng hạn như xem xét tài liệu, phân loại tin nhắn hoặc duyệt qua nhiều mặt hàng) rồi muốn chuyển sang Googlebook với màn hình lớn hơn, bàn phím thực và không gian làm việc nhiều cửa sổ.

Tính năng Tiếp tục trên trong Android 17 (cấp độ API 37) trở lên cho phép thanh tác vụ Googlebook hiển thị biểu tượng ứng dụng (gọi là đề xuất) cho hoạt động mà người dùng đang chạy trên điện thoại ở gần. Chỉ bằng một lần nhấp, người dùng có thể tiếp tục hoạt động trên Googlebook.

Việc triển khai tính năng tiếp nối trên nhiều thiết bị cho phép ứng dụng của bạn chuyển ngữ cảnh hoạt động để người dùng tiếp tục quy trình làm việc của họ, cho dù là trong ứng dụng Android thích ứng hay trong trình duyệt máy tính, tại chính vị trí mà họ đang làm dở trên điện thoại.

Chọn một quy trình bàn giao

Trước khi bật tính năng chuyển giao giữa nhiều thiết bị, hãy quyết định cách mở từng Hoạt động trong Ứng dụng Điện thoại khi người dùng chọn đề xuất Tiếp tục trên Googlebook:

Quy trình chuyển giao Nhắm mục tiêu trên Googlebook Phù hợp nhất cho Key API
Bàn giao giữa các ứng dụng Ứng dụng Android gốc chạy trong cửa sổ máy tính Các ứng dụng được tối ưu hoá cho màn hình lớn và cửa sổ kiểu máy tính trên Google Play HandoffActivityData.Builder
Chuyển giao giữa các ứng dụng bằng phương án dự phòng trên web Ứng dụng Android gốc (nếu đã cài đặt); nếu không, một URL trong trình duyệt máy tính mặc định Ứng dụng được tối ưu hoá cho trải nghiệm cửa sổ gốc trên máy tính nhưng yêu cầu trình duyệt dự phòng đáng tin cậy khi ứng dụng chưa được cài đặt trên Googlebook setFallbackUri
Bàn giao trực tiếp cho web URL trong trình duyệt mặc định trên máy tính (hoặc bị ứng dụng gốc chặn nếu được định cấu hình cho Đường liên kết đến ứng dụng Android) Quy trình mà ứng dụng web của bạn là trải nghiệm chính trên máy tính HandoffActivityData.createWebHandoff

Bật tính năng chuyển giao cho một hoạt động

Theo mặc định, tính năng Tiếp tục được tắt và được định cấu hình cho từng hoạt động. Hãy làm theo các bước sau trong mỗi hoạt động gửi trên điện thoại:

  1. Gọi setHandoffEnabled và truyền true sau khi hoạt động đã tải trạng thái cần thiết để chuyển giao. Sau khi được bật, hệ thống có thể yêu cầu dữ liệu bàn giao trong khi hoạt động đang ở nền trước hoặc chuyển sang nền.
  2. Ghi đè onHandoffActivityDataRequested để trả về một đối tượng HandoffActivityData khác rỗng. Bạn có thể truy vấn isHandoffEnabled bất cứ lúc nào để kiểm tra xem hoạt động chuyển giao có đang hoạt động hay không.
import android.app.Activity
import android.app.HandoffActivityData
import android.app.HandoffActivityDataRequestInfo
import android.os.Bundle
import android.os.PersistableBundle

class DocumentEditorActivity : Activity() {

    private var documentId: String = "doc_42"
    private var scrollOffsetPx: Int = 0

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

        // Enable handoff once the activity is ready to transfer state.
        setHandoffEnabled(true, null)
    }

    override fun onHandoffActivityDataRequested(
        handoffRequestInfo: HandoffActivityDataRequestInfo
    ): HandoffActivityData {
        val extras = PersistableBundle().apply {
            putString(EXTRA_DOCUMENT_ID, documentId)
            putInt(EXTRA_SCROLL_OFFSET, scrollOffsetPx)
        }

        return HandoffActivityData.Builder(getComponentName())
            .setExtras(extras)
            .build()
    }

    companion object {
        const val EXTRA_DOCUMENT_ID = "extra_document_id"
        const val EXTRA_SCROLL_OFFSET = "extra_scroll_offset"
    }
}

Trạng thái của activity gói để chuyển giao

Hệ thống gọi onHandoffActivityDataRequested trong hai trường hợp:

  • Bàn giao ở nền trước: Hoạt động ở nền trước trên điện thoại và người dùng bắt đầu bàn giao từ thanh tác vụ của Googlebook.
  • Chụp trạng thái nền: Hoạt động chuyển sang nền trên điện thoại và hệ thống sẽ lưu trữ tải trọng chuyển giao vào bộ nhớ đệm trong trường hợp người dùng chọn đề xuất trên Googlebook sau này.

Kiểm tra xem yêu cầu chuyển giao có đang hoạt động hay không

Kiểm tra HandoffActivityDataRequestInfo bằng cách sử dụng isActiveRequest để phân biệt giữa một hoạt động bàn giao do người dùng chủ động thực hiện và một hoạt động lưu trong nền:

  • Nếu isActiveRequest trả về true, tức là người dùng đã chủ động yêu cầu tiếp tục trên một thiết bị khác trong khi hoạt động đang hiển thị.
  • Nếu isActiveRequest trả về false, hệ thống sẽ lưu trạng thái vào bộ nhớ đệm khi hoạt động dừng.

Nếu bạn hiển thị một chỉ báo trực quan trên điện thoại để xác nhận rằng việc cần làm đang chuyển sang Googlebook, hãy lên lịch cập nhật giao diện người dùng không đồng bộ để onHandoffActivityDataRequested trả về mà không bị trễ.

Đính kèm các phần bổ sung trạng thái và một giải pháp dự phòng trên web

Phần dữ liệu thực tế HandoffActivityData chứa các thông tin sau:

  • Thành phần đích (bắt buộc đối với hoạt động chuyển giao giữa các ứng dụng): Truyền this.getComponentName() (ComponentName của hoạt động) hoặc ComponentName(this, TargetActivity::class.java) rõ ràng đến HandoffActivityData.Builder để chỉ định hoạt động nào sẽ khởi chạy trên Googlebook.
  • Thông số bổ sung (không bắt buộc): Truyền một PersistableBundle đến setExtras() chứa giá trị nhận dạng và toạ độ giao diện người dùng (ví dụ: mã nhận dạng tài liệu mở, khoá thẻ đang hoạt động hoặc vị trí cuộn). Gói phải có kích thước nhỏ hơn 50 KB.
  • URI dự phòng (không bắt buộc): Truyền một Uri trên web đến setFallbackUri để Googlebook có thể mở tác vụ trong trình duyệt mặc định nếu ứng dụng Android của bạn chưa được cài đặt.
import android.app.Activity
import android.app.HandoffActivityData
import android.app.HandoffActivityDataRequestInfo
import android.app.HandoffActivityParams
import android.net.Uri
import android.os.Bundle
import android.os.PersistableBundle

class ArticleReaderActivity : Activity() {

    private var articleId: String = "android-desktop-windowing"
    private var readingProgressPercent: Int = 45

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

        setHandoffEnabled(
            true,
            HandoffActivityParams.Builder()
                .setAllowHandoffWithoutPackageInstalled(true)
                .build()
        )
    }

    override fun onHandoffActivityDataRequested(
        handoffRequestInfo: HandoffActivityDataRequestInfo
    ): HandoffActivityData {
        if (handoffRequestInfo.isActiveRequest()) {
            window.decorView.post {
                onHandoffStartedToSecondaryDevice()
            }
        }

        val extras = PersistableBundle().apply {
            putString(EXTRA_ARTICLE_ID, articleId)
            putInt(EXTRA_READING_PROGRESS, readingProgressPercent)
        }

        val fallbackWebUri = Uri.Builder()
            .scheme("https")
            .authority("example.com")
            .appendPath("articles")
            .appendPath(articleId)
            .appendQueryParameter("progress", readingProgressPercent.toString())
            .build()

        return HandoffActivityData.Builder(getComponentName())
            .setExtras(extras)
            .setFallbackUri(fallbackWebUri)
            .build()
    }

    private fun onHandoffStartedToSecondaryDevice() {
        // Save local drafts and update phone UI without blocking handoff.
    }

    companion object {
        const val EXTRA_ARTICLE_ID = "extra_article_id"
        const val EXTRA_READING_PROGRESS = "extra_reading_progress"
    }
}

Định cấu hình tính năng chuyển giao trực tiếp đến web

Nếu trải nghiệm chính của bạn trên máy tính là một ứng dụng web, hãy truyền HandoffActivityParams bằng setAllowHandoffWithoutPackageInstalled(true) đến setHandoffEnabled và gọi HandoffActivityData.createWebHandoff thay vì dùng HandoffActivityData.Builder:

import android.app.Activity
import android.app.HandoffActivityData
import android.app.HandoffActivityDataRequestInfo
import android.app.HandoffActivityParams
import android.net.Uri
import android.os.Bundle

class WebDashboardActivity : Activity() {

    private var projectId: String = "proj_901"

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

        setHandoffEnabled(
            true,
            HandoffActivityParams.Builder()
                .setAllowHandoffWithoutPackageInstalled(true)
                .build()
        )
    }

    override fun onHandoffActivityDataRequested(
        handoffRequestInfo: HandoffActivityDataRequestInfo
    ): HandoffActivityData {
        val webUri = Uri.Builder()
            .scheme("https")
            .authority("app.example.com")
            .appendPath("projects")
            .appendPath(projectId)
            .build()

        return HandoffActivityData.createWebHandoff(webUri)
    }
}

Nhận và khôi phục trạng thái trên Googlebook

Khi người dùng chọn đề xuất Tiếp tục trên thanh tác vụ Googlebook, hệ thống sẽ khởi chạy ComponentName mục tiêu trong một cửa sổ trên máy tính và đính kèm các phần bổ sung PersistableBundle vào Intent khởi chạy.

Trên Googlebook, hãy xử lý yêu cầu bàn giao đến bằng cách sử dụng chế độ cửa sổ kiểu máy tính và bố cục nhiều ngăn:

  1. Trích xuất các phần bổ sung của hoạt động chuyển giao: Trong cả phương thức onCreate và onNewIntent của hoạt động nhận, hãy truy xuất các phần bổ sung từ Intent đến. Việc xử lý onNewIntent đảm bảo rằng nếu hoạt động của bạn sử dụng chế độ chạy singleTop hoặc chuyển đến một cửa sổ hiện có trên máy tính, thì cửa sổ đó sẽ cập nhật thành mục được chuyển.
  2. Điều chỉnh trạng thái cho bố cục nhiều ngăn: Trạng thái chiếm chế độ xem chi tiết toàn màn hình trên điện thoại thường tương ứng với một mục đã chọn trong bố cục danh sách chi tiết hoặc ngăn hỗ trợ trên Googlebook. Điền cả lựa chọn danh sách chính và ngăn chi tiết để cửa sổ trên máy tính có đầy đủ bối cảnh.
  3. Giữ lại trạng thái đã khôi phục khi đổi kích thước cửa sổ: Lưu trạng thái được chuyển giao trong ViewModel (SavedStateHandle) hoặc rememberSaveable trong Jetpack Compose để việc đổi kích thước cửa sổ tuỳ ý trên Googlebook không đặt lại phiên đã khôi phục.
import android.app.Activity
import android.content.Intent
import android.os.Bundle

class DocumentEditorReceiverActivity : Activity() {

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

        // Restore from the handoff intent only on initial launch so subsequent
        // desktop window configuration changes use saved instance state.
        if (savedInstanceState == null) {
            handleHandoffIntent(intent)
        }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
        handleHandoffIntent(intent)
    }

    private fun handleHandoffIntent(incomingIntent: Intent?) {
        val documentId = incomingIntent?.getStringExtra(EXTRA_DOCUMENT_ID)
            ?: return
        val scrollOffsetPx = incomingIntent.getIntExtra(EXTRA_SCROLL_OFFSET, 0)

        openDocumentInDesktopWorkspace(
            documentId = documentId,
            initialScrollOffsetPx = scrollOffsetPx
        )
    }

    private fun openDocumentInDesktopWorkspace(
        documentId: String,
        initialScrollOffsetPx: Int
    ) {
        // Load the document into the list-detail layout and restore scroll position.
    }

    companion object {
        const val EXTRA_DOCUMENT_ID = "extra_document_id"
        const val EXTRA_SCROLL_OFFSET = "extra_scroll_offset"
    }
}

Kiểm thử tính năng tiếp nối giữa điện thoại và Googlebook

Cách xác minh toàn bộ quá trình triển khai:

  1. Chuẩn bị cả hai thiết bị
    • Đăng nhập vào cùng một Tài khoản Google trên cả điện thoại Android (chạy Android 17 trở lên) và Googlebook (hoặc một thiết bị thử nghiệm chạy ở chế độ cửa sổ kiểu máy tính).
    • Bật Bluetooth và kết nối cả hai thiết bị với cùng một mạng Wi-Fi.
    • Trên cả hai thiết bị, hãy mở phần Cài đặt > Thiết bị đã kết nối > Lựa chọn ưu tiên về kết nối > Dịch vụ cho nhiều thiết bị > Tiếp tục hoạt động, làm theo lời nhắc thiết lập và bật Tasks. Để biết các điều kiện tiên quyết đầy đủ về môi trường, hãy xem phần Thiết lập và kiểm thử.
  2. Cài đặt ứng dụng
    • Đối với tính năng chuyển giao giữa các ứng dụng, hãy cài đặt bản dựng trên cả điện thoại và Googlebook.
    • Đối với web fallback (dự phòng trên web) hoặc direct-to-web handoff (bàn giao trực tiếp cho web), hãy cài đặt bản dựng trên điện thoại và không cài đặt ứng dụng trên Googlebook để bạn có thể xác minh rằng trình duyệt mở URL dự kiến.
  3. Kích hoạt tính năng chuyển giao
    • Trên điện thoại, hãy mở ứng dụng của bạn rồi chuyển đến hoạt động cho phép chuyển giao (gọi setHandoffEnabled(true, null) để chuyển giao từ ứng dụng sang ứng dụng hoặc truyền HandoffActivityParams bằng setAllowHandoffWithoutPackageInstalled(true) khi kiểm thử với ứng dụng đã gỡ cài đặt trên Googlebook).
    • Trên Googlebook, hãy kiểm tra phía bên phải của thanh tác vụ để tìm biểu tượng ứng dụng có huy hiệu Tiếp tục trên rồi chọn biểu tượng đó.
  4. Xác minh quá trình khôi phục dữ liệu trên máy tính
    • Xác nhận rằng ứng dụng của bạn mở trong một cửa sổ trên máy tính (hoặc thẻ trình duyệt) tại chính mục và vị trí cuộn trên điện thoại.
    • Đổi kích thước cửa sổ ứng dụng trên Googlebook và xác minh rằng nội dung đã khôi phục và lựa chọn vẫn duy trì trong các quá trình chuyển đổi lớp kích thước cửa sổ.

Tài nguyên khác