在AppCompatActivity中使用FlutterFragment与FlutterPlugin:如何确保二者调用时机的正确性
嘿,我之前在维护一个混合栈项目的时候,刚好碰到过和你几乎一模一样的场景——用AppCompatActivity搭壳,配合预加载的FlutterEngine和FlutterFragment做混合开发,还得保证Native和Flutter的双向通信时机绝对靠谱。结合你的限制(不能改FlutterFragmentActivity),给你整理几个亲测有效的方案:
一、先搞定:FlutterFragment发起插件调用前,确保插件已绑定到Engine
因为你用的是预加载FlutterEngine,核心思路就是把插件绑定的时机提前到Engine初始化阶段,而不是等FlutterFragment创建的时候。这样当Fragment关联这个预加载Engine时,插件早就完成绑定了。
具体操作步骤:
在Application里预加载Engine并绑定插件
把Engine的初始化和插件绑定放到Application的onCreate里,这样整个App启动时就完成了预加载,后面Fragment直接用就行:class MyApp : Application() { lateinit var preWarmedEngine: FlutterEngine override fun onCreate() { super.onCreate() // 预加载FlutterEngine,提前启动Dart虚拟机 preWarmedEngine = FlutterEngine(this) preWarmedEngine.dartExecutor.executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ) // 直接把你的自定义插件绑定到这个预加载的Engine上 MyCustomPlugin.registerWith(preWarmedEngine.dartExecutor.binaryMessenger) } }这么做的话,等你在Activity里创建FlutterFragment并关联这个Engine时,插件的
onAttachedToEngine早就执行完了,绝对就绪。等Flutter侧完全初始化后再发起调用
插件绑定好只是Native侧就绪,还要等Flutter端的页面、MethodChannel都初始化完成。可以通过FlutterFragment的生命周期+Dart空闲检查来确保时机:class MainActivity : AppCompatActivity() { private lateinit var flutterFragment: FlutterFragment override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 拿到预加载好的Engine val preWarmedEngine = (application as MyApp).preWarmedEngine // 创建关联预加载Engine的FlutterFragment flutterFragment = FlutterFragment.withCachedEngine(preWarmedEngine.id).build() // 把Fragment加到布局里 supportFragmentManager.beginTransaction() .add(R.id.flutter_container, flutterFragment) .commit() // 监听Fragment生命周期,等Flutter侧彻底就绪再发消息 flutterFragment.lifecycle.addObserver(object : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { super.onResume(owner) // waitForIdle()会等Dart侧所有初始化、渲染任务完成,此时通信绝对安全 flutterFragment.flutterEngine?.dartExecutor?.waitForIdle()?.thenAccept { // 这里就可以放心调用插件方法给Flutter发消息了 MyCustomPlugin.invokeFlutterMethod(/* 你的参数 */) } } }) } }
二、再解决:插件调用FlutterFragment逻辑前,确保Fragment已就绪
先聊你碰到的ActivityAware没触发的坑
你说ActivityAware的方法始终没调用?我当时也踩过这个雷!原因很直白:FlutterFragmentActivity内部会自动把Activity实例传递给已绑定的插件,但咱们用的普通AppCompatActivity没有这个内置逻辑,所以得手动补这个环节。
方案1:给插件手动传递Fragment/Activity引用
修改你的自定义插件,加个静态方法用来接收Fragment和Activity的引用,这样插件就能自己判断Fragment是否就绪:
class MyCustomPlugin : FlutterPlugin, MethodCallHandler { private var hostActivity: AppCompatActivity? = null private var targetFragment: FlutterFragment? = null private lateinit var channel: MethodChannel companion object { fun registerWith(messenger: BinaryMessenger): MyCustomPlugin { val channel = MethodChannel(messenger, "my_custom_channel") val plugin = MyCustomPlugin() channel.setMethodCallHandler(plugin) return plugin } // 给插件传递Fragment和Activity的引用 fun bindHost(plugin: MyCustomPlugin, fragment: FlutterFragment, activity: AppCompatActivity) { plugin.targetFragment = fragment plugin.hostActivity = activity } } override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(binding.binaryMessenger, "my_custom_channel") channel.setMethodCallHandler(this) } override fun onMethodCall(call: MethodCall, result: Result) { when (call.method) { "executeNativeFragmentLogic" -> { // 先检查Fragment是否真的就绪:已添加到Activity、上下文不为空 targetFragment?.takeIf { it.isAdded && it.context != null }?.let { fragment -> // 这里放心执行Fragment相关逻辑就行 fragment.doYourBusiness() result.success("操作成功") } ?: result.error("FRAGMENT_NOT_READY", "FlutterFragment还没绑定好", null) } // 其他方法处理... } } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) hostActivity = null targetFragment = null } }
然后在Activity里创建完Fragment后,把引用传递给插件:
// 在MainActivity的onCreate中,Fragment添加完成后 val plugin = MyCustomPlugin.registerWith(preWarmedEngine.dartExecutor.binaryMessenger) MyCustomPlugin.bindHost(plugin, flutterFragment, this)
方案2:插件主动询问Activity的Fragment状态
如果你不想修改插件的结构,也可以让插件通过MethodChannel先询问Activity:当前的FlutterFragment是否已就绪?
// 插件的onMethodCall中 call.method == "checkFragmentReady" -> { val activity = (context as? AppCompatActivity) val fragment = activity?.supportFragmentManager?.findFragmentById(R.id.flutter_container) as? FlutterFragment result.success(fragment != null && fragment.isAdded) }
然后Flutter侧先调用这个checkFragmentReady方法,得到肯定答复后再发起真正的逻辑调用。
三、最后划几个关键要点
- 预加载Engine时提前绑定插件:从根源上避免Fragment关联Engine时插件还没就绪的问题
- 用
waitForIdle()配合生命周期:确保Flutter侧的页面和Channel都初始化完成后再发消息 - 手动传递Fragment引用:绕开AppCompatActivity不支持ActivityAware的问题,让插件能自主判断Fragment状态
内容的提问来源于stack exchange,提问作者Dennis Anderson

