Android 集成 Flutter Module
Flutter官方介绍的是,Flutter 可以作为 Gradle 子项目源码或者 AAR 嵌入到现有的 Android 应用程序中。反正现在就我一个人开发,出于方便,就直接导入子项目源码了,编写完代码直接运行即可。
1. Android Studio导入
先介绍下使用Android Studio导入过程,直接贴图:
我是已经提前创建好了Flutter工程,所以用的是 Import Flutter Module
,选择已有Flutter工程,点击Finish。
2. gradle编码导入
除了使用AS导入,还可以直接通过修改代码进行集成,AS导入其实就是生成以下代码:
首先,把Flutter 模块作为子项目添加到宿主应用中,在 settings.gradle
中:
include ':app'
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'my_flutter/.android/include_flutter.groovy' // new
))
my_flutter
是Flutter工程的文件夹名称,便于管理,建议Android项目和Flutter项目放在同一目录下。
然后,在你的应用中build.gradle
引入对 Flutter 模块的依赖:
dependencies {
implementation project(':flutter')
}
提醒一下:
Flutter 当前仅支持 为 x86_64
,armeabi-v7a
和 arm64-v8a
构建预编(AOT)的库。
android {
//...
defaultConfig {
ndk {
// Filter for architectures supported by Flutter.
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
}
}
}
至此,Android集成Flutter Module结束。
Android 使用 Flutter
刚开始参照Flutter中文网文档,使用FlutterEngine
和FlutterEngineCache
来创建引擎,对于嵌套多Flutter页面交互通信并不友好,使用麻烦,效果不佳。后来改用闲鱼的FlutterBoost,虽然解决了多Flutter嵌入和通信的问题,但是总体来说,还是有很多问题,再加上Flutter
版本更新迭代也太频繁,使用FlutterBoost
侵入性太强,后期维护成本高,所以也没有投入使用,只是写一些练练手。
官方宣称在 Flutter 2.0.0 发布之前,FlutterEngine
的多个实例和相关的 UI 可以同时启动,但是每个实例都有明显的延迟和固定的内存占用。多个 Flutter 实例在以下场景有优势:
- 集成了 Flutter 界面的应用,其位置并不在路由栈的叶子节点上,且其可能是混合路由栈,即 native -> Flutter -> native -> Flutter。
- 多个 Flutter view 同时集成在同一个页面上,且同时显示。 Flutter 2.0.0 大幅减少了额外的 Flutter 引擎的内存占用,从 Android 上 约 19MB,iOS 上 约 13MB,降至 约 180kB。将固定成本减少了约 99% 后,您可以更自由地将多个 Flutter 集成至您的应用。
Flutter 2.0.0 版本以后,Flutter引入了FlutterEngineGroup
,总体看下来,对于Native
接入Flutter
友好了许多,相关的一些技术方案也成熟了许多。刚好最近有一些全新的需求,借此机会,把Flutter接入到项目中。
关于FlutterEngine
、FlutterEngineCache
和FlutterBoost使用方式就不列举了,只梳理一下我使用FlutterEngineGroup
的方式以及使用过程中遇到的一些问题,目前我的使用方式就是上文所述的混合路由栈的模式,native -> Flutter -> native -> Flutter。代码参考官方demo multiple_flutters 来写的,根据个人需求代码有所改动,以下内容为个人工作记录,仅供参考。
1. 初始化FlutterEngineGroup
首先在全局Application中:实例化一个FlutterEngineGroup
class WorkApplication : Application() {
lateinit var engineGroup: FlutterEngineGroup
//...
override fun onCreate() {
super.onCreate()
//...
// 创建FlutterEngineGroup对象
engineGroup = FlutterEngineGroup(this)
}
}
2. 创建FlutterEngine
工具类
封装一个创建FlutterEngine
的工具类,官方demo中工具类中包含了MethodChannel
,由于我项目中与原生通信使用的是pigeon
,所以我就只保留了创建FlutterEngine
相关代码。
/// kotlin
object FlutterEngineManager {
fun flutterEngine(context: Context, engineId: String, entryPoint: String): FlutterEngine {
// 1. 从缓存中获取FlutterEngine
var engine = FlutterEngineCache.getInstance().get(engineId)
if (engine == null) {
// 如果缓存中没有FlutterEngine
// 1. 新建FlutterEngine,执行的入口函数是entryPoint
val app = context.applicationContext as WorkApplication
val dartEntrypoint = DartExecutor.DartEntrypoint(FlutterInjector.instance().flutterLoader().findAppBundlePath(), entryPoint)
engine = app.engineGroup.createAndRunEngine(context, dartEntrypoint)
// 2. 存入缓存
FlutterEngineCache.getInstance().put(engineId, engine)
}
return engine!!
}
}
///dart
@pragma('vm:entry-point')
void topMain() => runApp(MyApp());
上述代码中,engineId
为缓存的key,entryPoint
参数所对应的就是dart
中的topMain
函数,首先使用DartExecutor.DartEntrypoint
函数根据Flutter工程中定义的entryPoint
创建出一个DartEntrypoint
对象,然后就可以使用engineGroup
创建出对应的FlutterEngine
。
3. 在Android中使用FlutterActivity
通过上述方式创建好FlutterEngine
,现在就可以在原生App中去渲染编写好的Flutter UI
,参考官方Demo,通过继承FlutterActivity
来创建一个用于渲染Flutter UI
的Activity
@Route(path = ARouterPath.FLUTTER_ACTIVITY)
class SingleFlutterActivity : FlutterActivity(){
//Flutter Module 对应页面
private var entryPoint:String = ""
override fun provideFlutterEngine(context: Context): FlutterEngine {
entryPoint = intent.getStringExtra("entryPoint")?:""
return FlutterEngineManager.flutterEngine(this,entryPoint)
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
}
}
继承FlutterActivity
然后override
父类的provideFlutterEngine
方法,这样就完成了Flutter页面整个嵌入Android Activity中了。
4. 在Android中使用FlutterView
除了在Android中创建一个FlutterActivity
当做一个单独的页面,还可以在现有页面中的Android View
中加入一个FlutterView
,实现一个页面中同时存在Android View
和FlutterView
的效果。
同样的FlutterView
的使用也封装了一个工具类,比较官方Demo也根据我个人习惯做了一下调整:
class FlutterViewManager(private val context: Context, private val engine: FlutterEngine) : LifecycleObserver{
private var flutterView: FlutterView? = null
private var activity: ComponentActivity? = null
fun attachToActivity(activity: ComponentActivity,flutterView: FlutterView) {
this.activity = activity
this.flutterView = flutterView
engine.activityControlSurface.attachToActivity(activity, activity.lifecycle)
flutterView.attachToFlutterEngine(engine)
activity.lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private fun resumeActivity() {
if (activity != null) {
engine.lifecycleChannel.appIsResumed()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
private fun pauseActivity() {
if (activity != null) {
engine.lifecycleChannel.appIsInactive()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private fun stopActivity() {
if (activity != null) {
engine.lifecycleChannel.appIsPaused()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
private fun destroyActivity() {
if (activity != null) {
engine.activityControlSurface.detachFromActivity()
engine.lifecycleChannel.appIsDetached();
}
flutterView?.detachFromFlutterEngine()
activity?.lifecycle?.removeObserver(this)
activity = null
flutterView = null
}
fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (activity != null && flutterView != null) {
engine.activityControlSurface.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (activity != null && flutterView != null) {
engine.activityControlSurface.onActivityResult(requestCode, resultCode, data);
}
}
fun onUserLeaveHint() {
if (activity != null && flutterView != null) {
engine.activityControlSurface.onUserLeaveHint();
}
}
}
上述方法,其实核心就是通过调用flutterView.attachToFlutterEngine(engine)
把Flutter UI
渲染到Android中的FlutterView
上,engine
的创建还是参考上述介绍FlutterActivity
的方式创建,余下的代码是通过 Jetpack Lifecycle
绑定engine
和Activity
的生命周期,和定义一下其他业务场景下所使用的方法。
然后是在Activity中添加FlutterView的使用方式:
private lateinit var flutterEngine: FlutterEngine
private lateinit var flutterViewManager :FlutterViewManager
private lateinit var flutterView : FlutterView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
flutterEngine = FlutterEngineManager.flutterEngine(mContext,"category")
flutterView = FlutterView(mContext)
flutterViewManager = FlutterViewManager(mContext,flutterEngine)
flutterViewManager.attachToActivity(mActivity,flutterView)
binding.frameLayout.addView(flutterView)
}
首先,先使用封装的FlutterEngineManager
创建要添加到页面的flutterEngine
,然后实例化flutterView
、flutterViewManager
,使用flutterViewManager
把flutterEngine
渲染到flutterView
上,再把flutterView
添加到Android原生的View中去。
在使用过程中,遇到了一个当页面中添加了两个或对个FlutterView时,Flutter中的SafeArea
不生效的问题,百度搜索了一番,找到的解决方案是:
//解决 多个FlutterView SafeArea不生效
flutterView.requestApplyInsets()
暂时项目中还没有使用到FlutterFragment
,虽然尝试写过一些代码去使用,但因为在一些业务场景有些问题,所以没有正式使用到项目中,暂时先不记录了,哪天用过之后再加吧!