Android与Flutter模块通信实现
创始人
2024-06-02 04:54:34
  • 本篇文章会使用相对最新的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-binid 'com.android.library' version '7.4.0' apply false			//操作3:更改versionid '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? = nullcompanion object {const val INIT_PARAMS = "initParams"private var mtype = 0fun start(context: Context, initParams: String = "", type: Int) {mtype = typeval intent = Intent(context, FlutterAppActivity::class.java)intent.putExtra(INIT_PARAMS, initParams)context.startActivity(intent)}}//优先级高于onCreateoverride 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(R.id.normal).setOnClickListener {FlutterAppActivity.start(this@MainActivity,"打开普通页面的参数信息", type = 1)}findViewById(R.id.btn1).setOnClickListener {FlutterAppActivity.start(this@MainActivity, "BasicMessageChannel", 2)}findViewById(R.id.btn2).setOnClickListener {FlutterAppActivity.start(this@MainActivity, "MethodChannel", 3)}findViewById(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});@overrideWidget 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();@overrideState createState() => _MyHomePageState();
}class _MyHomePageState extends State {//省略...@overrideWidget 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 {private var messageChannel: BasicMessageChannelinit {messageChannel =//参数1:消息信使//参数2:channel的名字,唯一标识//参数3:消息编码器,有多种不同类型的实现,这里通信只选择StringCodecBasicMessageChannel(messenger, "BasicMessageChannelManager", StringCodec.INSTANCE)// 注册处理的Handler,处理来自Flutter的消息messageChannel.setMessageHandler(this)}//重写函数:接收Flutter Module传递过来的消息 并可以回应给Flutter Moduleoverride fun onMessage(s: String?, reply: BasicMessageChannel.Reply) {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) {messageChannel.send(message, callback)}//native主动向Flutter Module发送消息  方式二    不带回调fun send(message: String) {messageChannel.send(message)}}
  • FlutterAppActivity类
class FlutterAppActivity : FlutterActivity() {private var mInitParams: String? = nullprivate var mBasicMessageChannelManager: BasicMessageChannelManager? = nullcompanion object {const val INIT_PARAMS = "initParams"private var mtype = 0fun start(context: Context, initParams: String = "", type: Int) {mtype = typeval intent = Intent(context, FlutterAppActivity::class.java)intent.putExtra(INIT_PARAMS, initParams)context.startActivity(intent)}}//优先级高于onCreateoverride 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 {@overrideState createState() {return _MyHomePageState();}
}class _MyHomePageState extends State {// int _counter = 0;_MyHomePageState();void _incrementCounter() {// setState(() {//   _counter++;// });// 主动向Android发送消息_basicMessageChannel!.send("Flutter--->Android的消息2,这次是Flutter主动的。");}String _basicMessage = '';BasicMessageChannel? _basicMessageChannel;@overridevoid initState() {super.initState();_basicMessageChannel =BasicMessageChannel('BasicMessageChannelManager', StringCodec());//设置setMessageHandler,目的:为了能够接收来自Android的消息//Future:向Android回传消息 _basicMessageChannel!.setMessageHandler((String? message) => Future(() {setState(() {//Android --> Flutter_basicMessage = 'Android--->Flutter的消息:' + message!;});return "Flutter--->Android的消息1:收到了。";}));}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("native与flutter通信之BasicMessageChannel"),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [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代码
      1. 创建MethodChannel实例(传入channel name)
      1. 注册处理的Handler
      1. 调用Flutter端方法(两种方式,区别在于有无返回值)
      1. 根据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}")//返回结果给Dartresult.success("Android收到了 Flutter--->Android的消息,这次是回复操作")}else -> result.notImplemented()}}private var methodChannel: MethodChannelinit {// 1. 创建MethodChannel实例(传入channel name)methodChannel = MethodChannel(messenger, "MethodChannelManager")// 2. 注册处理的HandlermethodChannel.setMethodCallHandler(this)}companion object {fun register(messenger: BinaryMessenger): MethodChannelManager =MethodChannelManager(messenger)}
}
  • FlutterAppActivity 类
class FlutterAppActivity : FlutterActivity() {private var mInitParams: String? = nullprivate var mMethodChannelManager: MethodChannelManager? = nullcompanion object {const val INIT_PARAMS = "initParams"private var mtype = 0fun start(context: Context, initParams: String = "", type: Int) {mtype = typeval intent = Intent(context, FlutterAppActivity::class.java)intent.putExtra(INIT_PARAMS, initParams)context.startActivity(intent)}}//优先级高于onCreateoverride 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 {_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 = '';@overridevoid initState() {super.initState();// 1.创建MethodChannel_methodChannel = new MethodChannel("MethodChannelManager");// 2. 根据Android的要求,调用对应方法_methodChannel!.setMethodCallHandler((handler) => Future(() {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');}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("native与flutter通信之MethodChannelPage"),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [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 = eventSinkprintln("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.设置对应Handlerchannel.setStreamHandler(this)}companion object {fun register(messenger: BinaryMessenger): EventChannelManager = EventChannelManager(messenger)}
}
  • FlutterAppActivity类代码
class FlutterAppActivity : FlutterActivity() {private var mInitParams: String? = nullprivate var mEventChannelManager: EventChannelManager? = nullcompanion object {const val INIT_PARAMS = "initParams"private var mtype = 0fun start(context: Context, initParams: String = "", type: Int) {mtype = typeval intent = Intent(context, FlutterAppActivity::class.java)intent.putExtra(INIT_PARAMS, initParams)context.startActivity(intent)}}//优先级高于onCreateoverride 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 {@overrideState createState() {return _MyHomePageState();}
}class _MyHomePageState extends State {EventChannel? _eventChannelPlugin;String _eventMessage = '';_MyHomePageState();@overridevoid 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? cancelOnErrorprint("接收Android发送过来的数据 --- $event");setState(() {_eventMessage = event;});});}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("native与flutter通信之EventChannel"),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [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不被调用),类似的优化问题后续有精力在研究了,文章就先写到这了,感谢大佬能看到这里,笔芯。
  • 本篇文章的代码仓库地址

相关内容

热门资讯

苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
阿西吧是什么意思 阿西吧相当于... 即使你没有受到过任何外语培训,你也懂四国语言。汉语:你好英语:Shit韩语:阿西吧(아,씨발! )日...