這裏所說的動態加載是加載一個包(插件),其實就是一個 apk只是這個apk 動態加載而已。
創建一個要動態加載的 apk(插件)
寫一個類用於動態加載
//DynamicTest.java
package com.example.liuhailong.idynamic;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
public class DynamicTest {
public void showPluginWindow(Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("我是插件的對話框");
builder.setTitle(R.string.app_name);
builder.setNegativeButton("取消", new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
Dialog dialog = builder.create();
dialog.show();
}
}
其時就是一個工程可以運行
再創建一個工程
把剛纔編譯的 apk 放到 assets 目錄下
assets創建:
先搞清動態加載其實就是把 apk 加載到內存所以有讀apk,寫入的操作
定義一個讀 apk 的類,把 apk 寫入sdcard的文件
//Utils.java
package com.example.liuhailong.testplugin;
import android.content.Context;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Utils {
private static final int BUF_SIZE = 2048;
/**
* 將插件資源從assets寫入到sdcard上
* @param context 上下文
* @param assetFile asset目錄下的文件名稱
* @param sdCardPath 要寫入到sdcard的文件
*/
public static boolean prepareDex(Context context, String assetFile, File sdCardPath) {
BufferedInputStream bis = null;
OutputStream dexWriter = null;
try {
bis = new BufferedInputStream(context.getAssets().open(assetFile));
dexWriter = new BufferedOutputStream(new FileOutputStream(sdCardPath));
byte[] buf = new byte[BUF_SIZE];
int len;
while ((len = bis.read(buf, 0, BUF_SIZE))>0) {
dexWriter.write(buf, 0, len);
}
dexWriter.close();
bis.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
再寫主函數動態加載
package com.example.liuhailong.testplugin;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import java.io.File;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
public class MainActivity extends AppCompatActivity {
private static String TAG = "MainActivity";
//apk文件存放的路徑
private String dexFilePath;
//解壓出dex存儲路徑
private File dexFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG, "當前的classloader:" + getClassLoader());
Log.e(TAG, "系統的classloader:" + ClassLoader.getSystemClassLoader());
//設置dexFilePath
dexFilePath = getExternalFilesDir(null).getPath() + File.separator + "plugin.apk";
System.out.println("dexFilePath:" + dexFilePath);
dexFile = getDir("dex", 0);
boolean success = Utils.prepareDex(this, "PluginDemo.apk", new File(dexFilePath));
if (!success)
return;
//創建DexClassLoader
final DexClassLoader dexClassLoader = new DexClassLoader(dexFilePath, dexFile.getAbsolutePath(), null, getClassLoader());
findViewById(R.id.open_plug_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Class mClass = dexClassLoader.loadClass("com.example.liuhailong.idynamic.DynamicTest");
Log.e(TAG,"mClass的默認classLoader:"+mClass.getClassLoader());
Object dynamicTest = mClass.newInstance();
Method mMethod = mClass.getDeclaredMethod("showPluginWindow", new Class<?>[]{Context.class});
mMethod.setAccessible(true);
mMethod.invoke(dynamicTest, MainActivity.this);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
運行:
點擊就會自動加載:
日誌: