介绍原理的文章有很多,我就不再多说,直奔主题:
一、生成dex文件(源码下载地址:https://download.csdn.net/download/wanggangyanqing/18658862)
1.新建module实现需要动态加载的模块,我以简单的获取UUID为例:
class DexTest {
public String getUUID() {
String uuid = UUID.randomUUID().toString(); //获取UUID并转化为String对象
uuid = uuid.replace("-", ""); //因为UUID本身为32位只是生成时多了“-”,所以将它们去点就可
Log.e("DEX", "uuid" + uuid);
return uuid;
}
}
2.打包含dex文件的jar包
①在module的build中添加:
task againMakeJar(type: Copy) {
delete 'libs/dextest.jar' //删除之前的旧jar包
from('build/intermediates/aar_main_jar/release/') //从这个目录下取出默认jar包
into('libs/') //将jar包输出到指定目录下
include('classes.jar')
exclude('text/', 'BuildConfig.class', 'R.class')//去掉不需要打包的目录和文件
exclude { it.name.startsWith('R$') }//去掉R$开头的文件
rename('classes.jar', 'dextest.jar') //自定义jar包的名字
}
②双击gradle中againMackJar即可编译生成jar包:
3,将jar包转dex文件:
①将生成的jar包复制到SDK\build-tools\30.0.3中,cmd打开terminal执行dx命令:dx --dex --output=newdextest.jar dextest.jar,执行完生成newdextest.jar包。
②将newdextest.jar改成newdextest.zip并解压,得到的classes.dex文件即是我们需要的dex文件。
二、下载并执行dex文件(源码地址:https://download.csdn.net/download/wanggangyanqing/18658926)
1.从服务端下载dex文件并将其保存到App私有目录(我的dex下载路径:http://chuanglan-test.oss-cn-shanghai.aliyuncs.com/sdk/platform/sms_sdk/classes.dex)
String saveFilePath = null;//dex保存路径
File appPath = getFilesDir(); //获取app私有目录
saveFilePath = appPath + "dextest.dex";
String dexDownLoadUrl = "http://chuanglan-test.oss-cn-shanghai.aliyuncs.com/sdk/platform/sms_sdk/classes.dex";//下载链接
URL url = new URL(dexDownLoadUrl);//创建一个URL对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();//创建HttpURLConnection对象
conn.setConnectTimeout(10000);//设置连接超时
conn.setReadTimeout(10000);//设置读超时
conn.setRequestMethod("GET");//设置GET请求方式
int responseCode = conn.getResponseCode();//服务端响应码
if (responseCode == 200) {
byte[] bytes = new byte[1024];
int len = -1;
InputStream in = conn.getInputStream();//得到InputStream
//创建一个文件输出流,用指定的名字创建文件
FileOutputStream out = new FileOutputStream(saveFilePath, false);
//从InputStream当中读取数据,并写入
while ((len = in.read(bytes)) != -1) {
out.write(bytes, 0, len);
out.flush();
}
out.close();
in.close();
}
2.通过反射加载dex文件
private void loadDex(String saveFilePath) throws ClassNotFoundException {
DexClassLoader dexClassLoader = new DexClassLoader(saveFilePath, saveFilePath, null, getClassLoader());
Class<?> clz = dexClassLoader.loadClass("com.test.dexlib.DexTest");
Object instanceObject = getDecInstanceObject(clz);
Method method = getDecMethod(clz, "getUUID", null);
String uuid = invoke(method, instanceObject, null);
Log.e("DEX", "dex文件执行结果 UUID=" + uuid);
}
public static Object getDecInstanceObject(Class<?> aClass) {
Object instance = null;
if (null != aClass) {
try {
Constructor constructor = aClass.getDeclaredConstructor();
//打开禁用访问控制检查
constructor.setAccessible(true);
instance = constructor.newInstance();
} catch (Exception e) {
Log.e("DEX", "getDecInstanceObject e=" + e);
}
}
return instance;
}
public static Method getDecMethod(Class<?> aClass, String aMethod, Class<?>[] classType) {
Method method = null;
if (aClass != null) {
try {
method = aClass.getDeclaredMethod(aMethod, classType);
method.setAccessible(true);
} catch (Exception e) {
Log.e("DEX", "getDecMethod e=" + e);
}
}
return method;
}
public static String invoke(Method method, Object instanceObject, Object[] params) {
String result = null;
try {
if (method != null && instanceObject != null) {
result = (String) method.invoke(instanceObject, params);
}
} catch (Exception e) {
Log.e("DEX", "invoke e=" + e);
}
return result;
}
至此,生成dex文件并从服务端下载,然后在App中动态加载已下载的dex文件完整流程已经结束。