1.概述
1.1 Android使用Dalvik虛擬機(jī)加載可執(zhí)行程序,所以不能直接加載基于class的jar,而是需要將class轉(zhuǎn)化為dex字節(jié)碼,從而執(zhí)行代碼。優(yōu)化后的字節(jié)碼文件可以存在一個(gè).jar中,只要其內(nèi)部存放的是.dex即可使用,這里我們直接使用AS工程
直接打包出APK然后解壓去除dex文件即可1.2 Android支持動(dòng)態(tài)加載的兩種方式是:DexClassLoader和PathClassLoader,DexClassLoader可加載jar/apk/dex,且支持從SD卡加載;PathClassLoader只支持加載已經(jīng)安裝到 Android 系統(tǒng)中的 apk 文件
1.3 接下來(lái)我們使用DexClassLoader實(shí)現(xiàn)加載sd卡的dex文件并且調(diào)用內(nèi)部的方法
2.實(shí)踐
- 2.1 首先我們定義個(gè)IShowToast接口,這個(gè)其實(shí)要不要都可以
public interface IShowToast {
public int showToast(android.app.Application context);
}
- 2.2 在定義實(shí)踐類
public class ShowToastImpl implements IShowToast {
@Override
public int showToast(android.app.Application context) {
Toast.makeText(context, "我來(lái)自另一個(gè)dex文件", Toast.LENGTH_LONG).show();
return 100;
}
public void test() {
Log.e("\"我來(lái)自另一個(gè)dex文件\"","我來(lái)自另一個(gè)dex文件");
}
}
- 2.3 跑當(dāng)前的Android工程生成dex文件,將生成的dex文件拷貝到sd卡下
- 2.4 另開(kāi)啟一個(gè)新工程測(cè)試代碼如下
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
File dexOutputDir = getDir("dex1", 0);
String dexPath = Environment.getExternalStorageDirectory().toString() + File.separator + "a";
File file = new File(dexPath);
Log.e("MainActivity",file.length()+"");
DexClassLoader loader = new DexClassLoader(dexPath,
dexOutputDir.getAbsolutePath(),
null, getClassLoader());
Class clz = loader.loadClass("com.Company.Demo.ShowToastImpl");
Method m1 = clz.getDeclaredMethod("test");
Method showToast = clz
.getDeclaredMethod("showToast", new Class[]{getApplication().getClass()});
m1.invoke(clz.newInstance());
showToast.invoke(clz.newInstance(),getApplication());
} catch (Exception e) {
Log.e("MainActivity", "error happened", e);
}
}
}
其實(shí)這里就是通過(guò)DexClassLoader得到sd卡的dex的ShowToastImpl對(duì)象然后通過(guò)反射即可調(diào)用器內(nèi)部的方法
此處需要注意DexClassLoader的四個(gè)參數(shù):
參數(shù)1 dexPath:待加載的dex文件路徑,如果是外存路徑,一定要加上讀外存文件的權(quán)限(<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> ),否則會(huì)報(bào)與上面一樣的錯(cuò)誤,這點(diǎn)參考文章2中說(shuō)這個(gè)權(quán)限可有可無(wú)是錯(cuò)誤的。(更正下:Android4.4 KitKat及以后的版本需要此權(quán)限,之前的版本不需要權(quán)限)
參數(shù)2 optimizedDirectory:解壓后的dex存放位置,此位置一定要是可讀寫且僅該應(yīng)用可讀寫(安全性考慮),所以只能放在data/data下。本文getDir("dex1", 0)會(huì)在/data/data/**package/下創(chuàng)建一個(gè)名叫”app_dex1“的文件夾,其內(nèi)存放的文件是自動(dòng)生成a.dex;如果不滿足條件,Android會(huì)報(bào)的錯(cuò)誤為:
java.lang.IllegalArgumentException: optimizedDirectory not readable/writable: /storage/sdcard0
java.lang.IllegalArgumentException: Optimized data directory /storage/sdcard0 is not owned by the current user. Shared storage cannot protect your application from code injection attacks.
參數(shù)3 libraryPath:指向包含本地庫(kù)(so)的文件夾路徑,可以設(shè)為null
參數(shù)4 parent:父級(jí)類加載器,一般可以通過(guò)Context.getClassLoader獲取到,也可以通過(guò)ClassLoader.getSystemClassLoader()取到。