Hi all颖对,
從今天起我會每天寫郵件給大家發(fā)送日報(bào);日報(bào)的內(nèi)容是每天工作以外學(xué)習(xí)的東西豺妓,比如對組內(nèi)前人寫的代碼的閱讀筆記惜互,或是其他學(xué)到的東西。當(dāng)然這些內(nèi)容對各位老司機(jī)來說大概都是了解過的琳拭,而且由于時間的關(guān)系每天的內(nèi)容可能比較簡短训堆,不妥的部分請指教。另外我的寫作風(fēng)格可能比較啰嗦白嘁,還望見諒坑鱼。。日報(bào)使用Markdown語法編寫, 會同步到我的GitHub Pages絮缅。
下面開始鲁沥。今天的內(nèi)容是關(guān)于股神插件。
股神App開發(fā)的過程中使用了插件耕魄,把錢包中的股神插件作為了股神App唯一的插件画恰,也是robile框架的應(yīng)用。
0x01 插件的話題切入點(diǎn)
對于錢包中這套插件的原理吸奴,我大概是有印象的允扇,簡單描述就是缠局,插件是可以放在宿主中運(yùn)行的apk,但他沒有自己的Activity考润,需要別人提供狭园,比如Plugin框架提供的Activity(PluginActivity);然后這個Activity的生命周期也是傳遞過來的糊治。然后插件所有的新頁面都是Fragment唱矛。嗯,大概就是這樣井辜。然后插件的包名根目錄下一定有一個Plugin.java,它繼承Activityable绎谦,接收生命周期,像下面這樣抑胎。
if (TextUtils.isEmpty(mPluginClass)) {
if (PluginManager.getInstance().isDebugEnable()) {
mPluginPackage = PluginActivity.this.getPackageName();
}
pluginContextClass = mPluginPackage + ".Plugin";
}
對于Module來說呢燥滑,也要有一個HostActivity。以前錢包中有ModuleActivity是插件宿主阿逃,繼承自PluginActivity铭拧,可以直接setFunctionProvider,現(xiàn)在「Robile化」了恃锉,解耦了搀菩,ModuleActivity沒有了,取而代之的是module aar中的HostActivity破托。需要用moduleData保存moduleFunctionProvider(只能是serializable的肪跋,不能含有activity對象;這個問題的話也許可以考慮使用Parceable解決土砂,這一點(diǎn)以后再討論)州既。
好了,具體從哪里切入呢萝映。無論是插件化吴叶,熱修復(fù)還是動態(tài)加載,都涉及到classloader序臂,從這里看起蚌卤。
0x02 ClassLoader
classloader實(shí)例
PluginActivity中有這樣的代碼,它用loadClass()方法裝載了pluginContextClass奥秆,也就是上面構(gòu)造的.Plugin的那個類:
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)行時需要加載類到內(nèi)存里悼瘾。類加載器的實(shí)例不僅有一個签赃。
在Android系統(tǒng)啟動的時候會創(chuàng)建一個Boot類型的ClassLoader實(shí)例谷异,用于加載一些系統(tǒng)Framework層級需要的類,我們的Android應(yīng)用里也需要用到一些系統(tǒng)的類锦聊,所以APP啟動的時候也會把這個Boot類型的ClassLoader傳進(jìn)來。
getClassLoader()可以在程序運(yùn)行過程中讀取到ClassLoader箩绍,classLoader.toString()之后可以看到孔庭,一個APP至少有兩個ClassLoader,也就是上面引用部分提到的兩個材蛛。
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;
}
嗯,需要傳入一個parent作為新建的classloader的父母卑吭。這樣的話Android系統(tǒng)里所有的classloader實(shí)例都會關(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)加載過這個類的的話就返回
try {
//2.parent加載過的話豆赏,就返回parent加載的類
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
// Don't want to see this.
}
if (clazz == null) {
//3.繼承樹上的classloadler都沒有加載過這個類挣菲,就由child加載這個類
clazz = findClass(className);
}
}
return clazz;
}
Sdk源碼里寫得比較清楚,注釋也很翔實(shí)掷邦,這大概是我第一次認(rèn)真看Sdk源碼白胀。掌握閱讀源碼的姿勢是很有必要的。
可以看到抚岗,如果一個類被加載過或杠,那么這個類永遠(yuǎn)不會被重新加載。
思考一下宣蔚,這個「永遠(yuǎn)」的期限是什么呢向抢,從前面判斷,系統(tǒng)啟動之后就會有一個系統(tǒng)級別的ClassLoader胚委,app啟動之后會"fork"出一個新的挟鸠,那么加載之后的內(nèi)容會保存在哪里呢,保存的期限是多久呢篷扩。時間關(guān)系兄猩,明天再說吧。
-Nov21