在 插件化知識梳理(6) - Small 源碼分析之 Hook 原理 這一章的學習完成以後,下一步咱們將進入插件化加載的精髓,動態加載類的學習,在此以前,咱們須要先準備一些關於類加載的知識。java
Android
當中,支持動態加載的兩種方式爲:DexClassLoader
和PathClassLoader
。這二者之間的區別爲:bash
DexClassLoader
jar、apk、dex
SD
卡目錄加載。PathClassLoader
Dalvik
虛擬機上,只能加載已經安裝到系統中的Apk
文件,也就是/data/app
目錄下的apk
文件。之因此有這個限制是由於PathClassLoader
會去讀取data/dalvik-cache
目錄下通過優化後的dex
文件,若是文件不存在,那麼就會報錯。因爲手邊沒有機器,因此沒有版本驗證。ART
虛擬機上,經過源碼當中的註釋,能夠發現是支持的。
實例的工程目錄結構爲: app
app
:宿主模塊library
:插件模塊libraryinterface
:插件接口模塊其中,app
和library
模塊分別依賴於libraryinterface
,library
和libraryinterface
爲Android Library
類型的Module
,下面,咱們開始講解整個工程的構建過程。ide
接口模塊至關因而宿主模塊和插件模塊所定義的一套標準,宿主模塊遵循固定的業務邏輯,而具體的實現則根據插件模塊的不一樣而不一樣。 在接口模塊中,咱們定義一個簡單的接口IPlugin.java
:工具
public interface IPlugin {
public int getVersion();
}
複製代碼
首先,咱們在插件模塊的build.gradle
文件中,引入libraryinterface
模塊源碼分析
dependencies {
//引入接口模塊
compile project (':libraryinterface')
}
複製代碼
接着,咱們編寫一個實現類:學習
public class PluginImpl implements IPlugin {
@Override
public int getVersion() {
return 1;
}
}
複製代碼
接下來須要作的就是將該插件模塊打包成一個jar
文件,一樣是在build.gradle
文件中,建立一個Task
任務:gradle
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)
複製代碼
首先點擊make module
: 優化
./gradlew makeJar
複製代碼
就會獲得一個plugin.jar
文件,可是這個jar
文件是不可以被動態加載的,由於它內部實際上是.class
文件,咱們經過解壓能夠看出: ui
Android SDK
自帶的
dx
工具進行轉換,把它轉換爲
.dex
,轉換後的文件爲
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
解壓以後,能夠看到它已經被轉換成了.dex
文件:
jar
包
push
到手機中的
/sdcard/Plugin
目錄下:
首先,宿主模塊一樣須要依賴於接口模塊libraryinterface
:
dependencies {
//引入接口模塊
compile project (':libraryinterface')
}
複製代碼
在代碼當中,咱們經過DexClassLoader/PathClassLoader
動態外部的插件plugin_dex.jar
,經過反射實例化PluginImpl
類,並調用它的getVersion()
方法進行驗證:
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();
}
}
}
複製代碼
最終的結果爲: