`
sd6733531
  • 浏览: 65699 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

android phonegap源码详解(二)

阅读更多

传送门:

PhoneGap源码详解一

PhoneGap源码详解二

PhoneGap源码详解三

 

 

一、 Javascript 的源码结构

提醒一下大家 ,PhoneGap 的作者已经将 PhoneGap 的源码委托给了 Apache 基金会。 PhoneGap 的开源版本称为 cordova

PhoneGap 之于 Cordova ,正如 OpenJDK 之于 JDK 。两者基本上是差不多的。

 

cordova.android.js 是一个 build 版本。本人对其做了反 build 工作 ,cordova.android.js 展开的源码目录结构便如上所示。

从命名上其实已经可以看出 ,lib/android 属于 android 平台的专用库。其余平台基本上是 lib/windowsphone 或者 lib/ios 。而除 lib 下的 exec.js platform.js 这两个是固定的。但是不同平台版本其实现是不一样的。除 lib 包外,其余所有的 js 文件都是跨平台通用的。 ( 即使有差异应该也不大 )


简单介绍一下各个目录和一些关键组件 :

 

cordova.js:拦截DOM,Window事件,加入自定义cordova事件,管理回调Javascript。

scripts/require.js:PhoneGap中模块化机制的基础框架。简单但是也不简单!

scripts/bootstrap.js:负责cordova的启动。

common/channel.js:PhoneGap中实现事件监听的基础。

common/builder.js:具备定制化构造模块的能力。

common/plugin:如名字所示。这里放置所有平台通用的插件接口。

lib/exec.js:于上一篇解析中提到。是Javascript调用Native的入口。

lib/platform.js:与平台实现有关的初始化。

lib/android/plugin:与android平台紧密相关的插件。

 

 

二、 浅析 PhoneGap 中的模块化机制

也许是因为本人见过的 Javascript 代码太少,见到 PhoneGap 的模块化机制后便觉得非常的有趣和前卫。 Pascal 的作者沃斯曾写过一门叫做 module 的语言,其在语言级别做了模块化机制。我不知道 PhoneGap 模块化的思路是否也受 ; 此影响。

废话不多说了,从 require.js 开始看起吧。它是模块化的基础。

 

var require,

    define;

 

(function () {

    var modules = {};

 

    function build(module) {

        var factory = module.factory;

        module.exports = {};

        delete module.factory;

        factory(require, module.exports, module);

        return module.exports;

    }

 

    require = function (id) {

        if (!modules[id]) {

            throw "module " + id + " not found";

        }

        return modules[id].factory ? build(modules[id]) : modules[id].exports;

    };

 

    define = function (id, factory) {

        if (modules[id]) {

            throw "module " + id + " already defined";

        }

 

        modules[id] = {

            id: id,

            factory: factory

        };

    };

 

    define.remove = function (id) {

        delete modules[id];

    };

 

})();

 

//Export for use in node

if (typeof module === "object" && typeof require === "function") {

    module.exports.require = require;

    module.exports.define = define;

}

 

 

代码行数的确非常短。其定义了 require define 两个函数。首先 define 函数用于声明一个模块。其中 id 表示模块名称,这必须是唯一的,而 factory 便是构造模块的工厂方法。 require 函数使用懒加载的方式获得已 define 过的对应 id 的模块。

来看一个使用其的简单示例吧 :

 

define("cordova/plugin/android/app", function(require, exports, module) {

var exec = require('cordova/exec');

 

module.exports = {

  /**

   * Clear the resource cache.

   */

  clearCache:function() {

    exec(null, null, "App", "clearCache", []);

  },

 

  /**

   * Load the url into the webview or into new browser instance.

   *

   * @param url           The URL to load

   * @param props         Properties that can be passed in to the activity:

   *      wait: int                           => wait msec before loading URL

   *      loadingDialog: "Title,Message"      => display a native loading dialog

   *      loadUrlTimeoutValue: int            => time in msec to wait before triggering a timeout error

   *      clearHistory: boolean              => clear webview history (default=false)

   *      openExternal: boolean              => open in a new browser (default=false)

   *

   * Example:

   *      navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});

   */

  loadUrl:function(url, props) {

    exec(null, null, "App", "loadUrl", [url, props]);

  },

 

  /**

   * Cancel loadUrl that is waiting to be loaded.

   */

  cancelLoadUrl:function() {

    exec(null, null, "App", "cancelLoadUrl", []);

  },

 

  /**

   * Clear web history in this web view.

   * Instead of BACK button loading the previous web page, it will exit the app.

   */

  clearHistory:function() {

    exec(null, null, "App", "clearHistory", []);

  },

 

  /**

   * Go to previous page displayed.

   * This is the same as pressing the backbutton on Android device.

   */

  backHistory:function() {

    exec(null, null, "App", "backHistory", []);

  },

 

  /**

   * Override the default behavior of the Android back button.

   * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.

   *

   * Note: The user should not have to call this method.  Instead, when the user

   *       registers for the "backbutton" event, this is automatically done.

   *

   * @param override        T=override, F=cancel override

   */

  overrideBackbutton:function(override) {

    exec(null, null, "App", "overrideBackbutton", [override]);

  },

 

  /**

   * Exit and terminate the application.

   */

  exitApp:function() {

    return exec(null, null, "App", "exitApp", []);

  }

};

});

 

 

这是 phonegap 模块化编程的典型写法。首先在头部定义依赖的模块组件 , 再次通过设置 module.exports 向外部暴露出对应的方法。

         由于 Javascript 中在语法级别没有私有访问符。因此往往解决之道是: a. 模仿 C 风格,命名使用 _ 开头的一律表示是私有 ;b. 新建一个 private 对象 , 将其私有方法放置其中 , 起到命名空间的作用 ;c. 将私有部分用 function{} 套住 , return 返回公开部分。这种做法充分发挥了 javascript 闭包的优势,但是可读性比较差。

         phoneGap module 机制使用了第三种做法。但是通过将构造和依赖分离开来,使得可读性大大增加。代码清晰好懂,依赖性也一目了然。

         也许有朋友要问。这种模块机制碰到循环依赖的情况怎么办?例如 A define 阶段 requireB B define 阶段又 requireA 。很遗憾,这种循环依赖的情况 依照 phoneGap 的模块化思路是无法实现的。因此对于关键组件的编制 ,phoneGap 总会小心翼翼地处理其依赖顺序。

         通过阅读源码,发现大致的模块依赖顺序是这样子的 ( 被依赖模块到依赖模块 ):utils.js->channel.js|builder.js->cordova.js->exec.js|polling.js->callback.js->platform.js->bootstrap.js

三、 PhoneGap 中的事件处理机制

common/channel.js PhoneGap 事件处理机制的基础。每个事件类型,都会包装成一个 Channel 对象。

既然是事件,那么就得支持基础的观察者模式吧? Channel prototype 定义了事件的一些关键方法。 subscribe 用于注册一个监听器,并给监听器一个 guid guid 类似 cordova.js 中的 callbackId ,只是一个流水标识。但 Channel 中的 guid 稍有些不同,指定确切的 guid 可以对监听器做覆盖操作。

utils.close 是个很有趣的方法。 Javascript 中的调用不当引起 this 不对,这是新手常见的错误。常见的做法会通过封装 apply delegate 。而 close 这个方法是绝了 , 它通过闭包包装了一个指向确定 this ,调用确定 function, 使用确定实参的 final 函数。不管在什么样的环境下调用 , 这个方法总能正确执行。

subscribeOnce 类似于 YUI 或者 jquery 中的 one 。只会收到一次监听。 ( 若事件已经触发过,则在注册阶段立即回调监听 )

unsubscribe fire 分别用来注销监听器和触发事件。触发事件将会引起监听器的广播操作。可选的 fireArgs 用于保证 subscribeOnce 在事件已触发的情况下能获得正确的广播参数。

Channel 本身还有一个监听注册 / 注销的事件拦截。分别是 onSubscribe onUnSubscribe 。在 common\plugin\battery.js 中,我们可以看到。 battery.js 便是利用这个注册监听回调,来对 Plugin 服务做懒加载和卸载工作。

作为模块暴露公有部分的 channel 对象比较有意思。 join 这个工具方法类似 subscribeOnce, 它的第二个参数是个 Channel 数组。当且仅当所有的 Channel 事件都被 fire ,join 的监听才会被回调。这个方法还是挺有用的

create 是个构造工厂方法。新构造的 Channel 事件会被放置在 channel 对象中。使用上会方便点。在 channel.create('onCordovaReady'); , 便可以便捷的通过 channel[‘onCordovaReady’] 来方便的访问对应类型的 Channel 对象了。

deviceReadyMap,deviceReadyArray,waitForInitialization,initializeComplete 这四者紧密相关。它们决定了 onDeviceReady 事件在何时被触发。于 common/bootstrap.js 中我们看到下面一段代码。

 

channel.join(function() {

channel.onDeviceReady.fire();

}, channel.deviceReadyChannelsArray);

 

         waitForInitialization 用于添加 onDeviceReady 的等待 Channel 事件。 initializeComplete 用于触发指定的等待 Channel 事件。如果想要增加 onDeviceReady 的条件,我们只需要在 onCordovaReady 之前添加 waitForInitialization 即可。事实上,在 lib/android/plugin/storage.js 中我们便可以看到一个绝佳的例子。 cupcakeStorage 利用本地 Plugin 为不支持 localStorage API WebView 提供了一个备选方案。在本地建立好备用的 sqlite 数据库后 ,cupcakeStorage 的等待时间便结束完毕。

四、 启动与 PhoneGap 自定义事件

首先上图。


 

上图为本人整理的启动事件序列,待会儿大家便能从源码中看到了。

待续。。。。

 

 

  • 大小: 9.6 KB
  • 大小: 125.4 KB
1
0
分享到:
评论
15 楼 sd6733531 2012-08-10  
xiaofancn 写道
您好,我想问一下,我用mapabc重写了
https://github.com/apache/incubator-cordova-android/blob/master/framework/src/org/apache/cordova/GeoBroker.java

相关的类,还是提示无法获取位置信息。希望你能分析分析一下这个问题。


这个便涉及MapABC API与此结合的问题了.
具体情况还需要具体分析,兄台可以按照如下顺序排查:
剪去phonegap,原生层与mapabc的api结合是否成功
js层调用map abc插件时,原生层是否正确收到.参数和方法是否正确,所在线程是否正确
如若还是不能解决的话,可以加群讨论
14 楼 xiaofancn 2012-08-07  
您好,我想问一下,我用mapabc重写了
https://github.com/apache/incubator-cordova-android/blob/master/framework/src/org/apache/cordova/GeoBroker.java

相关的类,还是提示无法获取位置信息。希望你能分析分析一下这个问题。
13 楼 sd6733531 2012-07-29  
wmyzcs 写道

多谢兄台支持!
12 楼 wmyzcs 2012-07-29  
11 楼 Riddle0531 2012-07-11  
sd6733531 写道
Riddle0531 写道
Riddle0531 写道
channel = {
            /**
             * Calls the provided function only after all of the channels specified
             * have been fired.
             */
            join: function (h, c) {
                var i = c.length;
                var len = i;
                var f = function() {
                    if (!(--i)) h();
                };
                for (var j = 0; j < len; j++) {
                    !c[j].fired ? c[j].subscribeOnce(f) : i--;
                }
                if (!i) h();
            },
            create: function (type, opts) {
                channel[type] = new Channel(type, opts);
                return channel[type];
            },
这个channel是什么?object/function? 为什么不需要var开头;求关键字-自行学习

h  这里是什么function呢?
前辈,加不上,说服务器维护;能加我吧 717633529


Riddle兄,这里讲话不方便,直接加q群聊吧
248908795

10 楼 sd6733531 2012-07-11  
Riddle0531 写道
Riddle0531 写道
channel = {
            /**
             * Calls the provided function only after all of the channels specified
             * have been fired.
             */
            join: function (h, c) {
                var i = c.length;
                var len = i;
                var f = function() {
                    if (!(--i)) h();
                };
                for (var j = 0; j < len; j++) {
                    !c[j].fired ? c[j].subscribeOnce(f) : i--;
                }
                if (!i) h();
            },
            create: function (type, opts) {
                channel[type] = new Channel(type, opts);
                return channel[type];
            },
这个channel是什么?object/function? 为什么不需要var开头;求关键字-自行学习

h  这里是什么function呢?


Riddle兄,这里讲话不方便,直接加q群聊吧
248908795
9 楼 Riddle0531 2012-07-11  
Riddle0531 写道
channel = {
            /**
             * Calls the provided function only after all of the channels specified
             * have been fired.
             */
            join: function (h, c) {
                var i = c.length;
                var len = i;
                var f = function() {
                    if (!(--i)) h();
                };
                for (var j = 0; j < len; j++) {
                    !c[j].fired ? c[j].subscribeOnce(f) : i--;
                }
                if (!i) h();
            },
            create: function (type, opts) {
                channel[type] = new Channel(type, opts);
                return channel[type];
            },
这个channel是什么?object/function? 为什么不需要var开头;求关键字-自行学习

h  这里是什么function呢?
8 楼 Riddle0531 2012-07-11  
channel = {
            /**
             * Calls the provided function only after all of the channels specified
             * have been fired.
             */
            join: function (h, c) {
                var i = c.length;
                var len = i;
                var f = function() {
                    if (!(--i)) h();
                };
                for (var j = 0; j < len; j++) {
                    !c[j].fired ? c[j].subscribeOnce(f) : i--;
                }
                if (!i) h();
            },
            create: function (type, opts) {
                channel[type] = new Channel(type, opts);
                return channel[type];
            },
这个channel是什么?object/function? 为什么不需要var开头;求关键字-自行学习
7 楼 Riddle0531 2012-07-11  
楼主可以做篇文章从代码层逐步分析下channel.js的工作原理 和机制是如何实现的吗?我也努力研究 ;还有能加您qq什么的吗想交流下phoneGap
6 楼 sd6733531 2012-07-11  
Riddle0531 写道
主要还是.js太久没写不太熟悉了;
比如
this.events = {
            onSubscribe:null,
            onUnsubscribe:null
        };

我就不太清楚,这是类的定义吗?我还是一边看一边复习和学习JS吧


这是Channel对象的一个注册/注销触发器。类似C#中的delegate/event用法
当Channel对象被调用subscribe/unsubscribe的时候,会触发onSubscribe/onUnsubscribe。
很实用的一点便是可以拦截得知当前注册该通道(Channel)的监听器数目。上述的battery.js例子便是据此判定启用和关闭plugin。(无监听器注册通道,关闭Native的Battery监听。有监听器注册通道,打开Native的Battery监听)
5 楼 Riddle0531 2012-07-11  
主要还是.js太久没写不太熟悉了;
比如
this.events = {
            onSubscribe:null,
            onUnsubscribe:null
        };

我就不太清楚,这是类的定义吗?我还是一边看一边复习和学习JS吧
4 楼 sd6733531 2012-07-10  
Riddle0531 写道
如果有个zip 那当然最好不过了;
正在研读您的 - PhoneGap中的事件处理机制 这个部分真是挑战...
有没有推荐个读懂的思路;或者一些必备的知识背景


兄台,你要的zip包我已经传上去了。
PhoneGap的事件处理机制,兄台哪里不懂呢?
会javascript语法,了解观察者模式应该就能看懂
3 楼 Riddle0531 2012-07-10  
如果有个zip 那当然最好不过了;
正在研读您的 - PhoneGap中的事件处理机制 这个部分真是挑战...
有没有推荐个读懂的思路;或者一些必备的知识背景
2 楼 sd6733531 2012-07-10  
写正则逐个文件抽取出来.
开头有个:xx file的标志吧.兄台如果需要现成的话,我上传个zip包吧
1 楼 Riddle0531 2012-07-10  
问下 如何反build .js代码呢?谢谢

相关推荐

Global site tag (gtag.js) - Google Analytics