Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(10)

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(10)

JavaScriptとネイティブの通信の仕組み ― JsAlert方式/ローカルHTTPサーバ方式

2014年4月8日

ついに書籍転載最終回。Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブの通信の仕組みの解説が完結する。

  • このエントリーをはてなブックマークに追加

 オープンソースのフレームワーク「Apache Cordova」(Adobe版:「 PhoneGap」)を用いると、HTML5でiOSとAndroid向けのアプリをまとめて作成できます。この連載記事(=書籍転載)の第1回第6回で、その開発方法を一通り解説しています。また、第7回からは、「JavaScriptコード」と「iOS/Andoridネイティブ機能」をつなぐ仕組みを説明しています。

書籍転載について

 本コーナーは、技術評論社発行の書籍『[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門』の中から、特にBuild Insiderの読者に有用だと考えられる項目を編集部が選び、同社の許可を得て転載したものです。

 『[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門』の詳細や購入は技術評論社のサイト目次ページをご覧ください。

ご注意

本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。

 前回は「カスタムURLスキーム方式/iOSで特定のページの読み込みを制限する」を説明しました。本稿はその続きです。

11.7 JsAlert方式

 WebViewには、通常のブラウザでは設定できないような細かい挙動を設定できる項目があります。アラートダイアログを画面に表示するには、JavaScriptではalert関数を用いますが、AndroidのWebViewの場合、このalert関数を呼び出した際の挙動を設定できます。

 JavaScriptのalert関数の挙動をカスタマイズして、JavaScriptからネイティブに対して値を渡すのがJsAlert方式です。

11.7.1 実装

 WebViewに読み込まれたHTML内で、JavaScriptのalert関数が呼び出されると、WebViewClientクラスのonJsAlertメソッドが呼び出されます。onJsAlertメソッドには、alert関数に渡した引数とともに、呼び出し元のURLが渡されます(リスト11.21)。

 前述したaddJavascriptInterface方式では、信頼できるHTMLでも、そうでなくても、すべてのHTMLに対してブリッジが公開されていました。これでは信頼できないHTMLからの呼び出しに対して制限をかけることができません。

 onJsAlertメソッドを用いるこの方法の場合、呼び出し元のJavaScriptのURLがネイティブ側に渡されます。ネイティブ側では、そのURLを基に、信頼できるHTMLかどうかを判断できます。例えば、渡されてきたURLが「file:///android_asset」から始まるURLであればその呼び出しを受け入れ、そうでなければブロックするというようなこともできます。

 ただしこの方法の場合、JavaScriptからJavaのコードを呼び出すことしかできません。したがって、JavaからJavaScriptへ値を返したい場合は、また別の方法を取ります。

Java
package com.example.hybridappsandbox;

import android.os.Bundle;
import android.app.Activity;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class CustomSchemaExample extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    WebView webView = new WebView(this);
    webView.getSettings().setJavaScriptEnabled(true);

    webView.setWebViewClient(new MyWebViewClient());

    webView.loadUrl("file:///android_asset/customSchemaExample.html");

    setContentView(webView);
  }

  class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
      // 「apicall://」から始まるURLを読み込む際には、
      // ネイティブ機能の呼び出しと見なす
      if (url.startsWith("apicall://")) {
        String[] params = url.split("/?");
        // urlをパースしてネイティブの機能を呼び出す
        // ...

        return true;
      }

      return false;
    }
  }
}
リスト11.21 WebView.onJsAlertメソッドを利用したメッセージング

11.8 ローカルHTTPサーバ方式

 JavaScriptには、一般にAjaxと呼ばれるHTTPリクエストを投げる機能があります。Ajaxを用いてJavaScriptからWeb上に公開されているAPIを呼び出すことにより、JavaScriptだけでは実装できない機能も利用できます。

 このローカルHTTPサーバ方式は、AjaxでWebAPIに問い合わせる方法と似ています。

 違うのは、問い合わせ先のWebAPIを公開しているサーバが、端末内のローカルにあることです。HTML5ハイブリッドアプリを格納している端末の中で、簡易的なHTTPサーバを構築して、そのHTTPサーバにAjaxでリクエストすることで、ネイティブ側のコードが実行されます(図11.1)。

図11.1 ローカル
図11.1 ローカル

11.8.1 ローカルにHTTPサーバを構築する

 ここで構築するHTTPサーバは、あくまでJavaScriptとネイティブとのブリッジを提供するためだけに用いるので、HTTPの仕様に完全に沿ったサーバではなく、Ajaxによるリクエストに応えられるだけの簡易的なものを用意します。12

 次のAndroidでの例では、超軽量で扱いやすいHTTPサーバライブラリであるnanohttpdを利用します。

 まず、AndroidManifest.xmlにHTTPサーバを構築するにあたって必要なリスト11.22のパーミッションを追加します。

XML
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
リスト11.22 AndroidManifest.xmlに追加するパーミッション

 次に、nanohttpdをプロジェクトに追加したのち、リスト11.23のようにWebViewとともにネイティブとのブリッジを提供するHTTPサーバを記述します。

Java
package com.example.hybridappsandbox;

import java.io.IOException;
import java.util.Map;

