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

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

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

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 の 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 コードで、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();
    }
}

この例では、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 のウェブページからリンクをタップすると、デフォルトでは、Android は 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 が起動されます。この 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 をカスタマイズして、複数のウィンドウを開く動作をご自身で提供できます。

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