kazu22002の技術覚書

PHPer, Golang, AWS エンジニアの日々

【ソースコード・リーディング】cordova-device-plugin (4)

続きです。

device.js

device.js 37

function Device() {
    this.available = false;
    this.platform = null;
    this.version = null;
    this.uuid = null;
    this.cordova = null;
    this.model = null;
    this.manufacturer = null;

    var me = this;

    channel.onCordovaReady.subscribe(function() {
        me.getInfo(function(info) {
            //ignoring info.cordova returning from native, we should use value from cordova.version defined in cordova.js
            //TODO: CB-5105 native implementations should not return info.cordova
            var buildLabel = cordova.version;
            me.available = true;
            me.platform = info.platform;
            me.version = info.version;
            me.uuid = info.uuid;
            me.cordova = buildLabel;
            me.model = info.model;
            me.manufacturer = info.manufacturer || 'unknown';
            channel.onCordovaInfoReady.fire();
        },function(e) {
            me.available = false;
            utils.alert("[ERROR] Error initializing Cordova: " + e);
        });
    });
}

deviceプラグイン側の根幹部分ですね。
deviceクラスを作成しているといっていいと思います。

最初に、メンバ変数を作成ですかね。
初期化、いいことです。しっかりと初期化はするべきです。

エラーの可能性が減りますからね。

このクラス自信をmeとして使うのは気になりますね。
どうしてこういう書き方をするんですかね??

短くしたいから??意図がわかりませんが、関数の中で使用しているのはmeですね。

関数の中で使用している。ということは、そういうことかな?

Device()関数自身という意味合いで「me」変数を作成して、channel.onCordovaReady.subscribeの中では「me」を使用するのか。

channel.onCordovaReady.subscribe内でthisを使用すると確かに呼び出しの関数自体だから「Device」にはならないのか。

なるほどね。そういうことをしてもいいのか。いいテクニックだ。

getInfo関数の定義はコードのあとでやっていますね。
getInfo関数自体はinfoの中身が来ることを前提で、データをメンバ変数に埋めていく感じですね。

ここでコードとして気になるのは「channel.onCordovaReady.subscribe」の部分ですかね。

channelはrequireで読み込んでいるコードのため、cordova.js側に記述されています。

cordova.js 610

// file: src/common/channel.js
define("cordova/channel", function(require, exports, module) {

channel自体の機能はちょっとソース自体を詳しく読まないとわからない部分ですが、 端末側のイベント系を元に呼び出される内容をラップしているみたいです。

  • onCordovaReadyはCordova Javascript Objectが作成できた時
  • onDeviceReadyはCordovaの準備ができた時
  • onResumeは端末側のライフサイクルのstart/resume の時
  • onPauseは端末側のライフライクルのpause の時
  • onDestoryはアプリが終了される時(window.onloadまでいってないとよばれないっぽい)

ここで使用されているのはonCordovaReadyですので、cordova objectができた時に、実行しますね。

ということは、deviceの変数呼び出しを行っていいのは、cordova objectができたあとで、かつ、この処理がされてからになりますね。

まぁ、onDeviceReadyで呼び出せば安心ではありそうです。

device.js 74

Device.prototype.getInfo = function(successCallback, errorCallback) {
    argscheck.checkArgs('fF', 'Device.getInfo', arguments);
    exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
};

module.exports = new Device();

getInfoの定義をしています。prototypeのことはいまいち理解出来ていない。

JavaScriptの「プロトタイプ入門」 - Qiita

ここでprototypeの説明をしてくれていますね。googleでトップに出たから言いたいことはわかりやすい。

でも、個人的にはどうつかえばいいかあまり理解できていない。

じゃあ、最初からprototypeって書いて関数つくればいいじゃん。とか思ってしまう。
たしかにメモリとかを気にするのはいいことだけど、個人的にまだ読めないのがきついんです。
現状では、関数を定義したんだな。ぐらいの印象で進んでいきます。

個人的に実践的に書けるようになるには、もう少し理解する必要がありそうです。

ここでgetInfoの定義を書いていて、successCallbackとerrorCallbackを引数として渡してくれれば、動くよ。ってことね。

もう宣言の定義はできているから、あとはDeviceをインスタンス(new)化してしまえば、動作してくれるわけですね。

ここで重要なのは「exec」部分ですね。

    exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);

この部分でNative側への処理を実行しています。

読めることとして引数の4番目が関数としてNative側に用意されていると思われます。

cordova.js 852

// file: src/android/exec.js
define("cordova/exec", function(require, exports, module) {

execのモジュール。このあたりからNative側を呼ぶための処理を書いているわけですね。

かなりcordova自体の機能の根幹だと思いますので、時間を使って読みたいところです。

なので、現状としては関数を呼び出しているのだと理解して進みます。

じゃあ、この関数はどこに記述されているのか?

plugins/org.apache.cordova.device/src/android/Device.java 69

    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if (action.equals("getDeviceInfo")) {
            JSONObject r = new JSONObject();
            r.put("uuid", Device.uuid);
            r.put("version", this.getOSVersion());
            r.put("platform", this.getPlatform());
            r.put("model", this.getModel());
            r.put("manufacturer", this.getManufacturer());
            callbackContext.success(r);
        }
        else {
            return false;
        }
        return true;
    }

この部分でやっていますね。

actionの部分にさきほどのexecの4番目の引数が入ってきそうです。

関数があると思っていましたが、cordovaが用意している関数が吸収してくれているみたいですね。

たしかにNativeとの連携はやり方がありますが、cordovaでやるならこういうやり方でやれるみたいですね。

そして返り値として使用するために渡す値は「callbackContext.success(r)」の部分でしょうね。

JSON形式で返しています。これがgetInfo関数のinfoに当たるわけですね。

使うだけならここまででOK

使いたい部分の値で何がつかわれているとか、処理がどうなっているとかをしるならこの辺りでの読み込みでいいかと思います。

個人的にはまだ内部処理をみたいですが、ちょっと時間が掛かりそうですので、どこかでできればいいかと。

じつはdevice.jsの書き方自体は、cordova/utils とかと似ています。
moduleとしての書き方ということでかね。
たしかにクラスとして書いていくのは使いやすいという印象はあるので、この書き方はとても興味を持っています。

ただ読みづらくなるのは否定できないですね。
JSでのクラス化を理解している人がそこまで多くない印象なので、このあたりが僕の限界なのでしょうか。

JSはただでさえ、ルールを決めないとどこにでもいってしまう言語な気はしているので、クラス化を推奨できるところまで理解したいです。

時間があればさらに読みます。

個人的課題・・・

  • cordova.jsのexec
  • Android側のexec処理
  • cordova_plugin.jsの処理
  • prototypeの理解

(真夜中の二時に書くもんじゃないですね。理解しないといけないからきつい。ふらふらです。。。)