优化

Flutter 接安卓热修复的坑

2019-10-09  本文已影响0人  机智的黑猫

主项目最近上了flutter,主项目编译正常,之后打patch准备tinker热修复灰度上线测试,结果运行到相关代码崩溃:
[ERROR:flutter/runtime/dart_vm_data.cc(19)] VM snapshot invalid and could not be inferred from settings.
[ERROR:flutter/runtime/dart_vm.cc(238)] Could not setup VM data to bootstrap the VM from.
[ERROR:flutter/runtime/dart_vm_lifecycle.cc(89)] Could not create Dart VM instance.
[FATAL:flutter/shell/common/shell.cc(218)] Check failed: vm. Must be able to initialize the VM

跟了下代码
flutter构造FlutterView之前会确认是否初始化,FlutterMain.ensureInitializationComplete方法。然后里面会把so文件的地址给到shellArgs里传入FlutterJNI。然而这里的地址并不是patch包里的地址,从而导致出错。

 public static void ensureInitializationComplete(@NonNull Context applicationContext, @Nullable String[] args) {
        if (!isRunningInRobolectricTest) {
            if (Looper.myLooper() != Looper.getMainLooper()) {
                throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
            } else if (sSettings == null) {
                throw new IllegalStateException("ensureInitializationComplete must be called after startInitialization");
            } else if (!sInitialized) {
                try {
                    if (sResourceExtractor != null) {
                        sResourceExtractor.waitForCompletion();
                    }

                    List<String> shellArgs = new ArrayList();
                    shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");
                    ApplicationInfo applicationInfo = getApplicationInfo(applicationContext);
                    shellArgs.add("--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + "libflutter.so");
                    if (args != null) {
                        Collections.addAll(shellArgs, args);
                    }

                    String kernelPath = null;
                    shellArgs.add("--aot-shared-library-name=" + sAotSharedLibraryName);
                    shellArgs.add("--aot-shared-library-name=" + applicationInfo.nativeLibraryDir + File.separator + sAotSharedLibraryName);
                    shellArgs.add("--cache-dir-path=" + PathUtils.getCacheDirectory(applicationContext));
                    if (sSettings.getLogTag() != null) {
                        shellArgs.add("--log-tag=" + sSettings.getLogTag());
                    }

                    String appStoragePath = PathUtils.getFilesDir(applicationContext);
                    String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
                    FlutterJNI.nativeInit(applicationContext, (String[])shellArgs.toArray(new String[0]), (String)kernelPath, appStoragePath, engineCachesPath);
                    sInitialized = true;
                } catch (Exception var7) {
                    Log.e("FlutterMain", "Flutter initialization failed.", var7);
                    throw new RuntimeException(var7);
                }
            }
        }
    }

这边我们可以通过修改flutter生成的代码或者使用hook等方式替换 List<String> shellArgs的add方法,把so文件的路径替换为tinker里寻找so文件的路径即可,其他热修复平台可以参考类似策略

fun add(list: MutableList<Any>, s: Any): Boolean {
    val string = s.toString()
    val match = """(--[a-z-]+=).*/(lib\w*\.so)""".toRegex().find(string) ?: return list.add(s)
    val prefix = match.groupValues[1]
    val libName = match.groupValues[2]
    return list.add(prefix + findLibraryPath(appContext, "lib/armeabi-v7a", libName))
}

    fun findLibraryPath(context: Context, relativePath: String, libName: String): String {
        val patchedPath = findPatchedLibraryPath(context, relativePath, libName)
        if (patchedPath != null) {
            return patchedPath
        }
        val applicationInfo = context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA)
        return applicationInfo.nativeLibraryDir + File.separator + getFullLibName(libName)
    }
 fun findPatchedLibraryPath(context: Context, relativePath: String, libName: String): String? {
        val tinker = Tinker.with(context)

        val relativeLibPath = "$relativePath/${getFullLibName(libName)}"

        if (tinker.isEnabledForNativeLib && tinker.isTinkerLoaded) {
            val loadResult = tinker.tinkerLoadResultIfPresent
            if (loadResult.libs == null) {
                return null
            }
            for (name in loadResult.libs.keys) {
                if (name != relativeLibPath) {
                    continue
                }
                val patchLibraryPath = loadResult.libraryDirectory.toString() + "/" + name
                val library = File(patchLibraryPath)
                if (!library.exists()) {
                    continue
                }
                //whether we check md5 when load
                val verifyMd5 = tinker.isTinkerLoadVerify
                if (verifyMd5 && !SharePatchFileUtil.verifyFileMd5(library, loadResult.libs[name])) {
                    tinker.loadReporter.onLoadFileMd5Mismatch(library, ShareConstants.TYPE_LIBRARY)
                } else {
                    return patchLibraryPath
                }
            }
        }

        return null
    }

    private fun getFullLibName(libName: String): String {
        var fullLibName = if (libName.startsWith("lib")) libName else "lib$libName"
        fullLibName = if (fullLibName.endsWith(".so")) fullLibName else "$fullLibName.so"
        return fullLibName
    }

总结,flutter模块更新会生成货修改对应的so文件,并且在创建flutterview的时候会通过类似命令行的形式传入so文件的地址,在热修复的case下需要手动替换so路径为对应patch里的so才可以正常work

上一篇下一篇

猜你喜欢

热点阅读