WebView でのウェブアプリの作成

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

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

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

このドキュメントでは、WebView の使用ガイドと、ページ ナビゲーションの処理や、ウェブページから Android アプリのクライアント側コードへの JavaScript のバインドなど、他の処理方法を示します。

アプリへの WebView の追加

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

アクティビティ レイアウトで 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 =
            "&lt;html&gt;&lt;body&gt;'%23' is the percent code for ‘#‘ &lt;/body&gt;&lt;/html&gt;"
    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 =
         "&lt;html&gt;&lt;body&gt;'%23' is the percent code for ‘#‘ &lt;/body&gt;&lt;/html&gt;";
    String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
            Base64.NO_PADDING);
    myWebView.loadData(encodedHtml, "text/html", "base64");
    

注: この HTML の機能には制限があります。エンコード オプションの詳細については、loadData()loadDataWithBaseURL() をご覧ください。

ただし、この機能を使用するにはインターネットへのアクセスが必要です。インターネット アクセスを取得するには、マニフェスト ファイルで INTERNET 権限をリクエストしてください。次に例を示します。

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

基本的な WebView でウェブページを表示するにあたって必要な作業は以上です。また、以下のように変更して WebView をカスタマイズできます。

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

WebView での JavaScript の使用

WebView で読み込む予定のウェブページで JavaScript が使用されている場合、 で 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 が呼び出すことができるインターフェース名を渡します。

例として、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();
        }
    }
    

注意: targetSdkVersion を 17 以上に設定している場合、JavaScript で利用できるようにしたい任意のメソッドに @JavascriptInterface アノテーションを追加する必要があります(メソッドは public にする必要もあります)。アノテーションを設定しない場合、Android 4.2 以降ではウェブページからメソッドにアクセスできません。

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

addJavascriptInterface() を使用して WebView で実行する JavaScript にこのクラスをバインドし、インターフェースに Android という名前を付けることができます。次に例を示します。

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() メソッドを呼び出します。

注: JavaScript にバインドされたオブジェクトは、作成されたスレッドではなく、別のスレッドで実行されます。

注意: addJavascriptInterface() を使用すると、JavaScript で Android アプリを制御できるようになります。これは非常に便利な機能ですが、危険なセキュリティ問題となる可能性があります。WebView の HTML が信頼できない場合(HTML の一部またはすべてが不明なユーザーまたはプロセスによって提供されるなど)、攻撃者は、クライアント側のコードを実行する HTML、および攻撃者が選択した任意のコードを含めることができます。そのため、WebView に表示されるすべての HTML および JavaScript を記述しない限り、addJavascriptInterface() を使用しないでください。また、WebView 内では意図されている以外のウェブページにユーザーが移動できないようにする必要があります(代わりに、外部リンクはユーザーのデフォルトのブラウザ アプリケーションで開くようにします。URL リンクはすべてユーザーのウェブブラウザで開くのがデフォルトです。したがって注意が必要になるのは、次のセクションで説明するような、ページ ナビゲーションに対応する場合に限ります)。

ページ ナビゲーションの処理

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

注: セキュリティ上の理由から、システムのブラウザアプリはアプリとアプリケーション データを共有しません。

ユーザーがクリックしたリンクを開くには、setWebViewClient() を使用して WebViewWebViewClient を提供します。次に例を示します。

Kotlin

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

Java

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

これで完了です。これで、ユーザーがクリックしたすべてのリンクが WebView に読み込まれます。

クリックしたリンクの読み込み先をさらに制御したい場合は、shouldOverrideUrlLoading() メソッドをオーバーライドする独自の WebViewClient を作成します。次に例を示します。

Kotlin

    private class MyWebViewClient : WebViewClient() {

        override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
            if (Uri.parse(url).host == "www.example.com") {
                // This is my web site, so do not override; let my WebView load the page
                return false
            }
            // Otherwise, the link is not for a page on my 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, String url) {
            if ("www.example.com".equals(Uri.parse(url).getHost())) {
                // This is my website, so do not override; let my WebView load the page
                return false;
            }
            // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            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 ホストが特定のドメイン(上記で定義)に一致するかどうかを確認します。一致した場合、メソッドは URL の読み込みをオーバーライドしないように false を返します(WebView が通常どおり URL を読み込むことができるようにします)。URL ホストが一致しない場合、Intent が作成され、URL 処理用のデフォルトのアクティビティが起動されます(このアクティビティは、ユーザーのデフォルトのウェブブラウザに解決されます)。

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

Activity でデバイスの [戻る] ボタンを使用して後方に移動する方法は次のとおりです。

Kotlin

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        // Check if the key event was the Back button and if there's history
        if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
            myWebView.goBack()
            return true
        }
        // If it wasn't the Back key or there's no 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 if the key event was the Back button and if there's history
        if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
            myWebView.goBack();
            return true;
        }
        // If it wasn't the Back key or there's no web page history, bubble up to the default
        // system behavior (probably exit the activity)
        return super.onKeyDown(keyCode, event);
    }
    }

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

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

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

ウィンドウの管理

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

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