美文网首页Flutter学习
Flutter框架Android so库的一些实践

Flutter框架Android so库的一些实践

作者: 小鱼游儿 | 来源:发表于2019-10-10 16:47 被阅读0次

    学而思网校1对1的android工程是用flutter框架实现的,而android工程不可避免的要和cpu架构和so库打交道,在实践中,针对flutter框架对cpu架构的支持,我们也有一些经验在此总结。

    so库说明

    so库在android中,是使用c/c++代码编译出来的库文件,可以使用ndk调用,就是你在android代码中见到的native方法,具体的实现就在so库中。

    关于so库兼容性问题

    andorid中或多或少都会引用到第三方库,而很多第三方库中都有so的存在,不论是复制到项目中(如百度地图),或是gradle依赖(如个推)
    其中都涉及到了so库的相关问题,如果你选择的库是有所有cpu类型可选还好,如果不是,那么就需要自定义设置了。

    举个栗子

    你的app依赖两个库,分别是lib1,lib2

    lib1: arm64-v8a,armeabi-v7a
    lib2: armeabi-v7a

    那么当你运行在v7的手机上时,因为你的项目含有v7的so库,所以没有问题,可以跑起来如果,你运行在v8手机上,那么你的项目就会Crash, 为啥呢? 这就涉及到so对齐了

    so对齐

    简单来说,就是要有就必须都有,如果一个没有,那就一个都不要
    比如上面的例子,如果你是自己复制到项目下的,你需要删掉arm64-v8a的文件夹

    flutter经典问题couldn't find "libflutter.so"

    引发的原因

    导致这个问题出现的原因是因为我们在项目种使用了so库,或者项目中引用的三方sdk使用了so库。在引用so库时需要针对不同的cpu架构使用不同的.so文件。armeabi,armeabi-v7a,x86,arm64-v8a,大家通常会对这几个cpu架构进行适配。然而问题就出在现在Flutter在打包Apk时不能同引入arm32和arm64的libflutter.so。我门将打好包的Apk安装到arm64架构(默认打包会引入arm32)的手机上就出现了couldn't find libflutter.so这个异常。

    如何解决

    1. 针对arm32和arm64分别打包(flutter 提供了命令来之分别对arm32和arm64分别打包)

    flutter build apk --target-platform=android-arm32
    flutter build apk --target-platform=android-arm64

    我们用flutter build apk --target-platform=android-arm64打包apk,并在arm64cpu架构的手机安装运行,很好完美运行。但是当我们吧apk安装到32位时问题再次出现,原因就不再重复了。很显然到这里这个方式并不能解决这个问题,为了适配arm32和arm64我们需要分别打包,而国内大部分应用市场不能针对不同cpu架构上传不同的apk。

    1. 不对arm64做适配,打包时排除其他非arm32架构的so文件
      这时候有些朋友可能会又疑问,问题不就是因为打包时没有引入arm64的libflutter的so文件导致在arm64架构手机上出现“兼容”问题的么。arm64cpu架构是可以像下兼容的,简单点就是arm64架构的cpu可以使用arm32的.so文件。出现问题真正原因是我们在引用so库时(引用的三方库中引用了so库),对arm64做了兼容。这样就会导致运行时系统误以为我们的应用对所有的so库做了arm64架构的兼容,但是在打包时libflutter并没有引入arm64的版本。这就导致系统去寻找arm64版本的libflutter发现找不到。系统误会了我们,我们只能通过gradle在打包时排除其他非arm32架构的so文件来解除这个误会。
      好那如何排除其他非arm32架构的so文件。在app下的gradle文件加入如下代码
    buildTypes {
         release {
             ndk{
                 //这里其实我觉可以直接是用"armeabi-v7a",但国内几个大哥之前使用的都是"armeabi"
                 abiFilters "armeabi"
             }
         }
         debug {
             ndk {
                 //这里要加上,否则debug包会出问题,后面三个为可选,x86建议加上不然部分模拟器回报错
                 abiFilters "armeabi", ,"armeabi-v7a","arm64-v8a", "x86"
             }
         }
     }
    

    3.flutter 1.7.8+hotfix.2 以上版本已经支持flutter 64位了 直接打包即可.

    只支持64位:flutter build apk --target-platform android-arm64
    支持32位:flutter build apk --target-platform=android-arm
    同时支持64位和32位:flutter build apk

    工程加入x5内核引起的新问题

    经过上述文章方法的处理,我们的工程解决了在64位手机上,出现couldn't find "libflutter.so"引起的Crash问题,但是,当我们引入x5内核后,又出现了新的问题。
    这是我们在没有引入x5内核前工程支持的cpu架构配置,这个地方需要说明,如果不加入 "arm64-v8a"配置的话,flutter build apk命令虽然会将so库打入工程,但是平时开发时使用的flutter run apk命令并不会把so库打入工程。

    ndk {
                    abiFilters "armeabi" ,"armeabi-v7a", "arm64-v8a"
                }
    

    这种配置方式能保证flutter run和flutter build同时生效,不会引起程序因为找不到so库引起的Crash。
    但是当引入x5内核后,会出现如下错误

    X5 does not support the 64-bit mode to run

    x5官方解释是x5内核暂时不提供64位的so文件,在64位手机上需要让AP以32位模式运行,所以我们如果加入了 "arm64-v8a"的话,x5内核在64位手机无法启动。
    但是加入的话,又会引起flutter的so库找不见,解决办法如下:

    1. 修改flutter.gradle里面,关于flutter run的配置,使其支持把64位的so库打入工程
     String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter"
            flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile();
    
            if (useLocalEngine(project)) {
                String engineOutPath = project.property('localEngineOut')
                File engineOut = project.file(engineOutPath)
                if (!engineOut.isDirectory()) {
                    throw new GradleException('localEngineOut must point to a local engine build')
                }
                Path baseEnginePath = Paths.get(engineOut.absolutePath)
                flutterJar = baseEnginePath.resolve("flutter.jar").toFile()
                if (!flutterJar.isFile()) {
                    throw new GradleException("Local engine jar not found: $flutterJar")
                }
                localEngine = engineOut.name
                localEngineSrcPath = engineOut.parentFile.parent
                // The local engine is built for one of the build type.
                // However, we use the same engine for each of the build types.
                project.android.buildTypes.each {
                    addApiDependencies(project, it.name, project.files {
                        flutterJar
                    })
                }
            } 
    
    1. 修改构建脚本,在jenkens上构建工程时,动态修改cpu架构支持
     #andorid app build.gradle 工程配置文件地址
        # 删除 arm64-v8a,防止  hybrid 在某些手机上无法构建。
        androidAppGradlePath="$JENKINS_WORKSPACE/northstar_flutter/android/app/build.gradle"
        echo "App 中 app/build.gradle 配置文件地址是:${androidAppGradlePath}"
        sed -i 's/, "arm64-v8a"/ /' $androidAppGradlePath
        echo "已经将 arm64-v8a 从文件中剔除,文件路径是:$androidAppGradlePath"
    

    我们最终采用了方案2,因为每次更新flutter的版本,flutter.gradle文件都会被官方修改,尽量不动此文件为好。

    总结

    以上就是我们在学而思网校1对1android工程中对flutter框架支持so库做的一些实践和总结,随着flutter版本的不断更新,相信官方会对cpu架构支持越来越好。

    相关文章

      网友评论

        本文标题:Flutter框架Android so库的一些实践

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