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

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

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

WebView は、取得にインターネット接続が常に必要なデータ(メールなど)をユーザーにアプリで提供する場合にも役立ちます。この場合、ネットワーク リクエストを行ってからデータを解析し、Android レイアウトでレンダリングするよりも、Android アプリで WebView をビルドし、すべてのユーザーデータを含むウェブページを表示するほうが簡単な可能性があります。代わりに、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 の 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 を利用します。 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 が呼び出すことができるインターフェース名を渡します。

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

ユーザーがタップしたリンクを開くには、WebViewClientWebViewsetWebViewClient() を使用して指定します。ユーザーがタップしたすべてのリンクが 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 スキームを使用するリンクを解決したりするときに制限を適用します。たとえば、 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);
}

アプリで 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" を使用するページをリンクで 読み込むことができなくなります。