性能优化16_热更新(编辑中)

Android性能优化汇总
热修复

  1. 阿里系:DeXposed。andfix
    从底层C的二进制来入手的。

  2. 腾讯系:tinker
    Java类加载机制来入手的。

一 什么是热修复?

一般的bug修复,都是等下一个版本解决,然后发布新的apk。
热修复: 可以直接在客户已经安装的程序当中修复bug。bug一般会出现在某个类的某个方法地方。我们需要动态地将客户手机里面的apk里面的某个类给替换成我们已经修复好的类。

二 原理

  • java的类加载机制:
    安卓系统通过classLoader加载dex文件。
public class PathClassLoader extends BaseDexClassLoader {
用来加载应用程序的dex

public class DexClassLoader extends BaseDexClassLoader {
可以加载指定的某个dex文件。(限制:必须要在应用程序的目录下面)
  • 系统通过BaseDexClassLoader类 中的DexPathList,再通过DexPathList类中的Element[] dexElements,找到对应的第一个java类,从而进行类的加载;所以,我们只需把我们修改后的dex文件元素利用反射技术,插入到dexElements数组的中去,并在数组的前面。这样,程序在运行时,会先拿到我们修复的类,运行无异常,从而实现了修复

  • 安卓java文件最终打包生产dex文件,我们修改bug后的java文件要编译成dex文件,然后去“替换”已经存在的dex文件中对应的那个类

  • 通过tools文件夹下工具可以将java文件打包生产dex文件

三 实现方式

  • 修改后的java文件,编译生成dex文件
  • io流读入文件到应用中
  • classLoader加载文件获取加载的dex文件中对应的元素
  • 将加载进来的元素合并到已经存在的DexPathList的Element[] dexElements 中去

四 主要的dex文件加载代码

public class FixDexUtils {
	private static HashSet loadedDex = new HashSet();
	
	static{
		loadedDex.clear();
	}

	public static void loadFixedDex(Context context){
		if(context == null){
			return ;
		}
		//遍历所有的修复的dex
		File fileDir = context.getDir(MyConstants.DEX_DIR,Context.MODE_PRIVATE);
		File[] listFiles = fileDir.listFiles();
		for(File file:listFiles){
			if(file.getName().startsWith("classes")&&file.getName().endsWith(".dex")){
				loadedDex.add(file);//存入集合
			}
		}
		//dex合并之前的dex
		doDexInject(context,fileDir,loadedDex);
	}

	private static void setField(Object obj,Class cl, String field, Object value) throws Exception {
		Field localField = cl.getDeclaredField(field);
		localField.setAccessible(true);
		localField.set(obj,value);
	}

	private static void doDexInject(final Context appContext, File filesDir,HashSet loadedDex) {
		String optimizeDir = filesDir.getAbsolutePath()+File.separator+"opt_dex";
		File fopt = new File(optimizeDir);
		if(!fopt.exists()){
			fopt.mkdirs();
		}
		//1.加载应用程序的dex
		try {
			PathClassLoader pathLoader = (PathClassLoader) appContext.getClassLoader();

			for (File dex : loadedDex) {
				//2.加载指定的修复的dex文件。
				DexClassLoader classLoader = new DexClassLoader(
						dex.getAbsolutePath(),//String dexPath,
						fopt.getAbsolutePath(),//String optimizedDirectory,
						null,//String libraryPath,
						pathLoader//ClassLoader parent
				);
				//3.合并
				Object dexObj = getPathList(classLoader);
				Object pathObj = getPathList(pathLoader);
				Object mDexElementsList = getDexElements(dexObj);
				Object pathDexElementsList = getDexElements(pathObj);
				//合并完成
				Object dexElements = combineArray(mDexElementsList,pathDexElementsList);
				//重写给PathList里面的lement[] dexElements;赋值
				Object pathList = getPathList(pathLoader);
				setField(pathList,pathList.getClass(),"dexElements",dexElements);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
    }

	private static Object getField(Object obj, Class cl, String field)
			throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
		Field localField = cl.getDeclaredField(field);
		localField.setAccessible(true);
		return localField.get(obj);
	}
	private static Object getPathList(Object baseDexClassLoader) throws Exception {
			return getField(baseDexClassLoader,Class.forName("dalvik.system.BaseDexClassLoader"),"pathList");
	}

	private static Object getDexElements(Object obj) throws Exception {
			return getField(obj,obj.getClass(),"dexElements");
	}

	/**
	 * 两个数组合并
	 * @param arrayLhs
	 * @param arrayRhs
     * @return
     */
	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) {
				Array.set(result, k, Array.get(arrayLhs, k));
			} else {
				Array.set(result, k, Array.get(arrayRhs, k - i));
			}
		}
		return result;
	}
//	[12345] [9876]
//	[9876  12345]

}

五 使用

1 Activity中报错

MyTestClass 报错了,a= 0,a不能作为分母

public class MyTestClass {
	public  void testFix(Context context){
		int i = 10;
		int a = 0;
		Toast.makeText(context, "shit:"+i/a, Toast.LENGTH_SHORT).show();
	}
}

Activity中使用了MyTestClass

public class Lsn16Activity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.lsn16_acitivy);
    }

    public void test(View v) {
        MyTestClass myTestClass = new MyTestClass();
        myTestClass.testFix(this);
    }
  }

2 定义一个修复的方法

public void fix(View v) {
        //目录:/data/data/packageName/odex
        File fileDir = getDir(MyConstants.DEX_DIR, Context.MODE_PRIVATE);
        //往该目录下面放置我们修复好的dex文件。
        String name = "classes2.dex";
        String filePath = fileDir.getAbsolutePath() + File.separator + name;
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
        }
        //搬家:把下载好的在SD卡里面的修复了的classes2.dex搬到应用目录filePath
        InputStream is = null;
        FileOutputStream os = null;
        try {
            is = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + 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 重写成功", Toast.LENGTH_SHORT).show();
            }
            //热修复
            FixDexUtils.loadFixedDex(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3 修改报错的地方

public class MyTestClass {
	public  void testFix(Context context){
		int i = 10;
		int a = 1;
		Toast.makeText(context, "shit:"+i/a, Toast.LENGTH_SHORT).show();
	}
}

4 编译MyTestClass.class 生成dex文件

5 加载dex文件

实际运用中通过网络请求,从服务器中获取dex文件,这里用点击事件fix()方法代替

6 点击修复,重新运行,发现bug修复了

六 Demo

Lsn16Activity

你可能感兴趣的:(Android性能优化,热修复,安卓热修复,dex文件,PathClassLoader,安卓在线修复bug)