昨天讲解了DroidGap类,DroidGap继承Activity,在DroidGap的init()初始化方法中设置了WebView,今天主要讲解CordovaWebView类以及CordovaWebviewClient类和CordovaChromeClient类。
CordovaWebView类
public class CordovaWebView extends WebView {...}
CordovaWebView类继承WebView类,在上一篇博文中已经对WebView类做了简要的介绍。PhoneGap针对不同平台的WebView做了扩展和封装,使WebView这个组件变成可访问设备本地API的强大浏览器,所以开发人员在PhoneGap框架下可通过JavaScript访问设备本地API。
可以说,CordovaWebView是整个PhoneGap的核心组件。
1 /** 2 * Constructor. 3 * 4 * @param context 5 */ 6 public CordovaWebView(Context context) { 7 super(context); 8 if (CordovaInterface.class.isInstance(context)) 9 { 10 this.cordova = (CordovaInterface) context; 11 } 12 else 13 { 14 Log.d(TAG, "Your activity must implement CordovaInterface to work"); 15 } 16 this.loadConfiguration(); 17 this.setup(); 18 }
CordovaWebView类的构造函数中,需要传入Context,并且Context必须是CordovaInterface的实现类(这里需要特别注意)。构造函数里调用loadConfiguration()和Setup()方法。在CordovaWebView类的构造函数重载方法中,还有setWebChromeClient和setWebViewClient对CordovaWebView的设置,原因在上篇最后也有讲到,这两个类的具体讲解在下面。
loadConfiguration()的作用是从res/xml/cordova.xml文件中加载Cordova配置信息,包括允许加载本地网页等,xml文件没几行,不细说。
setup()方法初始化WebView配置,包括设置WebView初始化视图大小、是否允许垂直滚动条、触摸焦点信息。
因为PhoneGap的强大之处在于可以在Web端直接调用底层API,包括照相机、指南针、GPS等设备,其实现这些功能是通过JavaScript与底层交互实现的,所以在接下来设置settings.setJavaScriptEnabled(true)也就理所当然的事情了。
然后设置了数据库database、DOM storage和地理位置应用geolocation等信息,这里也不细讲。
需要着重提到的一点是最后PhoneGap插件的初始化。没错,PhoneGap插件的初始化是在这里进行的。PhoneGap插件管理机制是PhoneGap实现跨平台的基础。pluginManager类在后面也会讲到。
在上篇中讲到了DroidGap类loadUrl()方法,我们可以把它看成是整个PhoneGap应用的入口函数。实际DroidGap类的loadUrl()方法调用了CordovaWebView类的loadUrl()方法:
代码不多,也比较好理解,自己看吧,不细讲了。O(∩_∩)O~
这里需要细讲一下。Android WebView里并没有对加载超时的处理,PhoneGap自己实现了加载超时处理的方法。虽然看不看这里并不会影响对PhoneGap整个机制的理解,但拿出来讲一下对以后可能也会很有用处。
从代码的第26行注释看起,CordovaWebViewClient类有一个属性LoadUrlTimeout,这里不要被其定义为int类型迷惑,实际这个变量完全可以是boolean布尔值,因为它只有两个值:0和1。在页面开始加载时currentLoadUrlTimeout初始化为me.loadUrlTimeout(这个值初始化为0),在页面加载完成(注意:这里CordovaWebViewClient派上用场了),即CordovaWebViewClient类里OnpageFinished()方法中appView.LoadUrlTimeout被置成1。定义了loadError和runOnUiThread两个线程。然后起了一个在Timeout之后执行的线程,具体执行那个线程就看页面是否加载完成,即OnpageFinished()方法中appView.LoadUrlTimeout是否被置成了1。
CordovaWebViewClient类
CordovaWebViewClient类主要处理各种通知、请求事件的,重写了WebView类的一些方法,这里主要讲解其中onPageStarted()和onPageFinished()两个方法。
从文档注释我们可以看出,onPageStarted方法用于通知主应用程序Web页面开始加载了。其中在页面加载时比较重要的一步是创建CallbackServer对象并初始化。(CallbackServer类可是实现插件异步执行的重头戏啊,好紧张啊,写了这么久CallbackServer终于出现了啊o(╯□╰)o)
上文已经提到在onPageFinished方法中会执行this.appView.loadUrlTimeout++,定时器loadUrlTimeout的值被置为1,表明页面加载完成。如果没有执行到此,说明加载超时,在CordovaWebView类的loadUrlIntoView()方法中判断if(currentLoadUrlTimeout ==me.loadUrlTimeout),则执行loadError线程。
CordovaChromeClient类
WebChromeClient是辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度等,一般的WebView可以不设置ChromeClient,比如你的WebView不需要处理JavaScript脚本。前面已经提到为什么PhoneGap要设置ChromeClient,不再多说。
引用某位大侠的话说,关于Java/JS互调,在android sdk文档中,也有用JsInterface和loadUrl做到交互的示例。PhoneGap并没有选择用JsInterface,而是使用拦截prompt这种hack做法。
对JavaScript消息的拦截是在onJsPrompt()方法中实现并处理的。
onJsPrompt到底拦截了哪些信息呢?
从传入参数来说,比较重要的有两个:message和defaultValue。
message字符串存放了插件的应用信息,如Camera插件的图片质量、图片是否可编辑,图片返回类型等。
defaultValue字符串存放了插件信息:service(如Camera)、action(如getPicture())、callbackId、async等。
拦截到这些信息后就会调用this.appView.pluginManager.exec(service, action, callbackId, message, async)去执行。
当然onJsPrompt不只实现插件调用信息,还有JavaScript polling轮询信息、CallbackServer调用信息等处理。