WebView でウェブアプリを作成する

WebView を使用すると、ウェブ アプリケーションまたはウェブページをクライアント アプリの一部として配信できます。WebView クラスは Android の View クラスの拡張機能であり、ウェブページをアクティビティ レイアウトの一部として表示できます。ナビゲーション コントロールやアドレスバーなど、完全に開発されたウェブブラウザの機能は含まれていません。WebView のデフォルトの動作は、ウェブページを表示することです。

WebView を使用すると、エンドユーザー契約やユーザーガイドなど、更新が必要になる可能性のある情報をアプリで提供できます。Android アプリ内で、WebView を含む Activity を作成し、それを使用してオンラインでホストされているドキュメントを表示できます。

WebView は、メールなど、データの取得にインターネット接続が必要なデータをアプリがユーザーに提供する場合にも役立ちます。この場合、ネットワーク リクエストを実行してデータを解析し、Android レイアウトでレンダリングするよりも、すべてのユーザーデータを含むウェブページを表示する WebView を Android アプリに構築する方が簡単です。代わりに、Android 搭載デバイス向けにカスタマイズされたウェブページを設計し、そのウェブページを読み込む WebView を Android アプリに実装できます。

このドキュメントでは、WebView の使用を開始する方法、ウェブページから Android アプリのクライアントサイド コードに JavaScript をバインドする方法、ページ ナビゲーションを処理する方法、WebView の使用時にウィンドウを管理する方法について説明します。

以前のバージョンの Android で WebView を操作する

アプリが実行されているデバイスで、より新しい WebView 機能を安全に使用するには、Jetpack Webkit ライブラリを追加します。これは、以前のプラットフォーム バージョンでは利用できない android.webkit API を使用するためにアプリに追加できる静的ライブラリです。

次のように build.gradle ファイルに追加します。

Kotlin

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

Groovy

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

詳しくは、GitHub の WebView の例をご覧ください。

アプリに WebView を追加する

アプリに WebView を追加するには、アクティビティ レイアウトに <WebView> 要素を含めるか、onCreate で Activity ウィンドウ全体を WebView として設定します。

アクティビティ レイアウトに WebView を追加する

レイアウトで WebView をアプリに追加するには、アクティビティのレイアウト XML ファイルに次のコードを追加します。

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

WebView にウェブページを読み込むには、次の例に示すように loadUrl を使用します。

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.loadUrl("http://www.example.com")

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");

onCreate() に WebView を追加する

代わりにアクティビティの onCreate() メソッドでアプリに WebView を追加するには、次のようなロジックを使用します。

Kotlin

val myWebView = WebView(activityContext)
setContentView(myWebView)

Java

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

次に、ページを読み込みます。

Kotlin

myWebView.loadUrl("http://www.example.com")

Java

myWebView.loadUrl("https://www.example.com");

または、HTML 文字列から URL を読み込みます。

Kotlin

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
val unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
myWebView.loadData(encodedHtml, "text/html", "base64")

Java

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
String unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

アプリがインターネットにアクセスできる必要があります。インターネット アクセスを取得するには、次の例に示すように、マニフェスト ファイルで INTERNET 権限をリクエストします。

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

WebView は、次のいずれかの方法でカスタマイズできます。

  • WebChromeClient を使用して全画面表示のサポートを有効にする。このクラスは、WebView がホストアプリの UI を変更する権限(ウィンドウの作成や終了、ユーザーへの JavaScript ダイアログの送信など)を必要とする場合にも呼び出されます。このコンテキストでのデバッグの詳細については、ウェブアプリをデバッグするをご覧ください。
  • コンテンツのレンダリングに影響するイベント(フォーム送信のエラーや WebViewClient を使用したナビゲーションなど)を処理する。このサブクラスを使用して、URL の読み込みをインターセプトすることもできます。
  • WebSettings を変更して JavaScript を有効にする。
  • JavaScript を使用して、WebView に挿入した Android フレームワーク オブジェクトにアクセスする。

WebView で JavaScript を使用する

WebView に読み込むウェブページで JavaScript を使用している場合は、WebView で JavaScript を有効にする必要があります。JavaScript を有効にすると、アプリコードと JavaScript コードの間にインターフェースを作成できます。

JavaScript を有効にする

デフォルトでは、JavaScript は WebView で無効になっています。これは、WebView に接続されている WebSettings を介して有効にできます。getSettings で WebSettings を取得し、setJavaScriptEnabled で JavaScript を有効にします。

次の例をご覧ください。

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

WebSettings では、他にもさまざまな設定にアクセスできます。たとえば、Android アプリの WebView 専用に設計されたウェブ アプリケーションを開発している場合は、setUserAgentString を使用してカスタム ユーザー エージェント文字列を定義し、ウェブページでカスタム ユーザー エージェントをクエリして、ウェブページをリクエストしているクライアントが Android アプリであることを確認できます。

