在实战开发中,混合开发很常见,本篇文章讲解在混合开发中的各种问题。比如:Flutter Activity内存泄漏问题,多引擎通信问题。
截止目前本篇文章使用flutter3.35.4+Android Studio 2025.2.2.8
一. 创建flutter模块
在原有项目基础上,创建flutter module,步骤如下:



flutter_lib创建完成后,目录结构如下:

开始集成flutter_lib如下步骤:

如果遇到下边错误,在settings.gradle中进行这样修改:
kotlin
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
改为:
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)

这是最终的配置:
kotlin
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven {
setUrl("https://maven.aliyun.com/repository/public")
}
maven {
setUrl("https://storage.googleapis.com/download.flutter.io")
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
maven {
setUrl("https://maven.aliyun.com/repository/public")
}
maven {
setUrl("https://storage.googleapis.com/download.flutter.io")
}
}
}
rootProject.name = "android_flutter"
include(":app")
val filePath = "$settingsDir/flutter_lib/.android/include_flutter.groovy"
apply(from = File(filePath))
include(":flutter_lib")
二. Android原生端打开一个Flutter页面
配置flutter SDK
kotlin
implementation(project(":flutter"))
配置flutterEngine
kotlin
val flutterEngine = FlutterEngine(this)
// 启动 Flutter(默认 main())
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// 缓存 engine,供 Activity 使用
FlutterEngineCache
.getInstance()
.put("flutter_engine", flutterEngine)
跳转Flutter页面
kotlin
val intent = FlutterActivity
.withCachedEngine("flutter_engine")
.build(context)
context.startActivity(intent)
kotlin
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:exported="false"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" />
以上步骤就是简单的打开一个Flutter页面,Flutter Activity作为一个载体加载页面。
三. 混合开发FlutterActivity 引起的内存泄漏问题
3.1 内存泄漏产生的原因分析

问题描述:当我打开一个FlutterActivity页面后返回页面,按理说Activity已经销毁了,但引用依然存在。
有没有可能是因为缓存了flutterEngine导致的,那我们修改一下代码,再试试。
kotlin
context.startActivity(
FlutterActivity
.withNewEngine()
.build(context)
)
然而还是不行,依然有内存泄漏:

通过以上内存泄漏分析,这些问题是Flutter SDK 本身存在的问题,比如我们针对LocalizationPlugin插件问题做个分析。
既然插件官方插件出现了问题,那么如何释放掉这种资源呢,我们看到存在这样一个activity(FlutterFragmentActivity),其中存在一个回调方法如下图:

