Flutter Native混编调研
0. 背景
由于公司现在有多个移动应用已经在线上运行,比如易销售、智慧仓库等等,虽然Flutter拥有一次编写多端使用的优势,但是推倒用Flutter重新实现一遍代价也比较大,最好的折衷方案是在现有App的基础上进行混合开发编译,在拥有了成熟的Flutter技术沉淀以后可以在新应用上快速接入使用,提升开发效率。
1. 混编接入方式
- 在宿主项目中增加ndk支持
android {
//...
defaultConfig {
ndk {
// Filter for architectures supported by Flutter.
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
}
}
}
-
添加Java8支持
android { //... compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 } }
-
然后使用Android Studio新建一个Flutter Module项目
- 在这个Flutter Module项目中进行相关业务开发
- 选择以下三种接入方式之一完成混合开发
1. 本地级联编辑
- 在宿主项目的setting.gradle中增加本地Flutter Module依赖(假设Flutter Module鱼宿主项目同级)
// Include the host app project.
include ':app' // assumed existing content
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'my_flutter/.android/include_flutter.groovy' // new
))
- 在宿主项目的app中的build.gradle中增加module依赖
dependencies {
implementation project(':flutter')
}
2. 本地Maven仓库依赖
在Flutter Module中运行
cd some/path/my_flutter
$ flutter build aar
C:\Users\chao.bai\AndroidStudioProjects\flutter-demos\native-module\flutter_module_pure>flutter build aar
Building with sound null safety
Running Gradle task 'assembleAarDebug'... 12.3s
√ Built build\host\outputs\repo.
Running Gradle task 'assembleAarProfile'... 29.6s
√ Built build\host\outputs\repo.
Running Gradle task 'assembleAarRelease'... 30.6s
√ Built build\host\outputs\repo.
Consuming the Module
1. Open <host>\app\build.gradle
2. Ensure you have the repositories configured, otherwise add them:
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
maven {
url 'C:\Users\chao.bai\AndroidStudioProjects\flutter-demos\native-module\flutter_module_pure\build\host\outputs\repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}
3. Make the host app depend on the Flutter module:
dependencies {
debugImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_debug:1.0'
profileImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_profile:1.0'
releaseImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_release:1.0'
}
4. Add the `profile` build type:
android {
buildTypes {
profile {
initWith debug
}
}
}
这个命令会生成一个本地仓库(初始位置为有your flutter module/build/host/outputs/repo)
build/host/outputs/repo
└── com
└── example
└── my_flutter
├── flutter_release
│ ├── 1.0
│ │ ├── flutter_release-1.0.aar
│ │ ├── flutter_release-1.0.aar.md5
│ │ ├── flutter_release-1.0.aar.sha1
│ │ ├── flutter_release-1.0.pom
│ │ ├── flutter_release-1.0.pom.md5
│ │ └── flutter_release-1.0.pom.sha1
│ ├── maven-metadata.xml
│ ├── maven-metadata.xml.md5
│ └── maven-metadata.xml.sha1
├── flutter_profile
│ ├── ...
└── flutter_debug
└── ...
为了依赖生成的AAR,宿主项目必须找到这些文件
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
maven {
url 'C:\Users\chao.bai\AndroidStudioProjects\flutter-demos\native-module\flutter_module_pure\build\host\outputs\repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}
dependencies {
debugImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_debug:1.0'
profileImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_profile:1.0'
releaseImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_release:1.0'
}
3. 远程Maven仓库依赖
远程仓库依赖可以使用fat-aar三方库将Flutter Module库一起打包生成aar,然后上传至公司maven仓库
2. Native调用Flutter页面
最终我们的目的就是需要在存在的应用上调起Flutter页面,一套代码跨端使用
- 对于Android应用,需要在宿主的清单文件中进行注册
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
-
在Application中初始化Flutter引擎
// 实例化 flutterEngine = new FlutterEngine(this); // 引擎预热,这么做可以在初次调用Flutter时有更快的相应速度 flutterEngine.getDartExecutor().executeDartEntrypoint( DartEntrypoint.createDefault() ); // 根据id缓存对应的引擎 FlutterEngineCache .getInstance() .put("my_engine_id", flutterEngine);
-
启动页面
这里使用Activity最为例子,Fragment以及View粒度的实现这里暂不做介绍
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
FlutterActivity.createDefaultIntent(currentActivity)
);
}
});