JavaScript コードを Android コードにバインドする

Android アプリの WebView 専用に設計されたウェブ アプリケーションを開発する場合は、JavaScript コードとクライアントサイドの Android コードの間にインターフェースを作成できます。たとえば、JavaScript コードで Android コードのメソッドを呼び出して、JavaScript の alert() 関数を使用する代わりに Dialog を表示できます。

JavaScript と Android コードの間に新しいインターフェースをバインドするには、addJavascriptInterface を呼び出し、JavaScript にバインドするクラス インスタンスと、JavaScript がクラスにアクセスするために呼び出すインターフェース名を渡します。

JavaScript とネイティブ コード間の通信(より最新で安全な API を含む)について詳しくは、JSBridge を使用してネイティブ API にアクセスするをご覧ください。

例として、Android アプリに次のクラスを含めることができます。

Kotlin

/** Instantiate the interface and set the context.  */
class WebAppInterface(private val mContext: Context) {

    /** Show a toast from the web page.  */
    @JavascriptInterface
    fun showToast(toast: String) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
    }
}

Java

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context. */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page. */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

この例では、WebAppInterface クラスにより、ウェブページで showToast() メソッドを使用して Toast メッセージを作成できます。

次の例に示すように、addJavascriptInterface() を使用して、このクラスを WebView で実行される JavaScript にバインドできます。

Kotlin

val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")

Java

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

これにより、WebView で実行されている JavaScript 用の Android というインターフェースが作成されます。この時点で、ウェブ アプリケーションは WebAppInterface クラスにアクセスできます。たとえば、次の HTML と JavaScript は、ユーザーがボタンをタップしたときに新しいインターフェースを使用してトースト メッセージを作成します。

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

JavaScript から Android インターフェースを初期化する必要はありません。WebView により、ウェブページで自動的に利用できるようになります。ユーザーがボタンをタップすると、showAndroidToast() 関数は Android インターフェースを使用して WebAppInterface.showToast() メソッドを呼び出します。

ページ ナビゲーションを処理する

ユーザーが WebView 内のウェブページからリンクをタップすると、デフォルトでは、Android は URL を処理するアプリを起動します。通常は、デフォルトのウェブブラウザが開き、リンク先 URL が読み込まれます。ただし、WebView でこの動作をオーバーライドして、WebView 内でリンクを開くことができます。その後、WebView によって保持されているウェブページの履歴をユーザーが前後に移動できるようにします。

ユーザーがタップしたリンクを開くには、setWebViewClient を使用して WebView に WebViewClient を指定します。ユーザーがタップしたすべてのリンクが WebView に読み込まれます。クリックされたリンクの読み込み先をより細かく制御するには、shouldOverrideUrlLoading メソッドをオーバーライドする独自の WebViewClient を作成します。次の例では、MyWebViewClient が Activity の内部クラスであることを前提としています。

Kotlin

private class MyWebViewClient : WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        if (Uri.parse(url).host == "www.example.com") {
            // This is your website, so don't override. Let your WebView load
            // the page.
            return false
        }
        // Otherwise, the link isn't for a page on your site, so launch another
        // Activity that handles URLs.
        Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
            startActivity(this)
        }
        return true
    }
}

Java

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        if ("www.example.com".equals(request.getUrl().getHost())) {
      // This is your website, so don't override. Let your WebView load the
      // page.
      return false;
    }
    // Otherwise, the link isn't for a page on your site, so launch another
    // Activity that handles URLs.
    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
    startActivity(intent);
    return true;
  }
}

次に、以下のとおり、WebView 用に新しい WebViewClient のインスタンスを作成します。

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

ユーザーがリンクをタップすると、システムは shouldOverrideUrlLoading メソッドを呼び出します。このメソッドは、URL ホストが特定のドメインと一致するかどうかをチェックします(上記の例を参照)。一致する場合、メソッドは false を返し、URL の読み込みをオーバーライドしません。WebView は通常どおり URL を読み込みます。URL ホストが一致しない場合は、Intent が作成され、URL を処理するためのデフォルトの Activity が起動されます。これは、ユーザーのデフォルトのウェブブラウザに解決されます。

カスタム URL を処理する

WebView は、カスタム URL スキームを使用するリソースをリクエストしてリンクを解決するときに制限を適用します。たとえば、shouldOverrideUrlLoading や shouldInterceptRequest などのコールバックを実装すると、WebView は有効な URL に対してのみそれらを呼び出します。

たとえば、WebView は、次のようなリンクに対して shouldOverrideUrlLoading メソッドを呼び出さない場合があります。

