现有的热修正结构许多,尤以AndFix 和Tinker比较多

详细的完成办法和项目引证能够参考网络上的文章,今天就不谈,也不是主要目的

今天就来讨论,如何手写一个热修正的功用

关于简单的项目,不想集成其他修正结构的SDK,也不想用第三方平台,仅仅紧迫修正一些bug 还是挺便利的

言归正传,假如一个或多个类出现bug,导致了崩溃或许数据显示异常,假如修正呢,假如了解jvm dalvik 类的加载机制,就会清楚的了解 ClassLoader的 双亲托付机制 就能够经过这个

什么是双亲托付机制

  1. 当时ClassLoader首先从自己现已加载的类中查询是否此类现已加载,假如现已加载则直接回来本来现已加载的类。 每个类加载器都有自己的加载缓存,当一个类被加载了今后就会放入缓存,等下次加载的时分就能够直接回来了。
  2. 当时classLoader的缓存中没有找到被加载的类的时分,托付父类加载器去加载,父类加载器采用同样的策略,首先检查自己的缓存,然后托付父类的父类去加载,一直到bootstrp ClassLoader.
  3. 当一切的父类加载器都没有加载的时分,再由当时的类加载器加载,并将其放入它自己的缓存中,以便下次有加载恳求的时分直接回来。

突破口来了,看1(假如现已加载则直接回来本来现已加载的类) 关于同一个类,假如先加载修正的类,当后续在加载未修正的类的时分,直接回来修正的类,这样bug不就处理了吗?

Nice ,多看源码和jvm 许多问题能够从framework和底层去处理

话不多说,提出了处理办法,下面着手去完成

public class InitActivity extends FragmentActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //这儿默许在SD卡根目录,实际开发过程中能够把dex文件放在服务器,在发动页下载后加载进来
        //第二次进入的时分能够依据目录下是否现已下载过,处理,防止从头下载
        //最后依据当时app版别下载不同的修正dex包 等等一系列处理
        String dexFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/fix.dex";
        DexFile dexFile = null;
        try {
            dexFile = DexFile.loadDex(dexFilePath, null, Context.MODE_PRIVATE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        patchDex(dexFile);
        startActivity(new Intent(this, MainActivity.class));
    }
    /**
     * 修正过程,能够放在发动页,这样在等候的过程中,网络下载修正dex文件
     *
     * @param dexFile
     */
    public void patchDex(DexFile dexFile) {
        if (dexFile == null) return;
        Enumeration<String> enumeration = dexFile.entries();
        String className;
        //遍历dexFile中的类
        while (enumeration.hasMoreElements()) {
            className = enumeration.nextElement();
            //加载修正后的类,只能修正当时Activity后加载类(能够放入Application中履行)
            dexFile.loadClass(className, getClassLoader());
        }
    }
}

办法很简单在发动页,或许Application中提早加载有bug的类

这儿写的很简单,仅仅展现中心代码,实际开发过程中,dex包下载的网络恳求,据当时app版别下载不同的修正dex,文件存在的时分能够在Application中先加载一次,发动页就不用加载,等等,一系列优化和判别处理,这儿就不过多说明,详细一些处理看github上的代码

###ok 代码都了解了,这个 fix.dex 文件哪里来的呢 了解Android apk生成的小伙伴都知道了,跳过这个过程,不明白的小伙伴持续往下看

上面的InitActivitystartActivity(new Intent(this, MainActivity.class)); 发动了一个MainActivity 看看我的MainActivity

public class MainActivity extends FragmentActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //0不能做被除数,这儿会报ArithmeticException异常
        Toast.makeText(this, "成果" + 10 / 0, Toast.LENGTH_LONG).show();
    }
}

哎呀不小心,写了一个bug 0 咋能做除数呢,app现已上线了,这儿必崩啊,咋办 不要急,依照以下过程:

  1. 咱们要修正这个类MainActivity,先把bug处理
 Toast.makeText(this, "成果" + 10 / 2, Toast.LENGTH_LONG).show();
  1. 把修正类生成.class文件(能够先run一次,之后在 build/intermediates/javac/debug/classes/com开的的文件夹,找到生成的class文件,也能够经过javac 命令行生成,也能够经过右边的gradle Task生成)
    Android 手写热修复dex
  2. 把修正类.class文件 打包成dex (其他.class删去,只保存修正类) 翻开cmd命令行,输入下面命令
D:\Android\sdk\build-tools\28.0.3\dx.bat --dex --output C:\Users\pei\Desktop\dx\fix.dex C:\Users\pei\Desktop\dx\

D:\Android\sdk 为自己sdk目录 28.0.3build-tools版别,能够依据自己现已下载的版别更换 后边两个目录分别是生成.dex文件目录,和.class文件目录

牢记 .class文件的目录必须是包名一样的,我的目录是 C:\Users\pei\Desktop\dx\com\pei\test\MainActivity.class,不然会报 class name does not match path

  1. 这样dx文件夹下就会生成fix.dex文件了,把fix.dex放进手机根目录试试吧

再次翻开App,完美Toast 成果5,完美处理

总结

  1. 修正办法要在bug类之前履行
  2. 合适少量bug,太多bug影响性能
  3. 现在只能修正类,不能修正资源文件
  4. 现在只能适配单dex的项目,多dex的项目因为当时类和一切的引证类在同一个dex会 当时类被打上CLASS_ISPREVERIFIED标记,被打上这个标记的类不能引证其他dex中的类,不然就会报错 处理办法是在构造办法里引证一个单独的dex中的类,这样不符合规矩就不会被标记了