1.Android studio : 关闭 Instant run
Instant run 多次build的apk里会出现多个.dex
2.dex分包:mutildex
1)。 implementation 'com.android.support:multidex:1.0.1'
2)。android {
defaultConfig {
multiDexEnabled true
3)。 buildTypes {
release {
multiDexKeepFile file('dex.keep')指定要生成的文件(com.example.map.testhotfix/MainActivity.class)
4).public class App extends MultiDexApplication
@Override
protected void attachBaseContext(Context base) {
MultiDex.install(base);
super.attachBaseContext(base);
}
3.Classloader
加载应用程序的dex
public class PathClassLoader extends BaseDexClassLoader
加载指定的dex,(必须在应用程序的目录下)
public class DexClassLoader extends BaseDexClassLoader
BaseDexClassLoader->如何加载ClassBaseDexClassLoader
方法--->Class findClass(String name) 得到Class
常量--->private final DexPathList pathList;
DexPathList
方法 --->findClass
常量---> dexElements[](List of dex/resource (class path) elements.应用class集合) 数组中for循环找到返回
4.解决方法
把修改的class插入到dexElement 有问题的前
1).修改后rebuild
2).使用SDK 中的dx.bat 生成修改类的dex
添加dx.bat 路径到环境变量:E:\WorkRes\ASSDK\build-tools\23.0.2
3).准备classes
把要生成dex的class文件放到(随便一个文件夹下)E:\WorkRes\application\apk\hotfix\dex\com.xxx.xxx\MainActivity.class(全路径:com.xxx.xxx)
4).生成修改后的dex
dos命令 使用dx.bat 生成 classes2.dex ,先定位到 E:\WorkRes\application\apk\hotfix\dex
output=( dex文件的输出路径 ) 后面的路径class来源
dx --dex --output= E:\WorkRes\application\apk\hotfix\dex\classes2.dex E:\WorkRes\application\apk\hotfix\dex
5.通过代码把新的dex添加到项目中
1). 外部存储的dex转到应用内。
//当前应用目录下,存放修改后的dex
File fileDir = getDir(ProCst.DEX_DIR, Context.MODE_PRIVATE);
//创建一个应用内的文件目录
String name ="classes2.dex";
//从sd卡取得 classes2.dex
File dexFile =new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + name);
String filePath = fileDir.getAbsoluteFile() + File.separator + name;
File file =new File(filePath);
if (file.exists())
file.delete();
//把SD 卡里的dex 转到应用内目录下
InputStream is =null;
FileOutputStream os =null;
is =new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + name);
int len =0;
byte[] buffer =new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer,0, len);
}
File f =new File(filePath);
FixDexUtils.loadFixeDes(this);
2).得到两个 ClassLoader 的 pathList
// 1.加载应用的dex
PathClassLoader pathLoader = (PathClassLoader) context.getClassLoader();
// 2.加载修复的dex
for (File dex : loadeDex) {
DexClassLoader classLoader =new DexClassLoader(
dex.getAbsolutePath(),//
fopt.getAbsolutePath(),//类加载器目录
null,
pathLoader
);
反射
getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"),"pathList");
private static Object getField(Object obj, Class cl, String field)throws Exception {
Field loadField = cl.getDeclaredField(field);
loadField.setAccessible(true);
return loadField.get(obj);
}
3).从而得到 pathList里的 element[]进行合并
private static Object combineArray(Object arrayLhs, Object arrayRhs) {
// 得到数组类型
Class localClass = arrayLhs.getClass().getComponentType();
int i = Array.getLength(arrayLhs);
// 得到合并后长度
int j = i + Array.getLength(arrayRhs);
// 新建一个数据用于合并
Object result = Array.newInstance(localClass, j);
for (int k =0; k < j; k++) {
if (k < i) {//DexClassLoader 中的放于top
Array.set(result, k, Array.get(arrayLhs, k));
}else {
Array.set(result, k, Array.get(arrayRhs, k - i));
}
}
return result;
}
4).合并得到的dexElements 设置到PathClassLoader中的pathList里
setField(pathList, pathList.getClass(),"dexElements", dexElement);
private static void setField(Object obj, Class cl, String field, Object value)throws Exception {
Field loadField = cl.getDeclaredField(field);
loadField.setAccessible(true);
loadField.set(obj, value);
}
项目地址