美文网首页优化
Flutter 接安卓热修复的坑

Flutter 接安卓热修复的坑

作者: 机智的黑猫 | 来源:发表于2019-10-09 15:05 被阅读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

    相关文章

      网友评论

        本文标题:Flutter 接安卓热修复的坑

        本文链接:https://www.haomeiwen.com/subject/rrwoectx.html