Android与Flutter模块通信实现

  • 本篇文章会使用相对最新的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代码
      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}")
                //返回结果给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不被调用),类似的优化问题后续有精力在研究了,文章就先写到这了,感谢大佬能看到这里,笔芯。
  • 本篇文章的代码仓库地址

猜你喜欢

转载自blog.csdn.net/itTalmud/article/details/129478684