はじめに
今回は、Eclipseを使って、Androidデバイスを対象とするPhoneGap(Apache Cordovaとも呼ばれます)アプリケーション用のネイティブプラグインの使い方について解説します。
PhoneGapを初めて使用する場合、またはPhoneGapの基本事項をもう一度確認する必要がある場合は、続行する前に『EclipseとPhoneGapでAndroidアプリケーションを開発する』をお読みください。
「Cordova」および「PhoneGap」という用語は、この記事ではほぼ同じ意味で使用されます。いずれも、ネイティブにインストールされるモバイルアプリケーションをHTMLおよびJavaScriptで作成するための、オープンソースの同一のアプリケーションプラットフォームを指します。
PhoneGapのコードベースは、Apache Software Foundationの下でCordovaという名前のオープンソースになりました。アドビでは、現在でもPhoneGapという名前で配布しています。詳しくは、Brian Lerouxのブログ『PhoneGap, Cordova, and what's in a name?』を参照してください。このブログによると、「現時点で、唯一の相違点はダウンロードパッケージの名称であり、しばらくはこのままの状態が続く」とあります。
PhoneGapでは、ネイティブにインストールされるモバイルアプリケーションのユーザーインターフェイスを、Webテクノロジーを利用して構築できるだけでなく、デバイスのネイティブ機能を操作するためのJavaScriptベースのAPIも用意されています。デフォルトでは、PhoneGapからデバイスのカメラ、加速度センサー、ファイルシステム、GPS位置情報、メディア再生などの機能にアクセスできます。ただし、一部のネイティブAPIについては、JavaScriptアプリケーション内で利用することはできません。PhoneGapでデフォルトの機能セット以上のことをしたい場合は、PhoneGapネイティブプラグインモデルを使用して、核となるPhoneGap APIの機能を拡張できます。
デスクトップブラウザーのプラグインとは異なり、PhoneGapのネイティブプラグインでは、カスタムコードを組み込むことで、PhoneGapアプリケーションフレームワークの守備範囲を広げることができます。PhoneGapのネイティブプラグインを使用すると、ネイティブコードでまったく新しいカスタム機能を作成し、ネイティブからJavaScriptへのブリッジを介して、その機能をPhoneGapアプリケーションに公開できます。つまり、あらゆるネイティブライブラリまたはフレームワークをJavaScriptベースのPhoneGapアプリケーションで利用できるということです。
PhoneGapネイティブプラグインの構造を理解する
PhoneGapネイティブプラグインの記述を始める前に、PhoneGapアプリケーションのコンテナによってJavaScriptベースのアプリケーションからオペレーティングシステムのネイティブ機能をどのように利用できるようになるのか、その仕組みを理解しましょう。
すべてのCordova APIは、関連する2つの部分で構成されています。アプリケーションからアクセスできるJavaScriptベースのインターフェイスと、ネイティブコードで操作を実行するための対応するネイティブクラスです。通常、JavaScriptクラスとネイティブクラスのAPIは、相互に対応しているため、理解するのは簡単です。JavaScriptクラスは、Cordova.exec()関数を使用して、ネイティブコードを呼び出します。Cordova.execを呼び出すときに、ネイティブクラスの名前およびネイティブ関数名への参照に加え、結果ハンドラー関数、エラーハンドラー関数、ネイティブコードに渡されるパラメーターの配列を渡すことができます。JavaScriptからネイティブへの通信はCordovaが管理するため、デベロッパーはアプリケーションの作成に集中できます。
PhoneGapネイティブプラグインについて詳しくは、Cordova wikiで提供されている、核となるAPIのソースコードを参照してください。PhoneGapのフレームワーク全体は、ここに掲載されているパラダイムに基づいて構築されています。
最初のプラグインを作成する
最初のPhoneGapネイティブプラグインの作成を開始するには、『EclipseとPhoneGapでAndroidアプリケーションを開発する』で説明されている手順に従って、新規PhoneGapプロジェクトを作成する必要があります。私のプロジェクトには、「MyFirstPhoneGapNativePlugin」という名前を付けました。
JavaScriptクラス
Hello Eclipseプロジェクトをセットアップしたら、ネイティブプラグイン用のJavaScriptインターフェイスを作成できます。ネイティブコードで提供されるロジックに対応するクラスと関数を作成します。wwwフォルダーの下に、以下のような単純なJavaScriptクラスが含まれるHelloPlugin.jsという名前のJavaScriptファイルを作成します。
var HelloPlugin = { callNativeFunction: function (success, fail, resultType) { return cordova.exec( success, fail, "com.tricedesigns.HelloPlugin", "nativeAction", [resultType]); } };
HelloPluginクラスにはcallNativeFunctionという名前の関数が1つあり、この関数は、成功のコールバック関数、失敗のコールバック関数、resultType文字列パラメーターを取ります。このcallNativeFunction関数でcordova.exec関数をラップして、実際のネイティブコードを呼び出します。このクラス内には、その他のJavaScriptはありませんが、必要に応じて、ここにJavaScriptコードを追加できます。cordova.execが呼び出されるとき、5つのパラメーターが渡されます。
- 成功のコールバック関数への参照(ネイティブコードレイヤーからの成功の応答時に呼び出される関数)
- 失敗のコールバック関数(ネイティブレイヤーからの失敗の応答時に呼び出される関数)
- ネイティブコードクラスへの文字列参照(後ほど詳しく説明します)
- 呼び出されるアクションへの文字列参照(iOSとその他のプラットフォームで異なり、Android用のアプリケーションを構築する場合、呼び出される関数の名前ではなく、アクションへの参照となる)
- ネイティブコードに渡されるパラメーターの配列
JavaScriptとネイティブコードレイヤー間でのコード実行は、同時進行ではありません。したがって、PhoneGapネイティブプラグインを開発するときには、コールバック関数と非同期のコーディング手法を用いる必要があります。
ネイティブクラス
ネイティブコードレイヤーを作成するには、核となるCordova APIのorg.apache.cordova.api.Pluginクラスを拡張する新しいJavaクラスを作成することから始めます。Eclipseで、File(ファイル)/New(新規)/Class(クラス)を選択します。
次に、New Java Class(新規Javaクラス)ウィザードの手順に従って、「org.apache.cordova.api.Plugin」クラスを拡張する「HelloPlugin」という名前のクラスを作成します。
org.apache.cordova.api.Pluginクラスは、Cordovaクラスで拡張する際に必ず元となる親クラスです。org.apache.cordova.api.Pluginクラスには、PhoneGap APIによるネイティブからJavaScriptへの通信に必要なロジックがすべてカプセル化されています。
cordova.exec JavaScript関数が呼び出されると、対応するネイティブプラグインクラスの「execute」関数が呼び出されます。PhoneGapアプリケーションでHTMLコンテンツをレンダリングするときに使用されるAndroid WebViewでは、WebView APIを使用して、ネイティブコードとJavaScriptクラス間の通信を有効にします。
org.apache.cordova.api.Pluginを拡張するネイティブJavaクラスを作成したら、後は、「execute」メソッドを上書きして、ネイティブ機能を作成するだけです。このメソッドのパラメーターは、文字列「action」、JavaScriptからネイティブコードに渡されるパラメーターのJSONArray配列、現在のネイティブメソッドの呼び出しに対する固有の参照であるcallbackIdです。ネイティブクラスに渡されるアクションパラメーターにかかわらず、「execute」メソッドは常に呼び出されます。JavaScriptレイヤーからネイティブレイヤーに渡されるアクションを評価し、結果に従って応答するかどうかは、デベロッパーが判断します。
以下に示すのは、org.apacha.cordova.api.Pluginを拡張するHelloPluginクラスです。
Javaクラス、com.android.Logを必ず含めてください。これが含まれていないと、Eclipseプロジェクトでコンパイルエラーが発生します。
public class HelloPlugin extends Plugin { public static final String NATIVE_ACTION_STRING="nativeAction"; public static final String SUCCESS_PARAMETER="success"; @Override public PluginResult execute(String action, JSONArray data, String callbackId) { Log.d("HelloPlugin", "Hello, this is a native function called from PhoneGap/Cordova!"); //only perform the action if it is the one that should be invoked if (NATIVE_ACTION_STRING.equals(action)) { String resultType = null; try { resultType = data.getString(0); } catch (Exception ex) { Log.d("HelloPlugin", ex.toString()); } if (resultType.equals(SUCCESS_PARAMETER)) { return new PluginResult(PluginResult.Status.OK, "Yay, Success!!!"); } else { return new PluginResult(PluginResult.Status.ERROR, "Oops, Error :("); } } return null; } }
今回のHelloPluginクラスでは、executeメソッドを拡張しましたが、ここで最初に行われるのは、ネイティブコードが実際に実行されたことを確認できるように、デバッグメッセージを記述することです。次に、プラグインのコードは、上記のNATIVE_ACTION_STRINGにあるアクション「nativeAction」をチェックします。ネイティブコードは、アクションパラメーターの値が、実際に「nativeAction」の値と一致する場合にのみ応答します。このテクニックは、ネイティブコードの誤用を防ぐために使用されます。
この例では基本的用法を示していますが、実際のシナリオでもこのテクニックを使用する必要があります。また、1つのプラグインクラスで複数のアクションを可能にする場合も、このテクニックが必要になります。
ネイティブアクションのチェックが終わると、JSONArrayからresultType文字列が評価され、適切なPluginResult応答が返されます。resultTypeパラメーターが「success」の場合、ネイティブのPluginResultクラスのインスタンスが、PluginResult.Status.OKのステータスで返されます。その他の値の場合、PluginResultはPluginResult.Status.Errorのステータスで返されます。
executeメソッドの結果値を返すときは、org.apache.cordova.api.NativePluginのインスタンスを返す必要があります。NativePluginインスタンスで返されるステータスとメッセージの値が評価され、ネイティブ機能を呼び出したJavaScriptコードから適切な成功/失敗のコールバックハンドラーが呼び出されます。
プラグインを呼び出す
プラグインが完成したら、PhoneGapアプリケーションから呼び出すことができます。
まず、新しいプラグインのJavaScriptインターフェイスクラス(HelloPlugin.js)への参照を追加する必要があります。index.htmlファイルに新しい<script>タグを追加します。
<script type="text/javascript" charset="utf-8" src="HelloPlugin.js"></script>
また、onDeviceReady()関数の後に、ネイティブプラグインを呼び出してプラグインの結果を処理するためのJavaScriptを追加します。以下のように、callNativePlugin、nativePluginResultHandler、andnativePluginErrorHandlerという名前のJavaScript関数を追加します。
function callNativePlugin( returnSuccess ) { HelloPlugin.callNativeFunction( nativePluginResultHandler, nativePluginErrorHandler, returnSuccess ); } function nativePluginResultHandler (result) { alert("SUCCESS: \r\n"+result ); } function nativePluginErrorHandler (error) { alert("ERROR: \r\n"+error ); }
callNativePluginは、単にネイティブプラグインクラスのJavaScriptインターフェイスを呼び出すための関数です。この関数がcallNativeFunctionメソッドを呼び出すときに、ネイティブコードレイヤーから受け取る成功および失敗ステータスのコールバック関数が渡されます。ネイティブコードレイヤーから成功のコールバックが返された場合は、nativePluginResultHandler関数が呼び出され、ネイティブコードレイヤーから失敗のコールバックが返された場合は、nativePluginErrorHandler関数が呼び出されます。
次に、下記のコードに示すように、プラグインを呼び出すJavaScriptボタンを2つ追加します。
<body onload="onBodyLoad()"> <h1>Hey, it's Cordova!</h1> <button onclick="callNativePlugin('success');">Click to invoke the Native Plugin with an SUCCESS!</button> <button onclick="callNativePlugin('error');">Click to invoke the Native Plugin with anERROR!</button> </body>
最初のボタンがクリックされると、callNativeFunctionメソッドが「success」のパラーメーターと共に呼び出されます。PhoneGapは次にネイティブコードを実行し、JavaScriptレイヤーで成功のコールバックを呼び出します(thenativePluginResultHandler関数を呼び出します)。
2番目のボタンをクリックすると、callNativeFunctionメソッドが「error」のパラーメーターと共に呼び出されます。PhoneGapは次にネイティブコードを実行し、JavaScriptレイヤーで失敗のコールバックを呼び出します(thenativePluginErrorHandler関数を呼び出します)。
ネイティブコードクラスをマッピングする
この時点でほぼすべての準備が完了していますが、JavaScriptからネイティブコードを呼び出せるようにするには、実行する手順がもう1つあります。
Cordovaがネイティブコードクラスを識別できるように、マッピングを追加する必要があります。cordova.execを呼び出すときにネイティブクラスを識別するために使用した文字列参照を覚えていますか。その文字列を、Eclipseプロジェクトのres/xml/plugins.xmlファイルに含まれる実際のクラスインスタンスにマッピングする必要があります。res/xml/plugins.xmlファイルには、JavaScriptで使用できるCordovaネイティブAPIの設定情報がすべて含まれています。
Eclipseで、テキストエディタービューを使用してres/xml/plugins.xmlファイルを開きます。次に、作成した新しいネイティブプラグインの新しい<plugin>XMLノードエントリを追加します。<plugin>XMLノードには、nameとvalueの2種類の属性が必要です。
「name」属性は、cordova.exec JavaScript関数がネイティブコードを識別するために使用する値です。「ame」属性を使用して、「value」属性で識別されたネイティブクラスにマッピングします。plugins.xmlファイルを詳細に確認すると、PhoneGapのJavaScript APIで使用される「Geolocation」という名前が、ネイティブクラスorg.apacha.cordova.GeoBrokerと実際に対応していることがわかります。
新しい<plugin>xmlノードで、名前「com.tricedesigns.HelloPlugin」と値「com.tricedesigns.HelloPlugin」(図3を参照)を指定し、com.tricedesignsを自分の会社のIDに置き換えます。これは、cordova.execを呼び出すときに3番目のパラメーターでネイティブクラスを識別するために使用された文字列参照で、呼び出されると、com.tricedesigns.HelloPluginネイティブクラスにマップされます。
これで、アプリケーションを起動してテストできます。
アプリケーションを起動するには、Eclipseプロジェクトを右クリックし、Run As(実行)/Android Application(Androidアプリケーション)を選択します。
Androidエミュレーター(または接続したデバイス)でアプリケーションを起動すると、2つのボタンを持つ単純なインターフェイスが表示されます(図5を参照)。
いずれかのボタンをクリックしてネイティブプラグインのネイティブコードを呼び出すと、成功または失敗のコールバック時にJavaScriptによってアラートメッセージが表示されます。
ネイティブコードが呼び出されるときに、AndroidデバッグLogCatウィンドウで出力を確認することもできます。この出力は、ネイティブプラグインのLog.dメソッド呼び出しの出力を反映しています(図6を参照)。
次のステップ
Androidデバイス用PhoneGapアプリケーションのネイティブプラグインを作成する方法は理解していただけたでしょうか。このテクニックを使用して、核となるPhoneGap SDKで公開されていないAndroidフレームワークにアクセスできます(android.net、android.bluetoothおよびAndroid Development Kitで公開されているすべてのAndroid APIを含みますが、これらに限定されません)。また、オープンソースコミュニティで開発された数々のGitHubのPhoneGapネイティブプラグインも参照してください。
また、Adobe Developer ConnectionにPhoneGapデベロッパーセンターも開設されています。PhoneGap関連の記事はこちらで随時紹介していく予定ですので、ぜひご参照ください。
この作品はCreative Commons Attribution-Noncommercial-Share Alike 3.0 Unported Licenseに基づき使用が許可されます。この作品に含まれるサンプルコードに関して、このライセンスの範囲を超えた使用の許可については、アドビのWebサイトを参照してください。