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 を使用して全画面サポートを有効にする。このクラスは、ウィンドウの作成やクローズ、ユーザーへの JavaScript ダイアログの送信など、ホストアプリの UI を変更する権限を WebView が要求する場合にも呼び出されます。このコンテキストでのデバッグについて詳しくは、ウェブアプリをデバッグするをご覧ください。
  • 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 の 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 に対するインテントを起動することを主な目的としています。実装する際は、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 で行われる場合でも、リンクのターゲット属性によって行われる場合でも同じです。WebChromeClient をカスタマイズして、複数のウィンドウを開く独自の動作を指定できます。

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