Tinker热修复核心实现
2019-06-16 本文已影响0人
禅座
预备工作
自己打好一个修复好的dex包,命名为out.dex,然后放大sd卡目录下
将out.dex拷贝到app的odex目录下
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void test(View v) {
Test test = new Test();
test.testFix(this);
}
public void fix(View v) {
fixBug();
}
//将修复好的out.dex拷贝到odex目录下
private void fixBug() {
File filesDir = this.getDir("odex", Context.MODE_PRIVATE);
String name = "out.dex";
String filePath = new File(filesDir, name).getAbsolutePath();
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
InputStream is = null;
FileOutputStream os = null;
try {
Log.i(TAG, "fixBug: " + new File(Environment.getExternalStorageDirectory(), name).getAbsolutePath());
is = new FileInputStream(new File(Environment.getExternalStorageDirectory(), name));
os = new FileOutputStream(filePath);
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
File f = new File(filePath);
if (f.exists()) {
Toast.makeText(this, "dex overwrite", Toast.LENGTH_SHORT).show();
}
FixManager.loadDex(this);
// FixDexUtils.loadFixedDex(this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
os.close();
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
将odex中的修复包设置到dexElements当中
public class FixManager {
public static final String DEX_DIR = "odex";
// 用于存储我们的修复包dex
private static HashSet<File> loadedDex = new HashSet<File>();
static{
loadedDex.clear();
}
public static void loadDex(Context context) {
if (context == null) {
return;
}
//获取app目录下的odex文件
File filesDir = context.getDir("odex", Context.MODE_PRIVATE);
//准备遍历odex下的文件
File[] listFiles=filesDir.listFiles();
for (File file : listFiles) {
//如果是classes或者是.dex结尾则是我们需要的修复包
if(file.getName().startsWith("classes")||file.getName().endsWith(".dex")){
Log.i("INFO", "dexName:"+file.getName());
loadedDex.add(file);
}
}
//定义一个历史文件
String optimizeDir = filesDir.getAbsolutePath() + File.separator + "opt_dex";
File fopt = new File(optimizeDir);
if (!fopt.exists()) {
fopt.mkdirs();
}
//开始将修复包dex设置到dexElements数组当中
for (File dex : loadedDex) {
//我们通过创建一个DexClassLoader 加载我们的out.dex,将其存入DexClassLoader中的Elements数组当中,这个数组和PathClassLoader中的Elements不是同一个
//我们后面要做的便是将这两个数组进行合并,然后重新设置到PathClassLoader当中
DexClassLoader classLoader = new DexClassLoader(dex.getAbsolutePath(), fopt.getAbsolutePath(), null, context.getClassLoader());
PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
try {
//-----------------------系统的ClassLoader------------------------------------
Class baseDexClazzLoader=Class.forName("dalvik.system.BaseDexClassLoader");
Field pathListFiled=baseDexClazzLoader.getDeclaredField("pathList");
pathListFiled.setAccessible(true);
Object pathListObject = pathListFiled.get(pathClassLoader);
Class systemPathClazz=pathListObject.getClass();
Field systemElementsField = systemPathClazz.getDeclaredField("dexElements");
systemElementsField.setAccessible(true);
Object systemElements=systemElementsField.get(pathListObject);
//------------------自己的ClassLoader--------------------------
Class myDexClazzLoader=Class.forName("dalvik.system.BaseDexClassLoader");
Field myPathListFiled=myDexClazzLoader.getDeclaredField("pathList");
myPathListFiled.setAccessible(true);
Object myPathListObject =myPathListFiled.get(classLoader);
Class myPathClazz=myPathListObject.getClass();
Field myElementsField = myPathClazz.getDeclaredField("dexElements");
myElementsField.setAccessible(true);
Object myElements=myElementsField.get(myPathListObject);
//------------------------融合-----------------------------
Class<?> sigleElementClazz = systemElements.getClass().getComponentType();
int systemLength = Array.getLength(systemElements);
int myLength = Array.getLength(myElements);
int newSystenLength = systemLength + myLength;
//生成一个新的 数组 类型为Element类型
Object newElementsArray = Array.newInstance(sigleElementClazz, newSystenLength);
for (int i = 0; i < newSystenLength; i++) {
if (i < myLength) {
Array.set(newElementsArray, i, Array.get(myElements, i));
}else {
Array.set(newElementsArray, i, Array.get(systemElements, i - myLength));
}
}
//---------------------------融合完毕,将新数组放到系统的PathLoad内部---------------------------------
Field elementsField=pathListObject.getClass().getDeclaredField("dexElements");;
elementsField.setAccessible(true);
elementsField.set(pathListObject,newElementsArray);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
因为逻辑不是很复杂,上述注释都已经标注,关于odex是什么,干什么用的可以google一下,这边不做说明.