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

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

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

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

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

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

アプリが実行されているデバイスで最新の WebView 機能を安全に使用するには、AndroidX 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 コードでは、JavaScript の alert() 関数を使用する代わりに、Android コード内のメソッドを呼び出して Dialog を表示できます。

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

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

ユーザーがタップしたリンクを開くには、setWebViewClient() を使用して WebViewWebViewClient を指定します。ユーザーがタップしたすべてのリンクは WebView に読み込まれます。クリックされたリンクを読み込む場所をより細かく制御するには、shouldOverrideUrlLoading() メソッドをオーバーライドする独自の WebViewClient を作成します。次の例では、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 スキームを使用するリンクを解決したりする際に制限を適用します。たとえば、shouldOverrideUrlLoading()shouldInterceptRequest() などのコールバックを実装した場合、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 のインテントを起動することを主な目的としています。この API を実装する際は、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);
}

AndroidX 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() は何も行いません。

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

実行時にアクティビティの状態は、ユーザーがデバイスを回転させたり、インプット メソッド エディタ(IME)を閉じたりした場合など、デバイスの構成が変更されると発生します。これらの変更により、WebView オブジェクトのアクティビティが破棄され、新しいアクティビティが作成されます。これにより、破棄されたオブジェクトの URL を読み込む新しい WebView オブジェクトも作成されます。アクティビティのデフォルトの動作を変更するには、マニフェスト内の orientation の変更をアクティビティで処理する方法を変更します。実行時に構成の変更を処理する方法については、構成の変更を処理するをご覧ください。

ウィンドウを管理する

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

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