这个 cleanUpFlutterEngine() 是干嘛的?
在宿主(Activity / Fragment)销毁前,用来清理你在 configureFlutterEngine() 里"手动绑定"的资源,因此我们是不是可以从这个回调中手动清理引用呢。
3.2 解决内存泄漏
创建一个Activity继承自FlutterFragmentActivity如下代码,解决的核心代码是通过反射找到setLocalizationPlugin 手动释放资源。
kotlin
package com.example.android_flutter
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.localization.LocalizationPlugin
/**
* 自定义FlutterActivity
*/
class CommonFlutterActivity : FlutterFragmentActivity() {
override fun cleanUpFlutterEngine(flutterEngine: FlutterEngine) {
super.cleanUpFlutterEngine(flutterEngine)
try {
// 获取 FlutterEngine 类的 flutterJNI 字段
val flutterJniField = FlutterEngine::class.java.getDeclaredField("flutterJNI")
// 设置字段可访问
flutterJniField.isAccessible = true
// 获取 flutterJNI 实例
val flutterJNI = flutterJniField.get(flutterEngine)
// 获取 FlutterJNI 类的 setLocalizationPlugin 方法
val setLocalizationPluginMethod =
flutterJNI.javaClass.getMethod(
"setLocalizationPlugin",
LocalizationPlugin::class.java
);
// 调用 setLocalizationPlugin 方法并传入 null
setLocalizationPluginMethod.invoke(flutterJNI, null)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
四.混合开发 打开Flutter指定页面(路由跳转)
4.1 关键代码
先说Android端 代码:
创建入口引擎
kotlin
flutterEngineGroup!!.createAndRunEngine(
appContext,
DartEntrypoint.createDefault(),
initRoute
)
复用上边的CommonFlutterActivity,指定引擎,这样就把入口引擎和activity绑定一起了。
kotlin
override fun provideFlutterEngine(context: Context): FlutterEngine? {
val engine = FlutterUtil.createFlutterEngine(getRouter())
if (engine != null) return engine
return super.provideFlutterEngine(context)
}
上边的代码通过flutterEngineGroup创建一个engine,initRoute是传入的路由名字。
代码解读,flutterEngineGroup作用:
flutterEngineGroup用于在同一个 Android 应用中创建和管理多个独立的 Flutter 引擎实例,让多个 Flutter 页面/模块可以并行运行和共享资源。多个引擎共享同一个 Flutter 运行时环境,共享 Dart VM 和代码,共享 Flutter 插件注册,减少内存占用(相比每个页面创建新引擎)。
再说flutter端代码:
dart
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
initialRoute: "/",
home: Container(),
onGenerateRoute: AppRouter.onGenerateRoute,
navigatorObservers: [AppNavigatorObserver()],
);
}
}
这段代码就是通过initRoute字段传入的路由名字,从而指定跳转不同的页面。
dart
class AppRouter {
static Route<dynamic> onGenerateRoute(RouteSettings settings) {
final uri = Uri.parse(settings.name ?? '/');
final path = uri.path;
final queryParams = uri.queryParameters;
debugPrint("queryParams=$queryParams");
switch (path) {
case '/page_a':
return _page(const APage(), settings);
case '/page_b':
return _page(const BPage(), settings);
default:
return _page(Container(), settings);
}
}
static MaterialPageRoute _page(Widget page, RouteSettings settings) {
return MaterialPageRoute(builder: (_) => page, settings: settings);
}
}
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class AppNavigatorObserver extends NavigatorObserver {
@override
void didPop(Route route, Route? previousRoute) {
super.didPop(route, previousRoute);
if (previousRoute?.isFirst ?? false) {
SystemNavigator.pop();
}
}
}
4.2 完整代码
kotlin
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
FlutterUtil.init(this)
}
}
kotlin
package com.example.android_flutter
import android.content.Context
import android.content.Intent
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineGroup
import io.flutter.embedding.engine.FlutterEngineGroupCache
import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint
object FlutterUtil {
const val INIT_ROUTER = "initRouter"
const val PAGE_A = "/page_a"
const val PAGE_B = "/page_b"
const val FLUTTER_ENGINE_GROUP = "com.flutter.engine.group"
@Volatile
private var flutterEngineGroup: FlutterEngineGroup? = null
private lateinit var appContext: Context
fun init(context: Context) {
if (flutterEngineGroup != null) return
synchronized(this) {
if (flutterEngineGroup != null) return
appContext = context.applicationContext
flutterEngineGroup =
FlutterEngineGroupCache.getInstance()[FLUTTER_ENGINE_GROUP]
?: FlutterEngineGroup(appContext).also {
FlutterEngineGroupCache
.getInstance()
.put(FLUTTER_ENGINE_GROUP, it)
}
// 这段代码是个小技巧,创建个空engine可以让页面加载更快,因为flutterEngineGroup中会进行引擎复用。
// 使用引擎组,当你第二次再创建引擎时,引擎组内部会内存复用,速度极快,不然打开一个页面会很慢。
flutterEngineGroup!!.createAndRunEngine(
context.applicationContext,
DartEntrypoint.createDefault(),
"/"
)
}
}
/**
* Create and run a new FlutterEngine with initialRoute.
* Engine lifecycle should be managed by the caller (Activity / Fragment).
*/
fun createFlutterEngine(initRoute: String): FlutterEngine? {
return flutterEngineGroup!!.createAndRunEngine(
appContext,
DartEntrypoint.createDefault(),
initRoute
)
}
/**
* 跳转flutter指定页面,支持传入参数
*/
fun startFlutterActivity(context: Context, pageRoute: String, map: Map<String, Any>? = null) {
val intent = Intent(context, CommonFlutterActivity::class.java)
intent.putExtra(INIT_ROUTER, pageRoute)
if (map != null) {
intent.putExtra(INIT_PARAM, Gson().toJson(map))
}
context.startActivity(intent)
}
}
kotlin
package com.example.android_flutter
import android.content.Context
import android.os.Bundle
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.localization.LocalizationPlugin
/**
* 自定义FlutterActivity
*/
class CommonFlutterActivity : FlutterFragmentActivity() {
private var initRouter: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initRouter = intent.getStringExtra(FlutterUtil.INIT_ROUTER)
}
override fun provideFlutterEngine(context: Context): FlutterEngine? {
val engine = FlutterUtil.createFlutterEngine(getRouter())
if (engine != null) return engine
return super.provideFlutterEngine(context)
}
private fun getRouter(): String {
return initRouter ?: "/"
}
override fun cleanUpFlutterEngine(flutterEngine: FlutterEngine) {
super.cleanUpFlutterEngine(flutterEngine)
try {
// 获取 FlutterEngine 类的 flutterJNI 字段
val flutterJniField = FlutterEngine::class.java.getDeclaredField("flutterJNI")
// 设置字段可访问
flutterJniField.isAccessible = true
// 获取 flutterJNI 实例
val flutterJNI = flutterJniField.get(flutterEngine)
// 获取 FlutterJNI 类的 setLocalizationPlugin 方法
val setLocalizationPluginMethod =
flutterJNI.javaClass.getMethod(
"setLocalizationPlugin",
LocalizationPlugin::class.java
);
// 调用 setLocalizationPlugin 方法并传入 null
setLocalizationPluginMethod.invoke(flutterJNI, null)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
kotlin
package com.example.android_flutter
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.android_flutter.ui.theme.Android_flutterTheme
import io.flutter.embedding.android.FlutterFragment
import io.flutter.embedding.android.RenderMode
import io.flutter.embedding.android.TransparencyMode
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Android_flutterTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
val context = LocalContext.current
Column(modifier = modifier) {
Text(text = "Hello $name!")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
FlutterUtil.startFlutterActivity(context, FlutterUtil.PAGE_A)
}) {
Text("打开Flutter页面A")
}
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
FlutterUtil.startFlutterActivity(context, FlutterUtil.PAGE_B)
}) {
Text("打开Flutter页面B")
}
}
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
Android_flutterTheme {
Greeting("Android")
}
}
五.混合开发 多入口并行 多引擎数据传递
5.1 页面布局如图所示:我的页面嵌入了一个flutter Fragment。

