动态加载dex
动态加载dex
新建一个工程
新建一个类,在类中加一个方法,添加一个 TextView 调用
package com.example.myloaddex;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 调用
LoadDex.createView(this);
}
}MyLoadDex
package com.example.myloaddex;
import android.app.Activity;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.TextView;
public class LoadDex {
// 一个静态方法
public static void createView(Activity ac){
// 创建一个TextView
TextView tv= new TextView(ac);
// 创建布局设置参数
FrameLayout.LayoutParams params=new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
// 设置控件到顶点
params.topMargin=0;
// 设置控件的位置
params.gravity= Gravity.TOP|Gravity.CENTER_HORIZONTAL;
tv.setText("MyTestActivity Textview!");
// 添加TextView到Activity
ac.addContentView(tv,params);
}
}
反编译 apk,将新建的类的 smali 文件提取出来
单独将一个 smali 文件转成 dex 文件 E:\MyLoadDex\app\build\intermediates\javac\debug\classes
整个文件夹复制出来,因为要保留路径,不然会出错(版本不同路径可能不同,直接在文件夹下搜classes文件夹)
然后用./d2j-jar2dex.bat LoadDex.class -o test.dex命令将LoadDex.class文件转成test.dex
将 dex 文件放到 assets 文件夹中,assets自己右键新建
把原本的那个类代码删除,进文件夹直接删除文件夹里面的那个类文件就可以
写代码动态加载 dex,让程序正常跑起来
调用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1. 拷贝dex到安装目录下
String path = copyDex("test.dex");
// 2. 使用DexClassLoader,加载dex
DexClassLoader dex = loadDex(path);
// 3. 使用返回的dex对象,加载类,获取方法对象,调用方法
useClass(dex);
}
使用Java反射机制,动态加载dex,并调用
// 使用Java反射机制,动态加载dex,并调用
public void useClass(DexClassLoader dex) {
try {
// 获取加载的类信息
// 包名+工程名+类名
Class TestDex = dex.loadClass("com.bluelesson.app34_3.MyTestActivity");
// 参数为构造方法参数数组
// 获取成员方法
Method methodTest = TestDex.getDeclaredMethod("createView", // 方法名称
new Class[]{Activity.class}); // 方法参数类型数组
// 取消java访问检查,提高反射速度
methodTest.setAccessible(true);
// 调用方法,this指针传进去
methodTest.invoke(null, // 类对象
new Object[] { this }); // 方法参数数组
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
获得指定文件的dex加载器
// 获得指定文件的dex加载器
public DexClassLoader loadDex(String path) {
// 创建dex文件的类加载器,返回DexClassLoader对象
DexClassLoader dex = new DexClassLoader(
path, // dex路径
getCacheDir().toString(), // 优化之后的文件的路径
null, // 原生库路径
getClassLoader()); // 父类加载器
return dex;
}
拷贝指定的文件从assets目录中到/data/data/包名/files中
// 拷贝指定的文件从assets目录中到/data/data/包名/files中
String copyDex(String dexName) {
// 获取assets目录管理器
AssetManager as = getAssets();
// 合成路径
// getFilesDir返回的就是/data/data/包名/files
String path = getFilesDir() + File.separator + dexName; // File.separator 分隔符
Log.i("15pb-log", path);
try {
// 创建文件流
FileOutputStream out = new FileOutputStream(path);
// 打开文件
InputStream is = as.open(dexName);
// 循环读取文件,拷贝到对应路径
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
// 关闭文件
out.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
return "";
}
return path;
}