<a href="showProfile">Show Profile</a>

前の例に示したような無効な URL は、WebView で一貫性のない処理が行われるため、代わりに整形式の URL を使用することをおすすめします。組織が管理するドメインには、カスタム スキームまたは HTTPS URL を使用できます。

前の例のようにリンクで単純な文字列を使用する代わりに、次のようなカスタム スキームを使用できます。

<a href="example-app:showProfile">Show Profile</a>

この URL は、次のように shouldOverrideUrlLoading メソッドで処理できます。

Kotlin

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
const val APP_SCHEME = "example-app:"

override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
    return if (url?.startsWith(APP_SCHEME) == true) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8")
        respondToData(urlData)
        true
    } else {
        false
    }
}

Java

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
private static final String APP_SCHEME = "example-app:";

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(APP_SCHEME)) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
        respondToData(urlData);
        return true;
    }
    return false;
}

shouldOverrideUrlLoading API は主に、特定の URL のインテントを起動することを目的としています。実装する際は、WebView が処理する URL に対して false を返すようにしてください。ただし、インテントの起動に限定されるわけではありません。上記のコードサンプルでは、インテントの起動を任意のカスタム動作に置き換えることができます。

WebView が URL の読み込みをオーバーライドすると、アクセスしたウェブページの履歴が自動的に蓄積されます。goBack と goForward を使用して、履歴を前後に移動できます。

たとえば、次のコードは、Activity でデバイスの戻るボタンを使用して戻る方法を示しています。

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // Check whether the key event is the Back button and if there's history.
    if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
        myWebView.goBack()
        return true
    }
    // If it isn't the Back button or there isn't web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Check whether the key event is the Back button and if there's history.
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // If it isn't the Back button or there's no web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event);
}

アプリで Jetpack AppCompat 1.6.0 以降を使用している場合は、上記のコード スニペットをさらに簡素化できます。

Kotlin

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}

Java

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack();
    }
}

canGoBack メソッドは、ユーザーがアクセスしたウェブページの履歴がある場合に true を返します。同様に、canGoForward を使用して、進む履歴があるかどうかを確認できます。このチェックを行わないと、ユーザーが履歴の最後に到達した後に goBack と goForward は何も行いません。

アクティビティの再作成やバックグラウンド プロセスの再利用をまたいでナビゲーション履歴を保持するには、saveState を使用して WebView のナビゲーション履歴を Bundle にシリアル化し、restoreState を使用して復元します。保存された状態のサイズ制限を安全に管理し、TransactionTooLargeException を回避する方法については、WebView の状態を効率的に管理するをご覧ください。

デバイス設定の変更を処理する

実行時に、ユーザーがデバイスを回転させたり、インプット メソッド エディタ(IME)を閉じたりするなど、デバイスの構成が変更されると、アクティビティの状態が変化します。これらの変更により、WebView オブジェクトのアクティビティが破棄され、新しいアクティビティが作成されます。これにより、破棄されたオブジェクトの URL を読み込む新しい WebView オブジェクトも作成されます。onSaveInstanceState() 内で saveState を使用し、再作成時に restoreState を使用して、ユーザーのナビゲーション コンテキストを復元できます。状態サイズを安全に管理するための戦略については、WebView の状態を効率的に管理するをご覧ください。

アクティビティのデフォルトの動作を変更するには、マニフェストで orientation の変更を処理する方法を変更します。実行時の構成変更の処理について詳しくは、構成の変更に対処するをご覧ください。

ウィンドウの管理

デフォルトでは、新しいウィンドウを開くリクエストは無視されます。これは、JavaScript で開かれた場合でも、リンクの target 属性で開かれた場合でも同じです。WebChromeClient をカスタマイズして、複数のウィンドウを開く独自の動作を提供できます。

アプリのセキュリティを強化するには、ポップアップや新しいウィンドウが開かないようにすることをおすすめします。この動作を実装する最も安全な方法は、"true" を setSupportMultipleWindows に渡すことです。ただし、setSupportMultipleWindows() が依存する onCreateWindow メソッドはオーバーライドしないでください。このロジックにより、リンクで target="_blank" を使用するページが読み込まれなくなります。

メモリとパフォーマンスを管理する

WebView は、個別のプロセスとネイティブ コードを持つ Chromium ベースのエンジンで動作するため、ベースラインのメモリ使用量は標準の Android ビューよりも高くなります。WebView インスタンスを再利用し、適切に破棄することは、アプリの安定性を維持するために不可欠です。

ウェブ コンテンツを使用する際のアプリのメモリ使用量を診断して最適化する方法については、WebView のメモリを管理して診断するをご覧ください。