之前做项目直接用WebView加载url显示Web页,感觉简单无比。最近接触项目,采用的是Cordova框架实现,其中Activity继承自CordovaActivity然后在onCreate代码中就直接调用loadUrl(url)就加载网页了,当时我的小心脏扑通跳啊,因为当时知道Cordova框架还是基于WebView来实现的混合开发框架,所以为了一探究竟,决定研究研究源码看看到底是在什么地方通过WebView来加载的页面,这也就是本文的主要内容。
1、涉及到的主要类介绍
本小节主要是介绍分析代码过程中,揪出来的一些类,当然其中还有一些接口,但由于接口没有实现方法,所以在分析中略过,就直接列出涉及到的类了。
CordovaActivity:实例化CordovaWebViewImpl类型成员变量
CordovaWebViewImpl:实例化SystemWebViewEngine类型成员变量
SystemWebViewEngine:实例化SystemWebView类型成员变量
SystemWebView:WebView子类loadUrl操作最中在这个类中完成
2、初始化流程梳理
首先,CordovaActivity通过loadUrl方法开始加载页面
public void loadUrl(String url) {
if (appView == null) {
init();
}
// If keepRunning
this.keepRunning = preferences.getBoolean("KeepRunning", true);
appView.loadUrlIntoView(url, true);
}
通过代码可以看到其中有一个init()的初始化方法,它都干了啥呢?贴出它的代码看看吧
protected void init() {
appView = makeWebView();
createViews();
if (!appView.isInitialized()) {
appView.init(cordovaInterface, pluginEntries, preferences);
}
cordovaInterface.onCordovaInit(appView.getPluginManager());
// Wire the hardware volume controls to control media if desired.
String volumePref = preferences.getString("DefaultVolumeStream", "");
if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
}
其中有一个appView是一个CordovaWebViewImpl类型的变量,它通过makeWebView方法来完成实例化
protected CordovaWebView makeWebView() {
return new CordovaWebViewImpl(makeWebViewEngine());
}
从makeWebView方法实现可以看到,在实例化CordovaWebViewImpl时,还在CordovaWebViewImpl构造函数中通过makeWebViewEngine方法返回了SystemWebViewEngine类型参数,也就是在实例化CordovaWebViewImpl类的时候,实例化了它的SystemWebViewEngine成员变量。
再来看看makeWebViewEngine方法
protected CordovaWebViewEngine makeWebViewEngine() {
return CordovaWebViewImpl.createEngine(this, preferences);
}
/********************/
public static CordovaWebViewEngine createEngine(Context context, CordovaPreferences preferences) {
String className = preferences.getString("webview", SystemWebViewEngine.class.getCanonicalName());
try {
Class<?> webViewClass = Class.forName(className);
Constructor<?> constructor = webViewClass.getConstructor(Context.class, CordovaPreferences.class);
return (CordovaWebViewEngine) constructor.newInstance(context, preferences);
} catch (Exception e) {
throw new RuntimeException("Failed to create webview. ", e);
}
}
通过makeWebViewEngine方法实例化CordovaWebViewImpl,同时在CordovaWebViewImpl中实例化SystemWebViewEngine变量,这里实例化SystemWebViewEngine变量用到了反射构建实例的方法,以前我没有用这种用法,通过阅读源码也算是学到东西了。从构造函数的参数可以看出是通过SystemWebViewEngine的如下构造函数来实例化SystemWebViewEngine成员的。
/** Used when created via reflection. */
public SystemWebViewEngine(Context context, CordovaPreferences preferences) {
this(new SystemWebView(context), preferences);
}
public SystemWebViewEngine(SystemWebView webView, CordovaPreferences preferences) {
this.preferences = preferences;
this.webView = webView;
cookieManager = new SystemCookieManager(webView);
}
从以上代码可以看出,在实例化SystemWebViewEngine的过程中,又实例化了SystemWebViewEngine的SystemWebView成员变量。
到现在,CordovaActivity中的loadUrl中的init()初始化方法,算是完成了它干的事情,即初始化CordovaActivity的CordovaWebViewImpl成员变量,初始化CordovaWebViewImpl的SystemWebEngine成员变量,初始化SystemWebEngine的SystemWebView成员变量,这样梳理了一遍初始化流程后,几个类之间的关系也就理清楚了。
3、加载Url流程梳理
再看CordovaActivity的loadUrl方法,在完成初始化后执行了appView.loadUrlIntoView(url,true)方法,它调用了CordovaWebViewImpl中的loadUrlIntoView方法,其中有一段关键代码如下
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
if (loadUrlTimeoutValue > 0) {
cordova.getThreadPool().execute(timeoutCheck);
}
engine.loadUrl(url, _recreatePlugins);
}
});
其中在UI线程中调用了engine.loadUrl这个方法,对应到SystemWebViewEngine中去找对应方法有:
@Override
public void loadUrl(final String url, boolean clearNavigationStack) {
webView.loadUrl(url);
}
这里的webView就是SystemWebView了,那么整个加载到这里就基本结束了。第一次尝试着讲解阅读源码的东西,发现自己能讲出来的才是真的理解了的,当然这里讲的不好,也可能是自己理解的还不透彻,有疑问的欢迎留言咨询。