android phonegap源码详解(3)

android phonegap源码详解(三)

上两篇在这里:

android phonegap源码详解(一)

android phonegap源码详解(二)

 

一、PhoneGap的启动

近期因为赶项目和犯懒,所以一直没有更新,希望朋友们见谅。

前面介绍过phoneGap的模块化机制。其通过requiredefine,巧妙的将模块的定义和依赖关系隔离开来。


android phonegap源码详解(3)

如上图所示,在定义模块机制后。后面统统都是做define。那么,这些被定义的模块最终何时被实例化呢?


android phonegap源码详解(3)

cordova.js的末尾我们找到了这一句。第一个被请求实例化的模块是cordova(adobe版本是phonegap),接着在实例化cordova模块后,cordova依赖的模块也纷纷被实例化出来了。(channelutils)

cordova模块仅是个最基础的顶层通信模块。除了cordova模块外,其余数十个phonegap模块都还在沉睡之中。事实上,这一步走完其实phonegap已经完成了。Phonegap用户只需要require(‘cordova/plugin/contacts’)这样,即可访问到phonegap的功能。

但是,这样做还不够好。第一,require对于phonegap使用者来说并不友好。第二,虽然可以通过require得到构建模块的实例,可这并不代表Native/Javascript的通信关系已经建立。phonegap用户还不知道何时可以安全正确的使用这些功能。

基于上面两点,phonegap构建了一个bootstrap函数。在上图的后一句,大家可以看到bootstrap的封闭调用。下面我来详解一下bootstrap

二、Bootstrap详解

首先贴上代码。

(function (context) {

    var channel = require("cordova/channel"),

        _self = {

            boot: function () {

                /**

                 * Create all cordova objects once page has fully loaded and native side is ready.

                 */

                channel.join(function() {

                    var builder = require('cordova/builder'),

                        base = require('cordova/common'),

                        platform = require('cordova/platform');

 

                    // Drop the common globals into the window object, but be nice and don't overwrite anything.

                    builder.build(base.objects).intoButDontClobber(window);

 

                    // Drop the platform-specific globals into the window object

                    // and clobber any existing object.

                    builder.build(platform.objects).intoAndClobber(window);

 

                    // Merge the platform-specific overrides/enhancements into

                    // the window object.

                    if (typeof platform.merges !== 'undefined') {

                        builder.build(platform.merges).intoAndMerge(window);

                    }

 

                    // Call the platform-specific initialization

                    platform.initialize();

 

                    // Fire event to notify that all objects are created

                    channel.onCordovaReady.fire();

 

                    // Fire onDeviceReady event once all constructors have run and

                    // cordova info has been received from native side.

                    channel.join(function() {

                        channel.onDeviceReady.fire();

                    }, channel.deviceReadyChannelsArray);

 

                }, [ channel.onDOMContentLoaded, channel.onNativeReady ]);

            }

        };

 

    // boot up once native side is ready

    channel.onNativeReady.subscribeOnce(_self.boot);

 

    // _nativeReady is global variable that the native side can set

    // to signify that the native code is ready. It is a global since

    // it may be called before any cordova JS is ready.

    if (window._nativeReady) {

        channel.onNativeReady.fire();

    }

 

}(window));

 
 

我们先来详细关注下,phonegap是如何提供回调通知,来告诉用户NativeJavascript之间成功建立联系的呢?

首先,bootstrap通过channel模块注册监听了onNativeReady事件。这个事件由Native层触发,用来表示Native层准备完毕(可以接受plugin调用)。在Android平台上面,onNativeReady是在WebViewonPageFinished回调中触发的。


android phonegap源码详解(3)

 为了安全的启动,boot等待onNativeReadyonDOMContentLoaded完毕后才执行。那么Phonegapboot都做了些什么呢?

boot做了两件事情。首先是实例化和发布模块来给phonegap用户使用。其次是广播onCordovaReady来通知phonegapboot完毕,用户可以放心使用phonegap的功能。

通过builder,模块的实例化和发布工作变得很艺术。builder模块提供三种发布方式,分别是intoButDontClobber(若发布目标中已存在同名模块,则不允许做覆盖),intoAndClobber(若发布目标中已存在同名模块,则强制覆盖),intoAndMerge(若发布目标中已存在实例模块,则尝试对两者进行合并,合并的优先级是欲发布模块比发布模块高)

解释一下“发布”这个词。其实就是根据定义id实例化(require)模块,然后把它作为某个object的属性。从boot的源码中,我们可以看到。在common.jsplatform.js中,分别定义了objects对象。objects定义了各个模块的id和层次关系。父子关系通过children字段表明。

    objects: {

        cordova: {

            path: 'cordova',

            children: {

                exec: {

                    path: 'cordova/exec'

                }

            }

        },
 

         根据上面的定义,cordova模块下将挂一个exec模块。即通过builder,用户可以直接通过cordova.exec来访问exec模块。

         当然,发布的target都是windowcommon下的发布定义(objects),所使用的发布策略是不覆写原有属性。之所以这样做,是考虑到之后浏览器加强html5支持后,有些模块将会原生提供。

platform下的发布定义(objects)所使用的发布策略是覆写所有属性。其定义的是与平台密切相关的模块,因此会做强制覆写。

注意platform下的发布定义,除了objects外。还提供了merge定义。这里的merge作用在于“增强”。为原有的通用模块,增加一些平台相关的其它方法。在android/platform.jsmerges定义便是一个例子。最终发布的notification将既包含commonnotification.jsalert方法,也包含platformnotification.jsactivityStart方法。

发布模块后会执行platform.initialize方法。这个方法用于做platform初始化工作,将与平台特性紧耦合,尤其会与NativeJavascript互相通信相关。在android平台版本中,initialize包含了polling/xhr的初始化,android的按键事件、可兼容web数据库apilocalstorage。当这些工作完毕后,将会广播onCordovaReady事件。此后便可以安全的使用phonegap所提供的所有功能了。

onCordovaReady后会去准备onDeviceReady。关于onDeviceReady的详细描述,我在上篇解析的channel中提过了,在此也就不重复了。

三、总结

至此phonegapNativeJavascript层源码解析完毕。有关如何扩展phonegap插件,各位可以参照github上面的这个开源项目:https://github.com/purplecabbage/phonegap-plugins

需要注意的是。其中的addPlugin写法,phonegap已经不再推荐使用了。并表示该方法将在2.0版本中移除。

由于本人真正接触phonegap时间并不长,难免会有解释不当的地方。欢迎指正!也欢迎跟帖讨论!