flutterFragment的生成核心代码:
kotlin
private fun createFlutterFragment(): CommonFlutterFragment {
return FlutterFragment.NewEngineInGroupFragmentBuilder(
CommonFlutterFragment::class.java,
FLUTTER_ENGINE_GROUP
)
.initialRoute(PAGE_HOME)
.renderMode(RenderMode.texture)
.transparencyMode(TransparencyMode.transparent)
.shouldAttachEngineToActivity(true)
.build()
}
5.2 多引擎数据传递
现在有个问题,如果我用startFlutterActivity方式打开了一个页面,那么我在flutter端如何把数据传递给CommonFlutterFragment(我的页面)。
你可能会问,这两个页面代码都是flutter,直接传递不就行了,答案是不行的。因为这两个页面是在原生端使用FlutterEngine创建的,每个FlutterEngine是线程独立的。
要解决这个问题,也很简单,我们为每个FlutterEngine别分创建MethodChannel,把数据先传到原生端,在去传到相关的Channel。
关键代码如下 :
kotlin
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// 定义你的name,和flutter端使用同一个
val methodChannel =
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"CommonFlutterActivity"
)
methodChannel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
// 假如在这里收到数据后,需要传给HomePage,注意这里需要传给homeChannel,homeChannel就是在CommonFlutterFragment中创建的channel。
// homeChannel.invokeMethod("fromFlutter", "fromFlutter")
if (call.method.equals(FlutterUtil.METHOD_CHANNEL_NAME)) {
FlutterUtil.getHomeMethodChannel()?.invokeMethod(
FlutterUtil.METHOD_CHANNEL_NAME,
call.arguments
)
}
}
}