import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.NanoHTTPD.Response.Status;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.webkit.ConsoleMessage;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class APIServerExample extends Activity {

  APIHttpServer server;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    server = new APIHttpServer();
    try {
      // ローカルHTTPサーバを開始する
      server.start();
    } catch (IOException e) {
      e.printStackTrace();
    }

    WebView webView = new WebView(this);
    webView.setWebChromeClient(new MyWebChromeClient());

    // fileプロトコルから他のプロトコルのドメインにAjaxを投げられるように設定する
    try {
      webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
    } catch (e) { }

    webView.loadUrl("file:///android_asset/apiServerExample.html");

    setContentView(webView);
  }

  public class APIHttpServer extends NanoHTTPD {

    public APIHttpServer() {
      // ポート4000でHTTPサーバを開始する
      super(4000);
    }

    @Override
    public Response serve(String uri, Method method,
      Map<String, String> headers, Map<String, String> params,
      Map<String, String> files) {

      // ここに来たリクエストに応じて、ネイティブの機能を呼び出す
      // ...
      Log.d(
        getClass().getSimpleName(),
        "uri=" + uri + ", params=" + params
      );

      return new NanoHTTPD.Response(Status.OK, "application/json", "{}");
    }
  }
}
リスト11.23 nanohttpdを利用して内部APIサーバを構築する

 ブリッジを提供するHTTPサーバを用意できたら、今度はJavaScriptからAjaxを用いてHTTPサーバに対して―ここではhttp://localhost:4000に対してリクエストを投げます。

 このHTML内では、コードを簡略化するために、jQuery互換のAPIを持つZepto.jsを使ってAjaxを利用しています(リスト11.24)。

HTML
<html>
<head></head>
<body>

<script type="text/javascript" src="zepto.min.js"></script>
<script type="text/javascript">

$(function() {
  // localhostに向かってリクエストを投げる
  $.getJSON("http://localhost:4000/some_api_name", function(result) {
    console.log("呼び出しに成功しました: " + result);
  }, function() {
    console.log("呼び出しに失敗しました");
  });
});

</script>

<span id="status"></span>

</body>
</html>
リスト11.24 JavaScriptから内部APIサーバにリクエストを投げる

11.8.2 トークンによる利用の制限

 このブリッジ方法では、端末内にHTTPサーバを構築して、Ajaxを通じてAPIの呼び出しを受け付けます。この端末内であればどこからでもHTTPサーバに対してリクエストを投げることができるので、このままだとブリッジを提供するHTML5ハイブリッドアプリ以外のアプリからもAPI呼び出しができてしまうことになります。

 信頼できない呼び出し元をブロックするために、このブリッジではあらかじめトークンによる認証処理が行われます。APIを呼び出すための秘密のトークンを発行して、外部のアプリがAPI呼び出しをできないようにするのです。

 HTML5ハイブリッドアプリ側では、起動時にユニークなトークンをHTTPサーバとHTMLのJavaScript側の両方に配信します。HTTPサーバには、APIを呼び出すためのユニークなトークンがなければ呼び出しができないような処理を入れておきます。JavaScript側でも、配信されたトークンをパラメータに付けてHTTPリクエストを投げます。

 これにより、HTML5ハイブリッドアプリ内の信頼できるAPI呼び出し―トークンを用いたネイティブAPIの呼び出しのみが受け付けられるようになります。

11.9 まとめ

 この章では、JavaScriptとネイティブとのブリッジの仕組みについて紹介しました。これらの仕組みは、Cordovaなどの既存のハイブリッドアプリフレームワークの内部で利用されています。とはいえ、紹介した方法がそのまま用いられているわけではありません。

 今まで紹介した方法には、それぞれに欠点があります。例えば、AndroidもしくはiOSの片方のOSでしか利用できなかったり、セキュリティ上の懸念があったり、OSのバージョンへの依存があったり、ブリッジが双方向ではなかったりするのです。

 実際のブリッジは、これらの方法を組み合わせて欠点を補い合うようなかたちで提供されます。次の章では、フレームワークを用いずにハイブリッドアプリを作成するためのWebViewの使い方の基本や、WebViewの挙動を変える様々なオプションを解説します。

 以上で第11章の内容は終了です。また、転載についても今回が最終回となります。

久保田光則(くぼたみつのり)

東京都在住。アシアル株式会社に所属するUI/UXデザイナー兼ソフトウェアエンジニア。社内では,HTML5ハイブリッドアプリの開発に多数関わる。優れたデザインとエンジニアリングを両立したオーバークオリティなアプリケーションの開発を実現するために日々,頑張る。

アシアル株式会社(あしあるかぶしきがいしゃ、Asial Corporation)

PHPなどのサーバサイドの技術と,PhoneGapなどのスマートフォン関連を中心とした開発を手がける技術ベンチャー。HTML5ハイブリッドアプリをブラウザ上で開発できるMonacaや,PhoneGapの日本語情報を配信するPhoneGap Fanなどのウェブサービスを手がける。

※以下では、本稿の前後を合わせて5回分(第6回~第10回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(10)
6. Android向けのCordovaプラグインを実装する

アプリの一部をネイティブで記述するには、プラグインの実装が必要。そこでAndroid向けにCordovaプラグインを実装する方法を解説。今回で「Cordovaアプリ開発の基礎」に関する部分の転載完了。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(10)
7. JavaScriptからネイティブの機能を呼び出す方法 ― addJavascriptInterface方式(前編)

Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブとの通信の仕組みを解説する。書籍転載の7本目(「Part 2《実践編》 第11章 JavaScriptとネイティブとのブリッジ」より)。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(10)
8. addJavascriptInterface方式(後編)/ネイティブからJavaScriptへ値を渡す

Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブとの通信の仕組みを解説。書籍転載の8本目。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(10)
9. カスタムURLスキーム方式/iOSで特定のページの読み込みを制限する

JavaScriptからネイティブ側へ命令を投げる方法(Android&iOS対応)を紹介。またiOSでセキュリティのために、特定のドメインのページの読み込みを制限する方法も紹介。書籍転載の9本目。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(10)
10. 【現在、表示中】≫ JavaScriptとネイティブの通信の仕組み ― JsAlert方式/ローカルHTTPサーバ方式

ついに書籍転載最終回。Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブの通信の仕組みの解説が完結する。

サイトからのお知らせ

Twitterでつぶやこう!