Jetpack Webkit で WebView の実装を簡素化する

このガイドでは、Jetpack Webkit ライブラリのメリット、仕組み、プロジェクトへの実装方法について説明します。

概要

WebView は Android 開発に不可欠ですが、Android OS バージョンによって機能に一貫性がないため、管理が難しい場合があります。Android OS バージョンごとに、固定された WebView API のセットが提供されます。Android のリリースは WebView よりも頻度が低いため、Android API では利用可能なすべての WebView 機能に対応できない可能性があります。これにより、機能のロールアウトが遅くなり、テスト費用が増加します。

Jetpack Webkit は、互換性レイヤとして機能し、ユーザーのデバイス上の最新の WebView APK を活用することで、これらの問題を解決します。また、このライブラリでのみ利用可能な新しい最新の API も含まれています。

Jetpack Webkit を使用する理由

Jetpack Webkit は、バージョン間の互換性を提供するだけでなく、開発を簡素化し、アプリの機能を向上させる新しい最新の API も提供します。

  • 最新の認証を有効にする: WebView は、最新のウェブ 認証標準である WebAuthn などをシームレスに処理し、パスキーベースのログインを可能にします。 androidx.webkit ライブラリを使用すると、この統合を完全に制御できます WebSettingsCompat.setWebAuthenticationSupport メソッドを使用して、アプリに必要なサポートレベルを構成できます。

  • パフォーマンスを向上させる: setBackForwardCacheEnabled などの API を使用して WebView のパフォーマンスを微調整したり、prefetchUrlAsyncprerenderUrlAsync などの投機的読み込み API を使用してナビゲーション レイテンシを短縮したりできます。詳細については、WebView での投機的読み込みをご覧ください。

  • 安定性を高める: 停止または応答のないレンダラ プロセスを クラッシュせずに復元します。詳細については、 WebViewRenderProcess#terminate をご覧ください。

  • 閲覧データを細かく制御する: 特定のオリジンに対して WebView に保存されている閲覧データを削除するには、WebStorageCompat クラスを使用します。

  • ページ ナビゲーションを強化する: WebView.loadUrlWebViewCompat.navigate に置き換えて、ナビゲーションのきめ細かい制御、履歴エントリ の置換、保存された状態ヘッダーのサポート、および相関ライフサイクルのトラッキングを 使用してNavigationListener実現します。詳細については、WebViewCompat.navigate を使用したページ ナビゲーションの強化をご覧ください。

  • 状態管理を最適化する: TransactionTooLargeException 状態のシリアル化中に最大バイト数を設定して、防ぎます。詳細については、WebView の状態を効率的に管理するをご覧ください。

コンポーネントについて理解する

