PhoneGap(Cordova)でネイティブコード(Java)を呼び出す

慣れない Java + Eclipse つらい

ぼくがんばった

おおざっぱな概念

だいたい以下の流れになる。公式ドキュメントと公式プラグインのリポジトリは以下
Apache Cordova API Documentation
phonegap/phonegap-plugins · GitHub

  • cordova.js でネイティブコードを呼び出す処理をかく(この場合 Java)
  • cordova.exec を動かす
  • 実際にネイティブコードを呼び出す

で、もうちょっと言えば

  • cordova.js でネイティブコードを呼び出す処理をかく(この場合 Java)
    • service でプラグインとして文字列を渡す(keyみたいなもの?)
    • プラグイン追加の初期化処理をする
    • res/xml/config.xml に plugins を追加
  • 実際に cordova.exec を動かす
    • 成功コールバック関数、失敗コールバック関数を定義する
  • 実際にネイティブコードを呼び出す

具体的には以下のリポジトリのコードが参考になった
phonegap-plugins/Android/SMSPlugin at master · phonegap/phonegap-plugins · GitHub

注意

Cordova はバージョンアップが激しいという話で、この情報もすぐに古くなる可能性がある。いろいろ情報をあさってたが細かいところで動かないものとかあったりしてハマった。

具体的にみていこう

まずはプラグインの呼び出し側の根幹となる echo.js を定義

// 関数オブジェクトに中身をつめる
var EchoPlugin = function () {};
// EchoPlugin オブジェクトに send という関数を生やして、そこに 1. 渡す引数 2. 成功コールバック 3. 失敗コールバックを定義する
// なんでこんな書き方をして中身がどうなっているかというのは、よくわからない
// ちなみに引数を2つ以上とりたかったら content, content2...などと引数を増やし、 exec でも [content, content2...]などと増やしていけばよい。デバッグしてる感じ、12個までとれる?
EchoPlugin.prototype.send = function (content, successCallback, failureCallback) {
    return cordova.exec(successCallback, failureCallback, 'EchoPlugin', "EchoContent", [content]);
};

// window.plugins 以下に初期化処理しておく
if(!window.plugins) {
    window.plugins = {};
}
if (!window.plugins.echo) {
    window.plugins.echo = new EchoPlugin();
}

そのあと res/xml/config.xml にプラグインを追加。plugin.xmlじゃないよ!(これもハマった)

<plugin name="EchoPlugin" value="jp.aproud.hogeproject.EchoPlugin" />

で、実際に動かす EchoPlugin.java を定義

package jp.aproud.hogeproject;

import org.apache.cordova.api.Plugin;
import org.apache.cordova.api.PluginResult;
import org.apache.cordova.api.PluginResult.Status;
import org.json.JSONArray;
import org.json.JSONException;

// このクラス定義は公式ドキュメントなど参考に、プラグイン作成時はこういう書き方をする
public class EchoPlugin extends Plugin {
  public final String ACTION_ECHO_CONTENT = "EchoContent";

// ここらへんもおきまりなかんじ
  @Override
  public PluginResult execute(String action, JSONArray args, String callbackId) {
    PluginResult result = new PluginResult(Status.INVALID_ACTION);

    if (action.equals(ACTION_ECHO_CONTENT)) {
      try {
        String content = args.getString(0);
// 今回は引数を println するだけのコードを書いた
        EchoConent(content);
        result = new PluginResult(Status.OK);
      } catch (JSONException e) {
        result = new PluginResult(Status.JSON_EXCEPTION, e.getMessage());
      }
    }
    return result;
  }

  private void EchoConent(String content) {
    System.out.println(content);
  }
}

あとは発火させるだけ

// 今回はてきとうな h2 をとってきた。引数を増やす場合は arg1 arg2... としていけばよい
window.plugins.echo.send($("h2").text(), function() {
    alert('success')
}, function(e) {
    alert('error' + e)
});

公式ドキュメントの補足的ななにか

わかりやすいようで正直微妙にわかりづらかったので自分なりに解釈する

cordova.exec(function(winParam) {}, function(error) {}, "service",
             "action", ["firstArgument", "secondArgument", 42,
             false]);

The parameters explained in more detail:
1. function(winParam) {} - Success function callback. Assuming your exec call completes successfully, this function will be invoked (optionally with any parameters you pass back to it)
2. function(error) {} - Error function callback. If the operation does not complete successfully, this function will be invoked (optionally with an error parameter)
3. "service" - The service name to call into on the native side. This will be mapped to a native class. More on this in the native guides below
4. "action" - The action name to call into. This is picked up by the native class receiving the exec call, and, depending on the platform, essentially maps to a class's method. For more detail please check out the native guides located at the end of this article.
5. [/* arguments */] - Arguments to get passed into the native environment

成功コールバック、失敗コールバックはまあわかる。それぞれ成功、失敗したときに呼び出される関数だ。で、最後の配列 [] の部分は arguments とあるとおり、ネイティブコードにわたす引数だ。で、よくわからなかったのは service ってのと action 。日本語訳を呼んで実際に動かしてやっとある程度掴んだ感じだと*2、service ってのはネイティブコードに「呼び出すための」名前。action というのはネイティブコードで「つかう」ための名前。
公式ドキュメントだと Echo echo となっててなんだかよくわからなかったけど、Echo という外部ネイティブコードを呼び出すための名前と、 echo という String action で引数として使われるものの違いがよく見えなくて混乱してた。

ね、簡単でしょ?

簡単じゃなかった。ぼくはつかれたょ……

*1:こんな簡単なところでハマってたが、知らないものは知らなかったのだ

*2:個人差があります