在 Android 11 上使用自訂分頁

Android 11 導入了變更,讓應用程式與使用者裝置上安裝的其他應用程式互動。請參閱 Android 說明文件,進一步瞭解這些變更。

如果 Android 應用程式使用自訂分頁指定 SDK 級別 30 以上,你可能須進行調整。本文將介紹這些應用程式可能需要進行的異動。

在最簡單的情況下,使用者可以用單行文字啟動「自訂分頁」,如下所示:

new CustomTabsIntent.Builder().build()
        .launchUrl(this, Uri.parse("https://www.example.com"));

如果應用程式使用這個方法啟動應用程式,甚至是新增 UI 自訂項目 (例如變更工具列顏色新增動作按鈕),也不必在應用程式中進行任何變更。

優先使用原生應用程式

但如果您遵循最佳做法,可能就要做出一些調整。

第一個相關的最佳做法是,如果應用程式能夠處理該意圖,就應優先使用原生應用程式處理意圖,而不應使用「自訂分頁」。

Android 11 以上版本

Android 11 推出了新的意圖旗標 FLAG_ACTIVITY_REQUIRE_NON_BROWSER,這是嘗試開啟原生應用程式的建議方式,因為應用程式不需要宣告任何套件管理員查詢。

static boolean launchNativeApi30(Context context, Uri uri) {
    Intent nativeAppIntent = new Intent(Intent.ACTION_VIEW, uri)
            .addCategory(Intent.CATEGORY_BROWSABLE)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                    Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
    try {
        context.startActivity(nativeAppIntent);
        return true;
    } catch (ActivityNotFoundException ex) {
        return false;
    }
}

解決方法是嘗試啟動 Intent,並使用 FLAG_ACTIVITY_REQUIRE_NON_BROWSER 要求 Android 避免在啟動時使用瀏覽器。

如果找不到可以處理此意圖的原生應用程式,系統將擲回 ActivityNotFoundException

Android 11 之前的版本

即使應用程式可能指定 Android 11 或 API 級別 30,先前的 Android 版本也無法解讀 FLAG_ACTIVITY_REQUIRE_NON_BROWSER 旗標,因此在以下情況下,我們必須查詢套件管理員:

private static boolean launchNativeBeforeApi30(Context context, Uri uri) {
    PackageManager pm = context.getPackageManager();

    // Get all Apps that resolve a generic url
    Intent browserActivityIntent = new Intent()
            .setAction(Intent.ACTION_VIEW)
            .addCategory(Intent.CATEGORY_BROWSABLE)
            .setData(Uri.fromParts("http", "", null));
    Set<String> genericResolvedList = extractPackageNames(
            pm.queryIntentActivities(browserActivityIntent, 0));

    // Get all apps that resolve the specific Url
    Intent specializedActivityIntent = new Intent(Intent.ACTION_VIEW, uri)
            .addCategory(Intent.CATEGORY_BROWSABLE);
    Set<String> resolvedSpecializedList = extractPackageNames(
            pm.queryIntentActivities(specializedActivityIntent, 0));

    // Keep only the Urls that resolve the specific, but not the generic
    // urls.
    resolvedSpecializedList.removeAll(genericResolvedList);

    // If the list is empty, no native app handlers were found.
    if (resolvedSpecializedList.isEmpty()) {
        return false;
    }

    // We found native handlers. Launch the Intent.
    specializedActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(specializedActivityIntent);
    return true;
}

這裡的做法是向套件管理員查詢支援一般 http 意圖的應用程式。這類應用程式可能是瀏覽器。

接著,針對要啟動的特定網址,查詢可處理其依附元件的應用程式。系統會同時傳回瀏覽器和原生應用程式設定來處理該網址。

現在,請移除第二份清單所列的所有瀏覽器,且只會保留原生應用程式。

如果清單空白,表示我們沒有原生處理常式,並傳回 false。否則,我們會啟動原生處理常式的意圖。

平台比一比

我們必須確保在各種情境下都使用適當的方法:

static void launchUri(Context context, Uri uri) {
    boolean launched = Build.VERSION.SDK_INT >= 30 ?
            launchNativeApi30(context, uri) :
            launchNativeBeforeApi30(context, uri);

    if (!launched) {
        new CustomTabsIntent.Builder()
                .build()
                .launchUrl(context, uri);
    }
}

Build.VERSION.SDK_INT 提供我們需要的資訊。如果此值大於或等於 30,Android 知道 FLAG_ACTIVITY_REQUIRE_NON_BROWSER,我們就能嘗試使用新的方法啟動小品應用程式。否則,我們嘗試以舊方法啟動。

如果原生應用程式啟動失敗,我們會啟動「自訂」分頁。

這項最佳做法中有一些樣板。我們正設法簡化程式庫的複雜度 來簡化這項工作敬請密切留意 android-browser-helper 支援資料庫的更新。

偵測支援自訂分頁的瀏覽器

另一個常見的模式是使用 PackageManager 偵測哪些瀏覽器支援自訂分頁。這種做法的常見用途是在意圖上設定套件,以避免在連線至自訂分頁服務時造成應用程式混淆對話方塊,或選擇連線瀏覽器。

如果指定 API 級別 30,開發人員需要在 Android 資訊清單新增查詢區段,同時宣告符合支援自訂分頁瀏覽器的意圖篩選器。

<queries>
    <intent>
        <action android:name=
            "android.support.customtabs.action.CustomTabsService" />
    </intent>
</queries>

標記就緒後,用於查詢支援自訂分頁的瀏覽器的「現有程式碼」就可正常運作。

常見問題

問:這個程式碼會尋找自訂分頁供應商,查詢可處理 https:// 意圖的應用程式,但查詢篩選器只會宣告 android.support.customtabs.action.CustomTabsService 查詢。不應該宣告 https:// 意圖嗎?

答:宣告查詢篩選器時,系統會篩選 PackageManager 查詢的回應,而非查詢本身。支援自訂分頁的瀏覽器會宣告處理 CustomTabsService,因此不會遭到篩除。系統會篩除不支援自訂分頁的瀏覽器。

結論

為配合 Android 11 調整現有「自訂分頁」的整合方式,這些為必要變更。如要進一步瞭解如何將自訂分頁整合至 Android 應用程式,請先參閱實作指南,再參閱最佳做法,瞭解如何建構一流整合功能。

如有任何問題或意見,歡迎與我們聯絡。