返回

混混开发之Android端Flutter热更新——Sophix篇(二)

Android

之前写了利用Tinker来进行Flutter的热更新,分析了原理以及实现步骤。按照Sophix热修复的接入步骤一步步接入完成,实验了一把,原生的改动修复,Flutter的改动并未修复。解开sophix-patch.jar包,里面有Flutter的补丁so包。Sophix和Tinker类似,原理上都是通过Native层的classloader加载补丁dex/so进行修复。补丁包里面不仅有Native层的so修复文件,还有Flutter的so修复文件。

但是直接使用Sophix的方式并不能修复Flutter的bug。Sophix提供了一个gradle插件,用来处理Android原生so的补丁。但是对于Flutter来说,so文件补丁有自己的规则。所以就需要修改gradle插件,来支持Flutter的so补丁。

修改Sophix

public class CustomDexAndSoCopyTask extends DexAndSoCopyTask {

    @Override
    protected Collection<File> computeSoFilePaths() {
        return super.computeSoFilePaths();
    }
}

通过修改computeSoFilePaths方法,加上如下代码,用来支持so文件的增量更新。

Collection<File> soFilePaths = Super.computeSoFilePaths();
soFilePaths.addAll(flutterCopySoFiles);

混混使用

@Override
protected Collection<File> getSoFiles() {
    Collection<File> soFiles = Super.getSoFiles();
    soFiles.addAll(flutterSoFiles);
    return soFiles;
}

Flutter端

public static void initSoLoader() {
        try {
            NativeLibrary.init("libapp.so", null);
        } catch (UnsatisfiedLinkError e) {
            Log.i(TAG, "Failed to load app so: " + e);
        }
        if (!Constants.isRelease) {
            try {
                NativeLibrary.init("libapp_debug.so", null);
            } catch (UnsatisfiedLinkError e) {
                Log.i(TAG, "Failed to load app debug so: " + e);
            }
        }
    }

具体接入步骤请查看Sophix官方文档:

https://github.com/alibaba/sophix/wiki/%E6%89%93%E5%85%A5%E6%96%87%E6%A1%A3

修改之后的Sophix也可以支持Flutter的so热更新。

原理

对于So的加载,android提供了自己的一套机制。

    public static void init(String[] args) {
        // 主加载器,用于加载framework层的so
        registerMainDexClassLoader(getSystemClassLoader(), args);
        // auxiliary加载器,用于加载app层的so
        registerAuxiliaryDexClassLoader(getInstrumentation(), args, createClassLoader());
    }

getInstrumentation方法里获取Instrumentation对象,再从Instrumentation对象中获取auxiliaryClassLoader。Instrumentation用来检测和监控应用程序的运行,它有一个成员变量mAuxiliaryClassLoader,用来加载dex和so。

主加载器和辅助加载器通过Dex和so的路径的设置,来加载so。

public static void registerMainDexClassLoader(ClassLoader mainClassLoader, String[] args) {
        int index = args.length - 2;
        if (args[index++].equals("--classpath")) {
            String s = args[index++];
            String sharedLibPath = "app_so";
            if (s.equals(Constants.DEX_BOOTSTRAP_CLASS_PATH)) {
                sharedLibPath = "dalvik/lib";
            }
            addSearchPath(mainClassLoader, sharedLibPath);
        }
    }

所以可以通过修改Instrumentation对象的auxiliaryClassLoader的so的搜索路径,来实现so的热更新。

总结

通过修改Sophix,可以支持Flutter的so热更新。原理是通过修改Instrumentation对象的auxiliaryClassLoader的so的搜索路径,来实现so的热更新。

优点

  • 实现简单,直接修改Sophix源码
  • 无需修改Flutter源码
  • 支持So的增量更新

缺点

  • 需要修改Sophix源码,影响后续Sophix版本升级