Jetpack Webkit を効果的に使用するには、次のコンポーネント間の関係を理解する必要があります。

  • Android システムの WebView: これは Chromium ベースのレンダリング エンジンで、 Google は Chrome と同じペースで Google Play ストアを通じて定期的に更新しています。最新の機能が含まれており、すべての WebView API の基盤となる実装コードを提供します。

  • フレームワーク API(android.webkit: 特定の Android OS バージョンに固定されている API です。たとえば、Android 10 のアプリは、そのバージョンのリリース時に利用可能だった API にのみアクセスできます。そのため、最近のアップデートで WebView APK に追加された新機能を使用することはできません。たとえば、 応答のないレンダラを WebView#getWebViewRenderProcess()を使用して処理するには、Android 10 以降でのみ呼び出すことができます。

  • Jetpack Webkit ライブラリ(androidx.webkit: アプリにバンドルされている小さなライブラリです。このライブラリは、OS バージョンが固定されている Android プラットフォームで定義されている API を呼び出すのではなく、WebView APK を呼び出すブリッジとして機能します。これにより、Android 10 などの古い OS バージョンを実行しているデバイスにアプリがインストールされている場合でも、アプリは最新の WebView 機能を使用できます。たとえば、 WebViewCompat.getWebViewRenderProcess() は Framework API と同様に機能しますが、Android 10 より前のすべての OS バージョンでも呼び出すことができます。

API がフレームワークと Jetpack Webkit の両方で利用可能な場合は、Jetpack Webkit バージョンを選択することをおすすめします。これにより、幅広いデバイスで一貫した動作と互換性を確保できます。

Jetpack Webkit と APK の連携

Jetpack Webkit の API は、次の 2 つの部分で実装されています。

  • 静的 Jetpack Webkit: 静的 Jetpack Webkit ライブラリには API の実装を担当するコードの一部が含まれています。

  • WebView APK: WebView APK にはほとんどのコードが含まれています。

アプリは Jetpack Webkit API を呼び出し、次に WebView APK を呼び出します。

アプリで Jetpack Webkit のバージョンを管理できますが、ユーザーのデバイスでの WebView APK の更新を管理することはできません。通常、ほとんどのユーザーは最新バージョンの WebView APK を使用していますが、アプリは、特定のバージョンの WebView APK がサポートしていない API を呼び出さないように注意する必要があります。

Jetpack Webkit では、WebView のバージョンを手動で確認する必要もありません。 機能が利用可能かどうかを確認するには、その機能の定数を確認します。たとえば、WebViewFeature.WEB_AUTHENTICATION です。

併用の効果

Jetpack Webkit は、静的な Framework API と頻繁に更新される WebView APK のギャップを埋めます。機能検出パターンで Jetpack Webkit API を使用すると、ライブラリは、ユーザーのデバイスにインストールされている WebView APK でその機能がサポートされているかどうかを確認します。これにより、Android OS(フレームワーク)のバージョンを確認する必要がなくなります。

WebView APK が十分に新しいバージョンの場合、ライブラリはその機能を呼び出します。 そうでない場合は、機能が利用できないことを報告し、アプリがクラッシュするのを防ぎ、状況を適切に処理できるようにします。

Jetpack Webkit と Framework API の比較

このセクションでは、Jetpack Webkit ライブラリを使用する場合と使用しない場合の実装方法を比較します。

最新の認証(WebAuthn)を有効にする

Jetpack Webkit を使用しない場合

フレームワーク API ではできません。

Jetpack Webkit を使用する場合

WebViewFeature.WEB_AUTHENTICATION を活用して互換性を確認します。

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_AUTHENTICATION)) {
  WebSettingsCompat.setWebAuthenticationSupport(
      webView.settings,
      WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_APP
  )
}

オリジン(サイト固有のストレージ)のデータを削除する

Jetpack WebKit を使用しない場合

特定のオリジンデータを消去する直接的な API はありません。多くの場合、すべてのデータを消去する必要があります。

Jetpack WebKit を使用する場合

互換性 API を使用してデータを正確に削除します。次のいずれかのオプションを使用できます。

WebStorageCompat.getInstance().deleteBrowsingData()

または

WebStorageCompat.getInstance().deleteBrowsingDataForSite()

WebView のバージョンを取得する

Jetpack WebKit を使用しない場合

標準のフレームワーク クラスを使用します。

val webViewPackage = WebView.getCurrentWebViewPackage()

Jetpack WebKit を使用する場合

互換性レイヤを使用して安全に取得します。

val webViewPackage = WebViewCompat.getCurrentWebViewPackage()

応答のないレンダラ(レンダラ クライアント)を処理する

Jetpack WebKit を使用しない場合

標準のフレームワーク メソッドを使用します。

webView.setWebViewRenderProcessClient(myClient)

Jetpack WebKit を使用する場合

WebViewCompat と機能チェックを使用してクライアントを設定します。

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE)) {
  WebViewCompat.setWebViewRenderProcessClient(webView, myClient)
}

クラッシュ復旧戦略の実装に関するガイダンスについては、WebView の終了を処理する をご覧ください。API の詳細については、androidx.webkit リファレンス ドキュメントをご覧ください。

保存された状態とトランザクション サイズを管理する

WebViewCompat.saveState API を使用すると、シリアル化中にバイト数の上限を適用して履歴を削除し、重要なナビゲーション履歴を保持しながら TransactionTooLargeException クラッシュを防ぐことができます。

Jetpack WebKit を使用しない場合

標準のフレームワーク メソッドを使用します。このメソッドは、サイズ制限なしでナビゲーション スタック全体をシリアル化し、ペイロードが Android の 1 MB のトランザクション上限を超えると TransactionTooLargeException をトリガーする可能性があります。

webView.saveState(outState)

Jetpack WebKit を使用する場合

