イントロダクション
Cordova Pluginを実装する場合の基本事項についてまとめました。
Cordovaプラグインについての記事
- 基本事項 <- この記事
- JavaScript部分の実装
- [Native部分の実装iOS] ... TODO
- [Native部分の実装Android] ... TODO
- フックスクリプト
- [プラグインのテスト] ... TODO
必須ファイル
cordova 6.5以前は、plugin.xmlだけが必須でした。
cordova 7.0以後は、plugin.xmlのほか、package.jsonも必須となります。
ミニマムなプラグイン構成
Cordova 7.0以後を想定して、ミニマムなプラグインを構成するplugin.xmlとpackage.jsonを考えています。
ミニマムなplugin.xml
ミニマムなplugin.xmlファイルは次のようになります。ここでは、プラグイン名をhello-world-pluginとしています。
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" id="hello-world-plugin" version="0.0.1" >
<name>HelloWorldPlugin</name>
<description>HelloWorldPlugin</description>
<author>Ken Naito</author>
<license>MIT</license>
<engines>
<engine name="cordova" version=">=6.5.0" />
</engines>
</plugin>
この中で重要なのは、pluginディレクティブと、そのid属性、version属性です。これらは省略することが出来ません。
idは、英数字とハイフンで区切るのが一般的です。これは、nodeのパッケージ名と同じになるようにしているからです。
id名は、必ずしもnodeのパッケージ名と一致させる必要はありませんが、cordova 7.1以前では、nodeのパッケージ名と合っていないと正常に動作しない不具合があります。
また、多くの公開されているプラグインのプラグインidは、「cordova-plugin-」という形になっているものが多いです。cordovaで公式に公開しているプラグインもすべてこの形式になります。
version名は、semver形式で、メジャーバージョン、マイナーバージョン、パッチバージョンをピリオドでつないだものを指定して下さい。
engines
は省略することも可能ですが、基本的には記述しておいた方が良いです。特に、古いcordovaだとうまく組み込めない可能性があります。現状では、6.5や7.0以上としておけば問題ないでしょう。
name
, description
, author
, lisence
も省略することが可能ですが、一般的に何か記述しておいた方が良いでしょう。
ミニマムなpackage.json
package.jsonファイルは、cordova 7.0以後は必須となりました。最低限、nameとversionが記載されていれば利用可能です。
ミニマムなpackage.jsonファイルは次のようになります。
{
"name": "hello-world-plugin",
"version": "1.0.0"
}
先にも述べた様に、package.jsonのname
は、plugin.xmlのwidget
タグのid
属性と同じにした方が良いです。
また、version
は、plugin.xmlのversion
と同じにして下さい。
ここまでのサンプルコードのプラグインを、プロジェクトに組み込む
プラグイン開発用のディレクトリを作成し(ここでは【plugin_dir】とします)、上記のplugin.xmlとpackage.jsonを配置してください。
【plugin_dir】
┣ package.json
┗ plugin.xml
これで、ミニマムなプラグイン hello-world-pluginが出来ました。
以下のようにして、プラグインをプロジェクトに組み込んでみましょう。
$ cordova create sampleDevelopPlugin // サンプルプロジェクトの作成
$ cd sampleDevelopPlugin
$ cordova platform add android
$ cordova platform add ios
$ cordova plugin add 【plugin_dir】 // プラグインの組み込み
成功しましたでしょうか? 以下のコマンドで、正常に組み込まれたかどうかが分かります。
$ cordova plugin ls
また、次のコマンドでプラグインを解除することが出来ます。
$ cordova plugin rm hello-world-plugin
なお、このプラグインは何も機能を持っていないので、組み込んでも何もすることが出来ません。
ネイティブコードの記述
それでは、いよいよプラグインからネイティブコードを利用する方法へ進みましょう。
プラグインでネイティブコードを利用したい場合、plugin.xmlを編集し、プラットフォームごとに platform
ディレクティブで記述していく必要があります。
Androidの場合
platform
ディレクティブに、name
属性をandroid
として設定します。
例えば、最も簡単なプラグインの例として、ハローワールドプラグインでは、次のように記述しています。
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="HelloWorldPlugin">
<param name="android-package" value="com.example.plugin.HelloWorldPlugin"/>
</feature>
</config-file>
<source-file src="src/android/HelloWorldPlugin.java" target-dir="src/com/example/plugin" />
</platform>
source-file
ディレクティブで、組み込む必要のあるjavaコードを記述しています。src属性で、プラグイン内の相対アドレスで、ファイルを指定します。target-dir属性で、配置先をプロジェクトのplatforms/androidからの相対パスを指定します。(cordova-android のversion 7.0以後の場合は、platforms/android/app/src/main/javaからの相対パスに配置されます)
config-file
ディレクティブで、javascriptの cordova.exec
メソッドでの呼び出し方を指定しています。config-file
の target
属性と parent
属性は、常にこの値となります。
feature
ディレクティブで、呼び出し方を1つ指定します。feature
タグのname属性は、cordova.exec
メソッドで呼び出す場合の参照名を定義しています。
また、param
ディレクティブの value属性で、呼び出されるjavaクラスを指定します。この例では、com.example.plugin.HelloWorldPlugin
クラスが指定されています。このjavaクラスは、CordovaPlugin
クラスを継承している必要があります。param
ディレクティブのnameは、androidの場合は、固定でandroid-pacakge
とします。
なお、param
ディレクティブとして追加で次のように書くことも出来ます。
<param name="onload" value="true" />
この指定をすると、アプリ起動後にplugin (のネイティブコード)が初期化されます。この指定がないと、JavaScriptから最初の呼び出しが行われるまで、pluginの初期化が実行されません。
なお、この例で実際に呼び出されるのは、com.example.plugin.HelloWorldPlugin
の execute
メソッドとなります。
cordova.exec
による呼び出し方は
cordova.exec( success, fail, "HelloWorldPlugin", 【メソッド名】, 【パラメータ】);
となります。success
は成功コールバック、fail
は失敗コールバックです。
第三引数の"HelloWorldPlugin"は、上述の feature
タグのname属性で指定した文字列です。
第四引数のメソッド名は、Androidの場合文字列であれば何でも良いですが、iOSとの整合性を考えると、なるべくキャメルケースの文字列にした方が良いでしょう。これが、ネイティブ側のexecuteメソッドの第一引数として渡されます。
第五引数のパラメータは、かならず配列とします。一つしかパラメータがない場合、長さ1の配列とします。また、配列の要素は文字列でも数値でもJsonでも構いません。
なお、cordova.execを直接アプリケーションから呼ぶことも出来ますが、通常はそうではなく、プラグイン内のjavascriptからcordova.execを呼び出すようにします。そして、アプリケーションから呼び出すのは、プラグイン内のjavascriptだけにする方が良いです。
プラグインのjavascriptについては、JavaScript部分の実装を参照。
ネイティブコードの実装
ごく簡単に、「HelloWorldPlugin」という文字列を返すだけのプラグインとした場合、Androidのネイティブ部分の実装は次のようになります。
package com.example.plugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
public class HelloWorldPlugin extends CordovaPlugin {
@Override
public boolean execute(String action, JSONArray args,
CallbackContext callbackContext) throws JSONException {
if ("message".equals(action)) {
callbackContext.success("HelloWorld");
return true;
}
}
}
ここでは詳しくは説明しませんが、メソッド名としてmessage
を呼び出した時に、"HelloWorld"という文字列を返す処理をしています。
パラメータは何も受け付けていません。
サンプル:プラグインとしての構成
上記をまとめると、プラグインとしては次のようになります。
【plugin_dir】
┣ src
┃ ┗ android
┃ ┗ com/example/plugin/HelloCordovaPlugin.java
┣ package.json
┗ plugin.xml
サンプル:アプリケーションからの呼び出し方
プラグイン内に、呼び出し用のjavascriptを用意していないので、ここでは
deviceready後に、次のように直接cordova.exec
を使って呼び出します。
var success = function (x) { alert(x); };
var fail = function () { alert("error!"); };
cordova.exec( success, fail, "HelloWorldPlugin", "message", []);
iOSの場合
androidの場合と同様に、platformディレクティブを使い、name属性としてios
を設定します。
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="HelloWorldPlugin">
<param name="ios-package" value="CDVHelloWorldPlugin"/>
</feature>
</config-file>
<header-file src="src/ios/CDVHelloWorldPlugin.h" />
<source-file src="src/ios/CDVHelloWorldPlugin.m" />
</platform>
config-fileディレクティブもandroidの時と同様です。target属性の指定の仕方がやや異なります。iOSでは、プラットフォームのrootにconfig.xmlファイルがあるためです。
featureタグもandroidの時とほぼ同様です。valueでは、Androidではjavaなのでパッケージ名付きでクラスを指定していましたが、iOSではObjective-Cなので、クラス名のみを記述します。パッケージ名がない分、他のライブラリとファイル名が重ならないように、クラス名にプラグイン固有のプレフィックスをつけることが普通です。ここでは、CDV
をつけて、CDVHelloWorldPlugin
としました。
header-fileディレクティブと、source-fileディレクティブで、Objective-Cのヘッダファイル、ソースファイルを指定します。Androidの時と異なり、target-dirを指定することは出来ません。
cordova.exec
での呼び出し方は、androidと共通です。
cordova.exec( success, fail, "HelloWorldPlugin", 【メソッド名】, 【パラメータ】);
このコードで呼び出されるネイティブコードは、CDVHelloWorldPluginクラスの、【メソッド名】メソッドです。すなわち、cordova.execから直接Objective-Cのメソッドが呼びだされることになります。なお、CDVHelloWorldPluginクラスは、
CDVPlugin
クラスを継承している必要があります。
Androidの時と同様ですが、cordova.execを直接アプリケーションから呼ぶことも出来ますが、通常はそうではなく、プラグイン内のjavascriptからcordova.execを呼び出すようにします。そして、アプリケーションから呼び出すのは、プラグイン内のjavascriptだけにする方が良いです。
プラグインのjavascriptについては、JavaScript部分の実装を参照。
Windowsの場合
Windowsの場合はちょっと特殊です。そもそもWindowsの場合は、javascriptからデバイスの制御が全て行えるため、「ネイティブ」を呼び出す必要がありません。しかし、Android/iOSと呼び出し方を統一しておいた方が、アプリの実装を共通化出来るため好ましいです。そこで、Windowsの場合、デバイスを制御するようなjsコードをProxy形式(cordovaのproxyの仕様に従った形式のjavascript)で記述し、次のようにjs-moduleタグで指定するのが普通です。
<platform name="windows">
<js-module src="src/windows/HelloWorldPluginProxy.js" name="HelloWorldPlugin">
<merges target="" />
</js-module>
</platform>
そして、HelloWorldPluginProxy.js内部で
require("cordova/exec/proxy").add("HelloWorldPlugin", module.exports);
のように定義することで、Android/iOSと同様に cordova.exec
メソッドで呼び出すことが出来るようになります。
plugin.xmlファイル、package.jsonファイルの補足事項
dependency
プラグインが別のプラグインに依存している場合、plugin.xmlに次のように記述することで、自動的に依存先プラグインをインストールすることが出来る様になります。
<dependency id="【依存先プラグインのプラグインID】" version="【バージョン】" />
例えば、phonegap-plugin-pushの2.1.3に依存するのであれば、次の様になります。
<dependency id="phonegap-plugin-push" version="2.1.3" />
plugin.xmlにdependencyが記述してある場合、package.jsonにもdependenciesを記載しておいた方が良いでしょう。
例: cordova-plugin-media
の場合 https://github.com/apache/cordova-plugin-media
cordova-plugin-file
に依存しています。
<dependency id="cordova-plugin-file" version="^6.0.0" />