Android 项目接入Flutter
一、官方方案
创建Android项目
如果你已经有Android项目,可以直接使用。这里我们先创建一个空的android项目来模拟已有的项目,取名叫TestFlutter。
创建Flutter模块
在Android工程目录同级目录下执行命令
flutter create -t module flutter_module
上面的命令会创建一个flutter的项目模块,在flutter文件夹中有一个.android的隐藏文件夹,里面包裹了一个安卓库的工程模块。
可以尝试用Gradle编译这个库,但这不是必须的步骤:
$ cd .android/$ ./gradlew flutter:assembleDebug
编译后会在.android/Flutter/build/outputs/aar/路径下产生flutter-debug.aar的文件。
将Flutter模块作为依赖添加到主项目
打开你的Android工程的setting.gradle文件,添加如下代码:
include ':app' setBinding(new Binding([gradle: this])) evaluate(new File( settingsDir.parentFile, 'flutter_module/.android/include_flutter.groovy' ))
这几行代码的意思就是将你刚才创建的那个module作为android模块引入到Android工程中。
点击同步完成后,到你app目录的build.gradle文件把依赖加上:
dependencies { implementation project(':flutter')}
再次同步完成就已经将Flutter添加到了你的项目了。接下来就可以开始混合开发了。
遇到问题总结
1、比如,我们不在Android工程的同级目录去flutter create -t module my_flutter会怎么样,我尝试了,只需要对路径加上你工程目录名即可,这么写
setBinding(new Binding([gradle: this])) evaluate(new File( settingsDir.parentFile, '你工程目录名/flutter_module/.android/include_flutter.groovy' ))
2、release 混淆问题
-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.** { *; }
二、闲鱼方案
通过Android studio 新建Flutter Application项目
在命令行输入命令flutter build apk
会编译生成apk文件,位于build/app/outputs/apk/release/文件夹下。
这个apk里的产物实际上是在Android的app/build.gradle构建代码里引入了Flutter的构建代码。
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
通过阅读flutter构建源码我们发现在构建apk文件的时候,会将需要的文件构建到apk中。
1.assets文件夹
assets文件夹下面有flutter_assets文件夹、flutter_shared文件夹、isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data、vm_snapshot_instr文件。
flutter_assets里是flutter工程产生的assets文件
flutter_shared里是封装在flutter.jar里面的处理字符编码的ICU库
isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data、vm_snapshot_instr为特定平台的数据和指令
2.lib文件夹
lib文件夹下是特定平台(arm或者x86)的so文件。
flutter在Android平台下会默认生成arm-v7架构的的so库,flutter.gradle源码中会根据target-platform属性判断平台动态生成对应的so,官方注释目前flutter只支持在debug模式下生成x86的so。
提取aar
上面通过编译命令得到了apk,那想要打包成aar,理论上只要把app/build.gradle中的apply plugin: 'com.android.application'修改为apply plugin: 'com.android.library',同时删除applicationId "com.shanbay.flutterapp"再次执行flutter build apk命令,便可以得到app-release.aar文件。
或者进入android文件目录下,执行命令 ./gradlew assembleRelease 也可以编译得到app-release.aar
集成到现有项目
我们将得到的aar文件集成到现有的Android工程中使用,但是打开flutter页面却闪退了,同时flutter报出了error,错误是说aar里面缺少icudtl.dat文件。
解压缩aar查看文件结构,可以发现其中的问题。
image.png在aar文件夹下的assets里面缺少了flutter_shared文件夹,icudtl.dat文件正是在该文件夹里面,也就是说flutter.gradle在编译流程中并没有将icudtl.dat文件打进aar包里面,这一点从flutter库的issue里面得到了证实,我们的办法是将apk里面得到的flutter_shared文件夹手动copy到flutter工程中,再次编译aar,这样就可以得到有icudtl.dat的aar文件。再次集成到Android项目中便可以成功运行,不会产生错误。
总结
这个方案需要两个步骤,第一步是先编译成apk取得icudtl.dat文件放入到工程中,第二步修改apply plugin: 'com.android.library'再次编译取得aar。
如果Flutter项目引用了path_provider、shared_preferences 需要将这两个jar包导入aar中不然会提示找不到这两个jar包的PathProviderPlugin、SharedPreferencesPlugin
image.png在app的build.gradle 里面添加代码
implementation fileTree(dir: 'libs', include: ['*.jar'])
为了方便打aar包和flutter项目运行,我们可以设置一个变量来控制
在android目录下的gradle.properties里面添加BUILD_MODE=aar,src目录下新建一个文件夹里面放置AndroidManifest.xml
image.png在app的build.gradle中添加以下代码
image.png image.png image.png image.png之后需要打aar包的时候,只需要在gradle.properties中修改BUILD_MODE为aar就可以用来打aar包,修改为flutter就是正常的flutter编译模式
build.gradle 相关代码
def localProperties = new Properties()def localPropertiesFile = rootProject.file('local.properties')if (localPropertiesFile.exists()) { localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) }}def flutterRoot = localProperties.getProperty('flutter.sdk')if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")}def flutterVersionCode = localProperties.getProperty('flutter.versionCode')if (flutterVersionCode == null) { flutterVersionCode = '1'}def flutterVersionName = localProperties.getProperty('flutter.versionName')if (flutterVersionName == null) { flutterVersionName = '1.0'}def BUILD_MODE = localProperties.getProperty('BUILD_MODE')def isRunAsFlutter = "flutter".equals(BUILD_MODE)if (isRunAsFlutter) { apply plugin: 'com.android.application'} else { apply plugin: 'com.android.library'}apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"android { compileSdkVersion 28 lintOptions { disable 'InvalidPackage' } defaultConfig { if (isRunAsFlutter) { applicationId "com.test.flutter" } minSdkVersion 16 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { signingConfig signingConfigs.debug } } sourceSets { main { def srcFile = isRunAsFlutter ? 'src/main/AndroidManifest.xml' : 'src/maven/AndroidManifest.xml' manifest.srcFile srcFile java { srcDir 'src/main/java' } res.srcDirs = ['src/main/res'] assets.srcDirs = ['src/main/assets'] jniLibs.srcDirs = ['src/main/jniLibs'] } } configurations.all { resolutionStrategy { cacheChangingModulesFor 0, 'seconds' } }}flutter { source '../..'}dependencies { if (!isRunAsFlutter) { implementation fileTree(dir: 'libs', include: ['*.jar']) } testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'com.android.support:design:28.0.0' implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support:support-v13:28.0.0' implementation 'com.android.support:support-annotations:28.0.0'}
参考:
Flutter在混合项目中的构建和集成
Flutter混合开发和动态更新的探索历程 Android版
更多相关文章
- Android完整的app项目
- Eclipse下Android工程无法自动产生R文件解决
- React Native嵌入Android原生项目中
- android 使用Yasea和ijkplayer集成到自己项目中遇到的问题和优化
- [置顶] Android中资源文件的详解和android中的单位介绍
- android(drawable文件夹)图片适配
- 开源项目之Android Calender(日历组件)