相關(guān)閱讀
插件化知識(shí)梳理(1) - Small 框架之如何引入應(yīng)用插件
插件化知識(shí)梳理(2) - Small 框架之如何引入公共庫(kù)插件
插件化知識(shí)梳理(3) - Small 框架之宿主分身
插件化知識(shí)梳理(4) - Small 框架之如何實(shí)現(xiàn)插件更新
插件化知識(shí)梳理(5) - Small 框架之如何不將插件打包到宿主中
插件化知識(shí)梳理(6) - Small 源碼分析之 Hook 原理
插件化知識(shí)梳理(7) - 類(lèi)的動(dòng)態(tài)加載入門(mén)
插件化知識(shí)梳理(8) - 類(lèi)的動(dòng)態(tài)加載源碼分析
插件化知識(shí)梳理(9) - 資源的動(dòng)態(tài)加載示例及源碼分析
插件化知識(shí)梳理(10) - Service 插件化實(shí)現(xiàn)及原理
一、前言
在 插件化知識(shí)梳理(6) - Small 源碼分析之 Hook 原理 這一章的學(xué)習(xí)完成之后,下一步我們將進(jìn)入插件化加載的精髓,動(dòng)態(tài)加載類(lèi)的學(xué)習(xí),在此之前,我們需要先準(zhǔn)備一些關(guān)于類(lèi)加載的知識(shí)。
Android當(dāng)中,支持動(dòng)態(tài)加載的兩種方式為:DexClassLoader和PathClassLoader。這兩者之間的區(qū)別為:
-
DexClassLoader- 可以加載
jar、apk、dex - 支持從
SD卡目錄加載。
- 可以加載
-
PathClassLoader- 在許多文章中都有提到,在
Dalvik虛擬機(jī)上,只能加載已經(jīng)安裝到系統(tǒng)中的Apk文件,也就是/data/app目錄下的apk文件。之所以有這個(gè)限制是因?yàn)?code>PathClassLoader會(huì)去讀取data/dalvik-cache目錄下經(jīng)過(guò)優(yōu)化后的dex文件,如果文件不存在,那么就會(huì)報(bào)錯(cuò)。由于手邊沒(méi)有機(jī)器,所以沒(méi)有版本驗(yàn)證。 - 而在
ART虛擬機(jī)上,通過(guò)源碼當(dāng)中的注釋?zhuān)梢园l(fā)現(xiàn)是支持的。
- 在許多文章中都有提到,在
二、具體實(shí)例
實(shí)例的工程目錄結(jié)構(gòu)為:

-
app:宿主模塊 -
library:插件模塊 -
libraryinterface:插件接口模塊
其中,app和library模塊分別依賴(lài)于libraryinterface,library和libraryinterface為Android Library類(lèi)型的Module,下面,我們開(kāi)始講解整個(gè)工程的構(gòu)建過(guò)程。
2.1 接口模塊 libraryinterface
接口模塊相當(dāng)于是宿主模塊和插件模塊所定義的一套標(biāo)準(zhǔn),宿主模塊遵循固定的業(yè)務(wù)邏輯,而具體的實(shí)現(xiàn)則根據(jù)插件模塊的不同而不同。
在接口模塊中,我們定義一個(gè)簡(jiǎn)單的接口IPlugin.java:
public interface IPlugin {
public int getVersion();
}
2.2 插件模塊 library
首先,我們?cè)诓寮K的build.gradle文件中,引入libraryinterface模塊
dependencies {
//引入接口模塊
compile project (':libraryinterface')
}
接著,我們編寫(xiě)一個(gè)實(shí)現(xiàn)類(lèi):
public class PluginImpl implements IPlugin {
@Override
public int getVersion() {
return 1;
}
}
接下來(lái)需要做的就是將該插件模塊打包成一個(gè)jar文件,同樣是在build.gradle文件中,創(chuàng)建一個(gè)Task任務(wù):
task makeJar(type: Copy) {
delete 'build/libs/plugin.jar'
from ('build/intermediates/bundles/release/')
into ('../file/')
include ('classes.jar')
rename ('classes.jar','plugin.jar')
}
makeJar.dependsOn(build)
首先點(diǎn)擊make module:

接下來(lái),在項(xiàng)目的根目錄下執(zhí)行命令:
./gradlew makeJar
就會(huì)得到一個(gè)plugin.jar文件,但是這個(gè)jar文件是不能夠被動(dòng)態(tài)加載的,因?yàn)樗鼉?nèi)部其實(shí)是.class文件,我們通過(guò)解壓可以看出:

那么我們就需要通過(guò)
Android SDK自帶的dx工具進(jìn)行轉(zhuǎn)換,把它轉(zhuǎn)換為.dex,轉(zhuǎn)換后的文件為plugin_dex.jar
/Users/lizejun/Library/Android/sdk/build-tools/25.0.3/dx --dex --output=file/plugin_dex.jar file/plugin.jar
將plugin_dex.jar解壓之后,可以看到它已經(jīng)被轉(zhuǎn)換成了.dex文件:

最后,將該
jar包push到手機(jī)中的/sdcard/Plugin目錄下:
2.3 宿主模塊 app
首先,宿主模塊同樣需要依賴(lài)于接口模塊libraryinterface:
dependencies {
//引入接口模塊
compile project (':libraryinterface')
}
在代碼當(dāng)中,我們通過(guò)DexClassLoader/PathClassLoader動(dòng)態(tài)外部的插件plugin_dex.jar,通過(guò)反射實(shí)例化PluginImpl類(lèi),并調(diào)用它的getVersion()方法進(jìn)行驗(yàn)證:
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv_plug_result);
getPluginA();
}
private void getPluginA() {
File dexOutputDir = getDir("dex1", 0);
String dexPath = Environment.getExternalStorageDirectory().toString() + "/Plugin/plugin_dex.jar";
DexClassLoader loader = new DexClassLoader(dexPath, dexOutputDir.getAbsolutePath(), null, getClassLoader());
try {
Class clz = loader.loadClass("com.demo.lizejun.library.PluginImpl");
IPlugin impl = (IPlugin) clz.newInstance();
int version = impl.getVersion();
mTextView.setText("Version=" + version);
} catch (Exception e) {
e.printStackTrace();
}
}
private void getPluginB() {
String dexPath = Environment.getExternalStorageDirectory().toString() + "/Plugin/plugin_dex.jar";
PathClassLoader loader = new PathClassLoader(dexPath, getClassLoader());
try {
Class clz = loader.loadClass("com.demo.lizejun.library.PluginImpl");
IPlugin impl = (IPlugin) clz.newInstance();
int version = impl.getVersion();
mTextView.setText("Version=" + version);
} catch (Exception e) {
e.printStackTrace();
}
}
}
最終的結(jié)果為:

更多文章,歡迎訪問(wèn)我的 Android 知識(shí)梳理系列:
- Android 知識(shí)梳理目錄:http://www.itdecent.cn/p/fd82d18994ce
- 個(gè)人主頁(yè):http://lizejun.cn
- 個(gè)人知識(shí)總結(jié)目錄:http://lizejun.cn/categories/
