• 阿里 AndFix 热修复框架简介


    阿里AndFix热修复框架简介



    热修复原理:

    Android类加载机制

    Android的类加载器分为两种,PathClassLoader和DexClassLoader,两者都继承自BaseDexClassLoader

    PathClassLoader代码位于libcoredalviksrcmainJavadalviksystemPathClassLoader.java 
    DexClassLoader代码位于libcoredalviksrcmainjavadalviksystemDexClassLoader.java 
    BaseDexClassLoader代码位于libcoredalviksrcmainjavadalviksystemBaseDexClassLoader.java

    PathClassLoader: 用来加载系统类和应用类

    DexClassLoader :  用来加载jar、apk、dex文件.加载jar、apk也是最终抽取里面的Dex文件进行加载

    看下PathClassLoader代码

    1. public class PathClassLoader extends BaseDexClassLoader {
    2. public PathClassLoader(String dexPath, ClassLoader parent) {
    3. super(dexPath, null, null, parent);
    4. }
    5. public PathClassLoader(String dexPath, String libraryPath,
    6. ClassLoader parent) {
    7. super(dexPath, null, libraryPath, parent);
    8. }
    9. }
    DexClassLoader代码
    1. public class DexClassLoader extends BaseDexClassLoader {
    2. public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) {
    3. super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    4. }
    5. }
    两个ClassLoader就两三行代码,只是调用了父类的构造函数.
    1. public class BaseDexClassLoader extends ClassLoader {
    2. private final DexPathList pathList;
    3. public BaseDexClassLoader(String dexPath, File optimizedDirectory,
    4. String libraryPath, ClassLoader parent) {
    5. super(parent);
    6. this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    7. }
    8. @Override
    9. protected Class<?> findClass(String name) throws ClassNotFoundException {
    10. List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
    11. Class c = pathList.findClass(name, suppressedExceptions);
    12. if (c == null) {
    13. ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class "" + name + "" on path: " + pathList);
    14. for (Throwable t : suppressedExceptions) {
    15. cnfe.addSuppressed(t);
    16. }
    17. throw cnfe;
    18. }
    19. return c;
    20. }
    在BaseDexClassLoader 构造函数中创建一个DexPathList类的实例,这个DexPathList的构造函数会创建一个dexElements 数组

    1. public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) {
    2. ...
    3. this.definingContext = definingContext;
    4. ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
    5. //创建一个数组
    6. this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
    7. ...
    8. }

    然后BaseDexClassLoader 重写了findClass方法,调用了pathList.findClass,跳到DexPathList类中.

    1. /* package */final class DexPathList {
    2. ...
    3. public Class findClass(String name, List<Throwable> suppressed) {
    4. //遍历该数组
    5. for (Element element : dexElements) {
    6. //初始化DexFile
    7. DexFile dex = element.dexFile;
    8. if (dex != null) {
    9. //调用DexFile类的loadClassBinaryName方法返回Class实例
    10. Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
    11. if (clazz != null) {
    12. return clazz;
    13. }
    14. }
    15. }
    16. return null;
    17. }
    18. ...
    19. }
    会遍历这个数组,然后初始化DexFile,如果DexFile不为空那么调用DexFile类的loadClassBinaryName方法返回Class实例. 
    归纳上面的话就是:ClassLoader会遍历这个数组,然后加载这个数组中的dex文件. 
    而ClassLoader在加载到正确的类之后,就不会再去加载有Bug的那个类了,我们把这个正确的类放在Dex文件中,让这个Dex文件排在dexElements数组前面即可.


    热修复框架AndFix的使用

    AndFix:阿里开源的热修复框架AndFix,AndFix是 “Android Hot-Fix”的缩写。它支持Android 2.3到6.0版本,并且支持arm与X86系统架构的设备。完美支持Dalvik与ART的Runtime。AndFix 的补丁文件是以 .apatch 结尾的文件。

    我这是用eclipse写的Demo.

    1.把AndFix抽取成library依赖的形式

    这里写图片描述

    2.新建一个AndFixDemo项目,依赖AndFix这个library

    2.1 新建一个MyApplication继承Application

    public class MyApplication extends Application {
    
        private static final String TAG = "MyApplication";
    
        /**
         * apatch文件
         */
        private static final String APATCH_PATH = "/Dennis.apatch";
    
        private PatchManager mPatchManager;
    
        @Override
        public void onCreate() {
            super.onCreate();
            // 初始化
            mPatchManager = new PatchManager(this);
            mPatchManager.init("1.0"); // 版本号
    
            // 加载 apatch
            mPatchManager.loadPatch();
    
            //apatch文件的目录
            String patchFileString = Environment.getExternalStorageDirectory().getAbsolutePath() + APATCH_PATH;
            File apatchPath = new File(patchFileString);
    
            if (apatchPath.exists()) {
                Log.i(TAG, "补丁文件存在");
                try {
                    //添加apatch文件
                    mPatchManager.addPatch(patchFileString);
                } catch (IOException e) {
                    Log.i(TAG, "打补丁出错了");
                    e.printStackTrace();
                }
            } else {
                Log.i(TAG, "补丁文件不存在");
            }
    
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    实际当中肯定是通过网络接口下载apatch文件,我这里为了方便演示就放在了SD卡根目录

    2.2

    在MainActivity用一个按钮弹出吐司,上面是有Bug的代码,下面是修正后的代码

    这里写图片描述

    这里写图片描述

    分别打包成Bug.apk和NoBug.apk

    这里写图片描述

    2.3 . 然后要用到一个生成补丁的工具apkpatch 

    这里写图片描述

    _MACOSX是给OSX系统用的 
    .bat是给window系统用的

    我用得是.bat

    把之前生成的Bug.apkNoBug.apk,还有打包所使用的keystore文件放到apkpatch-1.0.3目录下 
    打开cmd,进入到apkpatch-1.0.3目录下,输入如下指令

    apkpatch.bat -f NoBug.apk -t Bug.apk -o Dennis -k keystore -p 111111 -a 111111 -e 111111

    每个参数含义如下

    -f 新版本的apk 
    -t 旧版本的apk 
    -o 输出apatch文件的文件夹,可以随意命名 
    -k 打包的keystore文件名 
    -p keystore的密码 
    -a keystore 用户别名 
    -e keystore 用户别名的密码

    这里写图片描述

    如果出现add modified …….就表示成功了,去apkpatch-1.0.3目录看下,新增了Dennis目录

    这里写图片描述

    这里写图片描述

    我把这个文件改为Dennis.apatch

    2.4

    手机装上Bug.apk运行起来

    这里写图片描述

    然后把Dennis.apatch 放到SD卡根目录,退出app,再进入,按下按钮

    这里写图片描述

    最后附上Demo还有apk和apatch 文件



























  • 相关阅读:
    javascript学习6
    javascript学习5
    javascript学习4
    javaccript学习3
    javaccript学习2
    javaccript学习1
    C++ 线性表实现
    深入解析策略模式(转)
    CentOS7安装MySQL
    万能媒体播放器 PotPlayer
  • 原文地址:https://www.cnblogs.com/jiaoxiake/p/6534348.html
Copyright © 2020-2023  润新知