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

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

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

WebView が役に立つもう 1 つのシナリオは、取得にインターネット接続が常に必要なデータ(メールなど)をユーザーにアプリで提供する場合です。この場合、ネットワーク リクエストを行ってからデータを解析し、Android レイアウトでレンダリングするよりも、Android アプリで WebView をビルドし、すべてのユーザーデータを含むウェブページを表示するほうが簡単な可能性があります。代わりに、Android 搭載デバイス用に調整したウェブページを設計し、ウェブページを読み込む Android アプリに WebView を実装できます。

このドキュメントでは、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 の the WebView example をご覧ください。

アプリに WebView を追加する

アプリに WebView を追加するには、<WebView> 要素を アクティビティ レイアウトに設定するか、Activity ウィンドウ全体を onCreate 内で 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 を利用します。 getSettingsWebSettings を取得してから、 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 が呼び出すことができるインターフェース名を渡します。

最新の安全な API など、JavaScript とネイティブ コード間の通信について詳しくは、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 クラスにより、ウェブページで Toast メッセージを作成できます。showToast() メソッドを使用して。

次の例に示すように、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 によって維持されるウェブページ履歴にそってユーザーが前後に移動できるようにも設定できます。

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

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 スキームを使用するリンクを解決したりするときに制限を適用します。たとえば、 shouldOverrideUrlLoadingshouldInterceptRequest などのコールバックを実装すると、 WebView は有効な URL に対してのみコールバックを呼び出します。

たとえば、次のようなリンクの場合、WebViewshouldOverrideUrlLoading メソッドを呼び出さないことがあります。

<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を使用して、進むことが可能な履歴があるかどうかを確認できます。このチェックを行わない場合、ユーザーが履歴の最後に到達すると、goBackgoForward は何も行いません。

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

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

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

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

ウィンドウの管理

デフォルトでは、新しいウィンドウを開くリクエストは無視されます。これは、開かれるのが JavaScript によるものでも、リンクのターゲット属性によるものでも同じです。WebChromeClient をカスタマイズして、複数のウィンドウを開く動作をご自身で提供できます。

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