相關(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)中的注釋公罕,可以發(fā)現(xiàn)是支持的。
- 在許多文章中都有提到镣陕,在
二耀销、具體實(shí)例
實(shí)例的工程目錄結(jié)構(gòu)為:
-
app
:宿主模塊 -
library
:插件模塊 -
libraryinterface
:插件接口模塊
其中楼眷,app
和library
模塊分別依賴于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
首先,宿主模塊同樣需要依賴于接口模塊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.reibang.com/p/fd82d18994ce
- 個(gè)人主頁(yè):http://lizejun.cn
- 個(gè)人知識(shí)總結(jié)目錄:http://lizejun.cn/categories/