WebViewCompat を使用して、最大バイト数の上限を適用するか、前方ナビゲーション エントリを削除して、トランザクションのオーバーフローを防ぎます。

WebViewCompat.saveState(webView, outState, maxSizeBytes)

詳細については、WebView の状態を効率的に管理するをご覧ください。

履歴エントリの置換、保存された状態ヘッダーのサポート、相関ライフサイクル コールバックをサポートするウェブページに移動するには、WebView.loadUrl ではなく WebViewCompat.navigate を使用します。

Jetpack WebKit を使用しない場合

WebView.loadUrl を使用します。これは、履歴エントリの置換や相関ライフサイクル コールバックのトラッキングをサポートしていません。

webView.loadUrl("https://www.example.com")

Jetpack WebKit を使用する場合

WebViewCompat.navigateNavigationParameters を使用して、履歴エントリを置き換え、保存された状態のカスタム ヘッダーを保持し、ナビゲーションの状態を追跡します。

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
  val params = NavigationParameters.Builder()
      .setShouldReplaceCurrentEntry(true)
      .build()
  val navigation = WebViewCompat.navigate(webView, "https://www.example.com", params)
} else {
  webView.loadUrl("https://www.example.com")
}

ナビゲーションのトラッキングとパラメータ構成の詳細については、 WebViewCompat.navigate を使用したページ ナビゲーションの強化をご覧ください。

Jetpack Webkit をコードに統合する

Jetpack Webkit を使用すると、標準の WebView クラスの機能が強化されますが、元の WebView クラスが完全に置き換えられるわけではありません。

android.webkit.WebView クラスを引き続き使用できます。XML レイアウトに追加して、コード内のインスタンスへの参照を取得できます。標準のフレームワーク機能にアクセスするには、WebView インスタンスまたはその設定オブジェクトでメソッドを直接呼び出すことができます。

最新の機能にアクセスするには、Jetpack Webkit が提供する静的ヘルパー メソッド(WebViewCompatWebSettingsCompat など)を使用します。既存の WebView インスタンスをこれらのメソッドに渡します。

Kotlin

import android.webkit.WebView
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature

// You still get your WebView instance the standard way.
val webView: WebView = findViewById(R.id.my_webview)

// To enable a modern feature, you pass that instance to a Jetpack Webkit helper.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
}

Java

import android.webkit.WebView;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;

// You still get your WebView instance the standard way.
WebView webView = findViewById(R.id.my_webview);

// To enable a modern feature, you pass that instance to a Jetpack Webkit helper.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON);
}

Jetpack Webkit を実装する

Jetpack Webkit を実装するには、次の手順を行います。

ステップ 1: 依存関係を追加する

モジュールの build.gradle.kts ファイルまたは build.gradle ファイルに 次の依存関係を追加して、Jetpack Webkit を追加します。

Groovy

dependencies {
    implementation "androidx.webkit:webkit:1.16.0"
}

Kotlin

dependencies {
    implementation("androidx.webkit:webkit:1.16.0")
}

Jetpack Webkit にはシン ラッパーが含まれているため、アプリのサイズへの影響は最小限に抑えられます。

ステップ 2: 機能検出パターンを採用する

利用できない API を呼び出す際のクラッシュを防ぐには、機能チェックを使用します。各 API 呼び出しを機能チェックで囲み、API が利用できない場合のフォールバック ロジックを検討することをおすすめします。

最新の WebView API を使用する場合は、次のパターンをおすすめします。

Kotlin

import android.webkit.WebView
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature

val webView: WebView = findViewById(R.id.my_webview)

// Before you use a modern API, first check if it is supported.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    // If the check passes, it is safe to call the API.
    WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
} else {
    // Optionally, provide a fallback for older WebView versions.
}

Java

import android.webkit.WebView;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;

WebView webView = findViewById(R.id.my_webview);

// Before you use a modern API, first check if it is supported.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    // If the check passes, it is safe to call the API.
    WebSettingsCompat.setForceDark(webView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
} else {
    // Optionally, provide a fallback for older WebView versions.
}

このパターンは、アプリの堅牢性を確保するのに役立ちます。機能チェックが最初に実行されるため、機能が利用できない場合でもアプリはクラッシュしません。 WebViewFeature#isFeatureSupported チェックのパフォーマンス オーバーヘッドは ごくわずかです。