为了不走寻常路,文章是我集成Flutter过程遇到的问题,如果不想看我啰嗦,可以直接看文章Flutter-Android快速集成
Flutter项目,同事已经开发完成,现需要将Flutter项目集成到自有Android项目中。
我的目的是赶紧将项目跑起来…
我的目的是赶紧将项目跑起来…
我的目的是赶紧将项目跑起来...
自有项目采用多module形式。基本一个大功能一个module。项目结构如下:
-app
-coreLib
-shareLib
-mineLib
-flutterLib//这个是新建的,flutter将放在这里
复制代码
之前没接触过Flutter,所以补充下。看了官方文档以及一些博客和WIKI。有了大概了解之后,开始集成。
官方有个WIKI说如何集成:github.com/flutter/flu…
呃,说实话,“麻(wo)烦(hai)的(mei)一(xi)逼(kan)”,先不按这个来。
目标:最好是flutter项目以aar形式放到library中,这样开发起来,独立,效率。自有项目的代码管理和flutter代码管理区分。毕竟不是一拨人开发。(呃,其实我们团队就这么多人,这俩人开发安卓项目,这俩人开发flutter项目,一个团队的)
走你---项目结构如下:
第一坑来了,aar怎么来?
回到flutter项目中,flutter run --release直接生成了apk..我要aar,你给我apk有个毛用 先看下flutter项目目录
熟悉安卓的都知道,build目录里面东西多多啊
哈哈,果然,在Flutter/build/outputs/aar找到了,看下文件大小,5.9M,嗯,差不多。就你了… 放到项目中
至此,不算填坑,且往后看
第二坑:arm架构调整
我们自有项目只支持arm架构,和美团类似。而Flutter项目默认是arm-v8的,好么,第二坑来了。 那按照美团的技术博客中所说,解压、复制吧啦吧啦…
呃,有这么麻烦么,直接把libflutter.so放到工程里面不就行了?
这样不是很简单?按照美团的提示,找到相应目录$FLUTTER_ROOT/bin/cache/artifacts/engine
先看下目录里面有啥?
呃,这么多,看美团处理了3个arm目录的。我只需要一个就行呀,用哪个?就3个,一个一个的试吧 release?这个显而易见,你先来。进入目录,unzip flutter.jar
好,直接拿走,扔到flutterLib/libs/armeabi/下面。好,走起,跑起来。
项目跑起来了,跳到Flutter页面..
啪啪啪,打脸…
Abort message: '[FATAL:flutter/runtime/dart_vm.cc(415)] Error while initializing the Dart VM: Snapshot not compatible with the current VM configuration: the snapshot requires 'product no-type_checks no-asserts no-error_on_bad_type no-error_on_bad_override sync_async reify_generic_functions arm64' but the VM has 'product no-type_checks no-asserts no-error_on_bad_type no-error_on_bad_override sync_async reify_generic_functions arm-eabi softfp’
复制代码
这是特喵的啥啊,google一波
github.com/flutter/flu…
这个里面,也没说咋解决。 看意思是要arm64的? 难道不能用android-arm-release里面的so?要用android-arm64-release的?那试试
跑起来,跳到Flutter页面
啪啪啪,又打脸…
java.lang.UnsatisfiedLinkError: dlopen failed: "/data/app/com.letv.android.client-RzaguRHTMAAeoInec8Xl8g==/lib/arm/libflutter.so" is 64-bit instead of 32-bit
复制代码
这特喵又是啥?google又走一波 blog.csdn.net/u014316462/…
看到这一句:“网上分析了出现这种情况的原因,可能是混合使用了32bit和64bit的库文件”。
后面的我就没看... 呃,那就是还是要用android-arm-*的so,不是用android-arm64-*里面的。
等等,前面只试了android-arm-release下面so,还有其他两个arm的目录呢:android-arm android-arm-profile
那试下android-arm
这个下面so
跑起来,跳到Flutter页面
啪啪啪,脸肿了,艹
Error while initializing the Dart VM: JIT runtime cannot run a precompiled snapshot
复制代码
擦...这又是啥…
不过看样子,so的问题解决了。这应该是个新坑。
好,总结一下二坑。
二坑填坑:so可以直接用android-arm下面的so。
第三坑
顺着二坑继续分析
JIT runtime cannot run a precompiled snapshot
复制代码
JIT:在debug包的时候才会用JIT
precompiled snapshot:这个是release模式生成的。
这么一想,自有项目应该也用release才对
试试
打release包,安装
[FATAL:flutter/shell/platform/android/library_loader.cc(24)] Check failed: result.
复制代码
额,这个啥也看不出来啊
后来分析:应该是so库的问题,release模式应该用android-arm-release下面的so文件。另外如果项目需要混淆,需要把flutter相关的混淆加上。
#Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
复制代码
第三坑填坑
当遇到Check failed: result
时,应该检查so文件是否正常,另外混淆的话,把flutter的混淆加上。
其实到了这一步,我就停下来思考了
不按照官网指导来,是否真的可行?
平常开发过程中自有项目都是用debug模式,flutter项目用release还是用debug?用release模式有上面的问题,用debug模式?测试阶段换成release模式?这有点不灵活啊
第四坑
另外一个问题:我在集成过程中,遇到一个问题,现在复现不出来了(?)
大概意思是:
NoClassFound :PathProviderPlugin
复制代码
GeneratedPluginRegistrant
类中调用导致的
对比flutter-release.aar和flutter通过release跑起来的apk
aar确实少了好多东西
这时候需要看下flutter 的gradle文件了,看看都干了啥
填坑
从app下的build.gradle发现依赖一个flutter的library。
dependencies {
implementation project(':flutter')
implementation fileTree(dir: 'libs', include: ['*.jar'])
...
}
复制代码
这个flutter的module哪里来的?
看下settings.gradle文件
include ':app'
rootProject.name = 'android_generated'
setBinding(new Binding([gradle: this]))
evaluate(new File('include_flutter.groovy'))
复制代码
include_flutter.groovy是什么鬼?
目录下面找到这个文件
def scriptFile = getClass().protectionDomain.codeSource.location.path
def flutterProjectRoot = new File(scriptFile).parentFile.parentFile
gradle.include ':flutter'
gradle.project(':flutter').projectDir = new File(flutterProjectRoot, '.android/Flutter')
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
gradle.include ":$name"
gradle.project(":$name").projectDir = pluginDirectory
}
gradle.getGradle().projectsLoaded { g ->
g.rootProject.afterEvaluate { p ->
p.subprojects { sp ->
if (sp.name != 'flutter') {
sp.evaluationDependsOn(':flutter')
}
}
}
}
复制代码
下面分析一下include_flutter.groovy这个文件
def scriptFile = getClass().protectionDomain.codeSource.location.path
def flutterProjectRoot = new File(scriptFile).parentFile.parentFile
//这两行是确定文件路径
gradle.include ':flutter'
gradle.project(':flutter').projectDir = new File(flutterProjectRoot, '.android/Flutter')
// OK,声明flutter的module,并设置module路径,前面提到的flutter的module的问题解决了
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
// 读取.flutter-plugins文件,这里看下文件有啥
// path_provider=/Users/PanJiafang/.pub-cache/hosted/pub.flutter-io.cn/path_provider-0.4.1/
// sqflite=/Users/PanJiafang/.pub-cache/hosted/pub.flutter-io.cn/sqflite-0.13.0/
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
gradle.include ":$name"
gradle.project(":$name").projectDir = pluginDirectory
}
// 意思是根据前面读入的内容引入path_provider和sqflite两个库,库路径为xxxx/android/
gradle.getGradle().projectsLoaded { g ->
g.rootProject.afterEvaluate { p ->
p.subprojects { sp ->
if (sp.name != 'flutter') {
sp.evaluationDependsOn(':flutter')
}
}
}
}
复制代码
看下目录里有啥
OK,到这里,应该能明白Flutter集成是咋回事了。 flutter相关的东西都通过include_flutter.groovy处理。
那么填坑之旅也算完成了,集成Flutter可以这么做,请看文章:Flutter-Android快速集成
参考文献:
美团的 Flutter的原理及美团的实践
Flutter 官方WIKI