Hi all,
從今天起我會(huì)每天寫郵件給大家發(fā)送日?qǐng)?bào);日?qǐng)?bào)的內(nèi)容是每天工作以外學(xué)習(xí)的東西,比如對(duì)組內(nèi)前人寫的代碼的閱讀筆記,或是其他學(xué)到的東西。當(dāng)然這些內(nèi)容對(duì)各位老司機(jī)來說大概都是了解過的,而且由于時(shí)間的關(guān)系每天的內(nèi)容可能比較簡(jiǎn)短,不妥的部分請(qǐng)指教。另外我的寫作風(fēng)格可能比較啰嗦,還望見諒。。日?qǐng)?bào)使用Markdown語法編寫, 會(huì)同步到我的GitHub Pages。
下面開始。今天的內(nèi)容是關(guān)于股神插件。
股神App開發(fā)的過程中使用了插件,把錢包中的股神插件作為了股神App唯一的插件,也是robile框架的應(yīng)用。
0x01 插件的話題切入點(diǎn)
對(duì)于錢包中這套插件的原理,我大概是有印象的,簡(jiǎn)單描述就是,插件是可以放在宿主中運(yùn)行的apk,但他沒有自己的Activity,需要?jiǎng)e人提供,比如Plugin框架提供的Activity(PluginActivity);然后這個(gè)Activity的生命周期也是傳遞過來的。然后插件所有的新頁面都是Fragment。嗯,大概就是這樣。然后插件的包名根目錄下一定有一個(gè)Plugin.java,它繼承Activityable,接收生命周期,像下面這樣。
if (TextUtils.isEmpty(mPluginClass)) {
if (PluginManager.getInstance().isDebugEnable()) {
mPluginPackage = PluginActivity.this.getPackageName();
}
pluginContextClass = mPluginPackage + ".Plugin";
}
對(duì)于Module來說呢,也要有一個(gè)HostActivity。以前錢包中有ModuleActivity是插件宿主,繼承自PluginActivity,可以直接setFunctionProvider,現(xiàn)在「Robile化」了,解耦了,ModuleActivity沒有了,取而代之的是module aar中的HostActivity。需要用moduleData保存moduleFunctionProvider(只能是serializable的,不能含有activity對(duì)象;這個(gè)問題的話也許可以考慮使用Parceable解決,這一點(diǎn)以后再討論)。
好了,具體從哪里切入呢。無論是插件化,熱修復(fù)還是動(dòng)態(tài)加載,都涉及到classloader,從這里看起。
0x02 ClassLoader
classloader實(shí)例
PluginActivity中有這樣的代碼,它用loadClass()方法裝載了pluginContextClass,也就是上面構(gòu)造的.Plugin的那個(gè)類:
Class<?> pluginMain = null;
if (!PluginManager.getInstance().isDebugEnable()) {
pluginMain = mPluginClassLoader.loadClass(pluginContextClass);
} else {
pluginMain = PluginActivity.this.getClassLoader().loadClass(
pluginContextClass);
}
loadClass()是什么呢?它是ClassLoader的方法,用來加載需要的類。類似JVM中的defineClass()方法。
Dalvik/ART虛擬機(jī)跟JVM虛擬機(jī)一樣,運(yùn)行時(shí)需要加載類到內(nèi)存里。類加載器的實(shí)例不僅有一個(gè)。
在Android系統(tǒng)啟動(dòng)的時(shí)候會(huì)創(chuàng)建一個(gè)Boot類型的ClassLoader實(shí)例,用于加載一些系統(tǒng)Framework層級(jí)需要的類,我們的Android應(yīng)用里也需要用到一些系統(tǒng)的類,所以APP啟動(dòng)的時(shí)候也會(huì)把這個(gè)Boot類型的ClassLoader傳進(jìn)來。
getClassLoader()可以在程序運(yùn)行過程中讀取到ClassLoader,classLoader.toString()之后可以看到,一個(gè)APP至少有兩個(gè)ClassLoader,也就是上面引用部分提到的兩個(gè)。
Parent-Delegation Model
我們可以自己創(chuàng)建CloassLoader實(shí)例來加載class,構(gòu)造函數(shù)是這樣的:
/*
* constructor for the BootClassLoader which needs parent to be null.
*/
ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
if (parentLoader == null && !nullAllowed) {
throw new NullPointerException("parentLoader == null && !nullAllowed");
}
parent = parentLoader;
}
嗯,需要傳入一個(gè)parent作為新建的classloader的父母。這樣的話Android系統(tǒng)里所有的classloader實(shí)例都會(huì)關(guān)聯(lián)在同一棵樹。這就是Parent-Delegation Model。
Sdk中看到的loadClass()方法:
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {//1.classLoader已經(jīng)加載過這個(gè)類的的話就返回
try {
//2.parent加載過的話,就返回parent加載的類
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
// Don't want to see this.
}
if (clazz == null) {
//3.繼承樹上的classloadler都沒有加載過這個(gè)類,就由child加載這個(gè)類
clazz = findClass(className);
}
}
return clazz;
}
Sdk源碼里寫得比較清楚,注釋也很翔實(shí),這大概是我第一次認(rèn)真看Sdk源碼。掌握閱讀源碼的姿勢(shì)是很有必要的。
可以看到,如果一個(gè)類被加載過,那么這個(gè)類永遠(yuǎn)不會(huì)被重新加載。
思考一下,這個(gè)「永遠(yuǎn)」的期限是什么呢,從前面判斷,系統(tǒng)啟動(dòng)之后就會(huì)有一個(gè)系統(tǒng)級(jí)別的ClassLoader,app啟動(dòng)之后會(huì)"fork"出一個(gè)新的,那么加載之后的內(nèi)容會(huì)保存在哪里呢,保存的期限是多久呢。時(shí)間關(guān)系,明天再說吧。
-Nov21