- 本篇文章会使用相对最新的Android Studio(version 2022.3)和Flutter sdk(version 3.7.7)环境来实现在现有的Android项目中使用Flutter跟Android与Flutter模块通信。
一.在现有Android项目中使用Flutter
- Flutter中文文档-将Flutter集成到现有应用,本篇文章的重点是通信机制,这里只使用一种方式,但是由于准备工作(随着版本不断的更新,准备工作会存在变化)存在一定的细节问题,单独做一段写出来可以避免大家少走弯路。
1.集成Flutter模块
- 在现有的Android项目中使用Flutter,可以通过在Android项目的同级目录下使用命令的方式创建
flutter create -t module flutter_module
- 创建AS项目,在AS的setting.gradle中配置(下方的flutter_module为flutter模块的名称)
//将 Flutter 模块作为子项目添加到宿主应用的 settings.gradle 中:
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_module/.android/include_flutter.groovy'
))
//加这里的配置开发起来更方便,直接在一个工程下切换原生项目或Flutter module
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')
- 在app的build.gradle中添加依赖
//应用中引入对 Flutter 模块的依赖:
implementation project(':flutter')
- 此时,出现了一个报错,解决方案分为3个操作:org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin class ‘FlutterPlugin’,处理方案。
- 在setting.gradle中注释部分代码
//Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin class 'FlutterPlugin'.
//操作1:注释
//dependencyResolutionManagement {
// repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
// repositories {
// google()
// mavenCentral()
// }
//}
- 更改as根目录的grade.setting中的配置
//plugins {
// id 'com.android.application' version '8.1.0-alpha06' apply false
// id 'com.android.library' version '8.1.0-alpha06' apply false
// id 'org.jetbrains.kotlin.android' version '1.7.21' apply false
//}
plugins {
id 'com.android.application' version '7.4.0' apply false //操作3:更改version,对应的gradle可以改为7.5-bin
id 'com.android.library' version '7.4.0' apply false //操作3:更改version
id 'org.jetbrains.kotlin.android' version '1.7.21' apply false
}
//Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin class 'FlutterPlugin'.
//操作2:新增
allprojects {
repositories {
google()
jcenter()
}
}
2.原生代码调用Flutter Module
- 这里只讲解原生代码启动自定义的FlutterActivity实现类
2.1.定义FlutterAppActivity
class FlutterAppActivity : FlutterActivity() {
private var mInitParams: String? = null
companion object {
const val INIT_PARAMS = "initParams"
private var mtype = 0
fun start(context: Context, initParams: String = "", type: Int) {
mtype = type
val intent = Intent(context, FlutterAppActivity::class.java)
intent.putExtra(INIT_PARAMS, initParams)
context.startActivity(intent)
}
}
//优先级高于onCreate
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
mInitParams = intent.getStringExtra(INIT_PARAMS);
}
//重载该方法来传递初始化参数
override fun getInitialRoute(): String? {
return if (mInitParams == null) super.getInitialRoute() else mInitParams
}
}
2.2.native层代码
class MainActivity : AppCompatActivity() {
@SuppressLint("MissingInflatedId")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<View>(R.id.normal).setOnClickListener {
FlutterAppActivity.start(this@MainActivity,"打开普通页面的参数信息", type = 1)
}
findViewById<View>(R.id.btn1).setOnClickListener {
FlutterAppActivity.start(this@MainActivity, "BasicMessageChannel", 2)
}
findViewById<View>(R.id.btn2).setOnClickListener {
FlutterAppActivity.start(this@MainActivity, "MethodChannel", 3)
}
findViewById<View>(R.id.btn3).setOnClickListener {
FlutterAppActivity.start(this@MainActivity, "EventChannel", 4)
}
}
}
2.3.flutter模块的代码
import 'package:flutter/material.dart';
import 'dart:ui';
import 'basic_message_channel_page.dart';
import 'event_channel.dart';
import 'method_channel_page.dart';
//必须要使用window.defaultRouteName来获取从native层传递过来的参数
void main() => runApp(MyApp(window.defaultRouteName));
class MyApp extends StatelessWidget {
String initParam;
MyApp(this.initParam, {super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: selectPage(),
);
}
//为了看起来更清晰,分别用了四个page,页面打开后可以通过标题栏进行区分
Widget selectPage() {
switch (initParam) {
case "BasicMessageChannel":
return BasicMessageChannelPage();
case "MethodChannel":
return MethodChannelPage();
case "EventChannel":
return EventChannelPage();
default:
return MyHomePage();
}
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
//省略...
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("普通的flutter页面"),
),
//省略...
);
}
}
//BasicMessageChannelPage的标题栏信息:title: Text("native与flutter通信之BasicMessageChannel")
//MethodChannelPage的标题栏信息:title: Text("native与flutter通信之MethodChannelPage")
//EventChannelPage的标题栏信息:title: Text("native与flutter通信之EventChannel")
- 运行项目,现在可以从native页面分别打开Flutter模块的四个页面,准备工作已经完成,下面正式开始Android与Flutter的通信实现。
二.Android与Flutter的通信实现
- 具体的通信场景就不做介绍,Android与Flutter的通信依靠channel,具体的分类有三种
- BasicMessageChannel:用于传递字符串和半结构化的消息(双向);
- MethodChannel:用于传递方法调用(双向);
- EventChannel:用于事件流的发送(单向,Native端发起);
2.1.BasicMessageChannel方式
- Android端,封装BasicMessageChannel的管理类,该类主要的逻辑。
- 2.2.1.初始化BasicMessageChannel,同时注册处理的Handler(目的是处理来自Flutter的消息);
- 2.2.2.定义Android发送给Flutter消息的方法
- 具体细节请看代码
- BasicMessageChannel的管理类
class BasicMessageChannelManager private constructor(
messenger: BinaryMessenger
) : BasicMessageChannel.MessageHandler<String> {
private var messageChannel: BasicMessageChannel<String>
init {
messageChannel =
//参数1:消息信使
//参数2:channel的名字,唯一标识
//参数3:消息编码器,有多种不同类型的实现,这里通信只选择StringCodec
BasicMessageChannel(messenger, "BasicMessageChannelManager", StringCodec.INSTANCE)
// 注册处理的Handler,处理来自Flutter的消息
messageChannel.setMessageHandler(this)
}
//重写函数:接收Flutter Module传递过来的消息 并可以回应给Flutter Module
override fun onMessage(s: String?, reply: BasicMessageChannel.Reply<String>) {
println("Flutter回复给Android的消息:$s")
reply.reply("Android收到了 Flutter--->Android的消息,这次是回复操作") //可以通过reply进行回复
}
companion object {
fun register(
messenger: BinaryMessenger,
): BasicMessageChannelManager = BasicMessageChannelManager(messenger) //创建BasicMessageChannelManager实例
}
//native主动向Flutter Module发送消息 方式一 带回调
fun send(message: String, callback: BasicMessageChannel.Reply<String>) {
messageChannel.send(message, callback)
}
//native主动向Flutter Module发送消息 方式二 不带回调
fun send(message: String) {
messageChannel.send(message)
}
}
- FlutterAppActivity类
class FlutterAppActivity : FlutterActivity() {
private var mInitParams: String? = null
private var mBasicMessageChannelManager: BasicMessageChannelManager? = null
companion object {
const val INIT_PARAMS = "initParams"
private var mtype = 0
fun start(context: Context, initParams: String = "", type: Int) {
mtype = type
val intent = Intent(context, FlutterAppActivity::class.java)
intent.putExtra(INIT_PARAMS, initParams)
context.startActivity(intent)
}
}
//优先级高于onCreate
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
mInitParams = intent.getStringExtra(INIT_PARAMS);
if (mtype == 2) {
mBasicMessageChannelManager =
BasicMessageChannelManager.register(flutterEngine.dartExecutor.binaryMessenger)
}
}
//重载该方法来传递初始化参数
override fun getInitialRoute(): String? {
return if (mInitParams == null) super.getInitialRoute() else mInitParams
}
override fun onStart() {
super.onStart()
sendMessageToFlutter()
}
private fun sendMessageToFlutter() {
if (mtype == 2) {
mBasicMessageChannelManager!!.send("通过方式一BasicMessageChannel传递参数") { message: String? ->
println("message mtype == 2 $message")
}
}
}
}
- BasicMessageChannelPage类
class BasicMessageChannelPage extends StatefulWidget {
State<StatefulWidget> createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<BasicMessageChannelPage> {
// int _counter = 0;
_MyHomePageState();
void _incrementCounter() {
// setState(() {
// _counter++;
// });
// 主动向Android发送消息
_basicMessageChannel!.send("Flutter--->Android的消息2,这次是Flutter主动的。");
}
String _basicMessage = '';
BasicMessageChannel<String>? _basicMessageChannel;
void initState() {
super.initState();
_basicMessageChannel =
BasicMessageChannel('BasicMessageChannelManager', StringCodec());
//设置setMessageHandler,目的:为了能够接收来自Android的消息
//Future:向Android回传消息
_basicMessageChannel!.setMessageHandler((String? message) => Future<String>(() {
setState(() {
//Android --> Flutter
_basicMessage = 'Android--->Flutter的消息:' + message!;
});
return "Flutter--->Android的消息1:收到了。";
}));
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("native与flutter通信之BasicMessageChannel"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_basicMessage',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
2.2.MethodChannel方式
- 定义MethodChannel的管理类,主要逻辑同2.1类似
- MethodChannelManager代码
-
- 创建MethodChannel实例(传入channel name)
-
- 注册处理的Handler
-
- 调用Flutter端方法(两种方式,区别在于有无返回值)
-
- 根据Flutter的要求,调用Android方法
-
class MethodChannelManager private constructor(
private val messenger: BinaryMessenger
) : MethodChannel.MethodCallHandler {
// 3. 用于调用Flutter端方法 方式一 无返回值
// method为需调用的方法名
fun callMethod(method: String, o: Any) {
methodChannel.invokeMethod(method, o)
}
//用于调用Flutter端方法 方式一 有返回值
// method为需调用的方法名、返回值在result内
fun callMethod(method: String, o: Any, result: MethodChannel.Result) {
methodChannel.invokeMethod(method, o, result)
}
// 4. 复写onMethodCall():根据Flutter的要求,调用Android方法
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"callAndroidMethod" -> {
println("Flutter--->Android的消息 ${call.arguments}")
//返回结果给Dart
result.success("Android收到了 Flutter--->Android的消息,这次是回复操作")
}
else -> result.notImplemented()
}
}
private var methodChannel: MethodChannel
init {
// 1. 创建MethodChannel实例(传入channel name)
methodChannel = MethodChannel(messenger, "MethodChannelManager")
// 2. 注册处理的Handler
methodChannel.setMethodCallHandler(this)
}
companion object {
fun register(messenger: BinaryMessenger): MethodChannelManager =
MethodChannelManager(messenger)
}
}
- FlutterAppActivity 类
class FlutterAppActivity : FlutterActivity() {
private var mInitParams: String? = null
private var mMethodChannelManager: MethodChannelManager? = null
companion object {
const val INIT_PARAMS = "initParams"
private var mtype = 0
fun start(context: Context, initParams: String = "", type: Int) {
mtype = type
val intent = Intent(context, FlutterAppActivity::class.java)
intent.putExtra(INIT_PARAMS, initParams)
context.startActivity(intent)
}
}
//优先级高于onCreate
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
mInitParams = intent.getStringExtra(INIT_PARAMS);
if (mtype == 3) {
mMethodChannelManager =
MethodChannelManager.register(flutterEngine.dartExecutor.binaryMessenger)
}
}
//重载该方法来传递初始化参数
override fun getInitialRoute(): String? {
return if (mInitParams == null) super.getInitialRoute() else mInitParams
}
override fun onStart() {
super.onStart()
sendMessageToFlutter()
}
private fun sendMessageToFlutter() {
if (mtype == 3) {
// Android调用Flutter的send方法
mMethodChannelManager!!.callMethod("AndroidCallFlutterMethod", "Android传递给Flutter的消息")
}
}
}
- MethodChannelPage类
class _MyHomePageState extends State<MethodChannelPage> {
_MyHomePageState();
void _incrementCounter() {
// setState(() {
// _counter++;
// });
_methodChannel!
.invokeMethod("callAndroidMethod", "Flutter--->Android的消息3,这次是Flutter主动的。") // 参数1:告诉Android要调用的方法名,参数2:传递的参数
.then((result) { // invokeMethod().then() 来处理正常结束的逻辑(获得返回值)
print('Android 回复 ---> Flutter的消息 $result');
// 成功:通过result.success 返回值
// 异常:通过 result.error 返回异常信息,可通过catchError 处理异常
});
}
MethodChannel? _methodChannel;
String _methodMessage = '';
void initState() {
super.initState();
// 1.创建MethodChannel
_methodChannel = new MethodChannel("MethodChannelManager");
// 2. 根据Android的要求,调用对应方法
_methodChannel!.setMethodCallHandler((handler) => Future<String>(() {
print("Android端要调用的方法和参数是:${handler}");
setState(() {
_methodMessage = "Android端要调用Flutter的方法名为${handler.method},方法参数是${handler.arguments}";
});
switch (handler.method) {
case "AndroidCallFlutterMethod":
//handler.arguments表示native传递的方法参数
testFun(handler.method, handler.arguments);
break;
}
return "Flutter--->Android的消息3:收到了。";
}));
}
void testFun(method, params) {
print('Android要调用Flutter的方式名为$method,参数是$params');
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("native与flutter通信之MethodChannelPage"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),Text(
'$_methodMessage',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
2.3.EventChannel方式
-
定义EventChannel管理类,执行逻辑
- 2.3.1.创建EventChannel,注意参数2的格式为"包名/标识符";
- 2.3.1.设置对应Handler;
- 2.3.2.重写onListen,在回调中给定义的EventChannel类型的成员变量赋值;
- 2.3.4.定义Android端发送数据,停止发送数据,发送数据失败的方法;
-
EventChannelManager类代码
class EventChannelManager private constructor(
private val messenger: BinaryMessenger
) : EventChannel.StreamHandler {
private var eventSink: EventChannel.EventSink? = null
// 4.1.Android端开始发送数据
fun send(params: Any) {
if (eventSink != null) {
eventSink!!.success(params)
println("sink success")
}
}
// 4.2.Android端停止发送数据
fun cancel() {
if (eventSink != null) {
eventSink!!.endOfStream()
}
}
// 4.3.Android端发送数据失败
fun sendError(str1: String, str2: String, params: Any) {
if (eventSink != null) {
eventSink!!.error(str1, str2, params)
}
}
// 3.回调时机:Flutter端开始监听该channel时
// 说明通道已经建立好,Android可以开始发送数据了
// 参数1:Flutter端初始化EventChannel时返回的值,仅此一次
// 参数2:传数据的载体
override fun onListen(o: Any?, eventSink: EventChannel.EventSink?) {
//此处注意时序,必须得该方法回调后,Android端才允许发送数据
this.eventSink = eventSink
println("onListen():eventSink = $eventSink")
}
// Flutter端不再接收数据时回调
override fun onCancel(o: Any) {
println("onCancel()")
eventSink = null
}
init {
//1.创建EventChannel 参数2的格式:包名/标识符
val channel = EventChannel(messenger, "com.jack.android_simple/EventChannelManager")
//2.设置对应Handler
channel.setStreamHandler(this)
}
companion object {
fun register(
messenger: BinaryMessenger
): EventChannelManager = EventChannelManager(messenger)
}
}
- FlutterAppActivity类代码
class FlutterAppActivity : FlutterActivity() {
private var mInitParams: String? = null
private var mEventChannelManager: EventChannelManager? = null
companion object {
const val INIT_PARAMS = "initParams"
private var mtype = 0
fun start(context: Context, initParams: String = "", type: Int) {
mtype = type
val intent = Intent(context, FlutterAppActivity::class.java)
intent.putExtra(INIT_PARAMS, initParams)
context.startActivity(intent)
}
}
//优先级高于onCreate
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
mInitParams = intent.getStringExtra(INIT_PARAMS);
if (mtype == 4) {
mEventChannelManager =
EventChannelManager.register(flutterEngine.dartExecutor.binaryMessenger)
}
}
//重载该方法来传递初始化参数
override fun getInitialRoute(): String? {
return if (mInitParams == null) super.getInitialRoute() else mInitParams
}
override fun onStart() {
super.onStart()
sendMessageToFlutter()
}
private fun sendMessageToFlutter() {
if (mtype == 4) {
//使用定时器模拟更佳
Handler().postDelayed({ mEventChannelManager!!.send("发送流信息") }, 5000)
}
}
}
- EventChannelPage类代码
class EventChannelPage extends StatefulWidget {
State<StatefulWidget> createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<EventChannelPage> {
EventChannel? _eventChannelPlugin;
String _eventMessage = '';
_MyHomePageState();
void initState() {
super.initState();
// 1.创建EventChannel
_eventChannelPlugin =
EventChannel("com.jack.android_simple/EventChannelManager");
// 2.初始化一个广播流从channel中接收数据
_eventChannelPlugin!
.receiveBroadcastStream() //dynamic arguments: 对应Android端onListen()的第一个参数,可不传
// 开启监听
.listen((event) {
// 注意:listen可以设置 监听数据流其它状态时 的方法 Function? onError, void onDone()?, bool? cancelOnError
print("接收Android发送过来的数据 --- $event");
setState(() {
_eventMessage = event;
});
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("native与flutter通信之EventChannel"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_eventMessage',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}
三.总结
- Android已有项目嵌入Flutter module,其存在一定的弊端,内存消耗的问题,一个engine对应着一套Flutter的进程实例,没有很完美的方案来解决这个问题,需要根据实际情况进行妥协。优化的方式,在Application中预初始化Flutter engine其提升Flutter页面的打开速度(但,这种方案会存在弊端,在Application中预加载flutterEngine引擎会导致FlutterAppActivity的getInitialRoute不被调用),类似的优化问题后续有精力在研究了,文章就先写到这了,感谢大佬能看到这里,笔芯。
- 本篇文章的代码仓库地址