Flutter系列之Platform Channel使用详解

躬行之

共 10411字,需浏览 21分钟

 ·

2020-09-22 18:03

PS:逐渐体会到关键少数原则的重要性,接下来就是付诸实践了,另外科创50ETF明天开始限额销售,可以适当关注或入手一点。

前面几篇文章介绍了 Navigator 组件、Flex 布局、图片加载、Widget 生命周期、混合开发等 Flutter 开发基础知识, 链接如下:

下面介绍一下 Flutter 混合开发中 Platform Channel 的使用,主要内容如下:

  1. 平台通道介绍

  2. 平台数据类型对照

  3. BasicMessageChannel

  4. MethodChannel

  5. EventChannel

平台通道介绍

Platform Channel 是一个异步消息通道,消息在发送之前会编码成二进制消息,接收到的二进制消息会解码成 Dart 值,其传递的消息类型只能是对应的解编码器支持的值,所有的解编码器都支持空消息,其 Native 与 Flutter 通信架构如下图所示:

Flutter 中定义了三种不同类型的 PlatformChannel,主要有三种如下:

  • BasicMessageChannel:用于数据传递;

  • MethodChannel:用于传递方法调用;

  • EventChannel:用于传递事件;

其构造方法都需指定一个通道标识、解编码器以及 BinaryMessenger,BinaryMessenger 是一个 Flutter 与平台的通信工具,用来传递二进制数据、设置对应的消息处理器等。

解编码器有两种分别是 MethodCodec 和 MessageCodec,前者对应方法后者对应消息,BasicMessageChannel 使用的是 MessageCodec,MethodChannel 和 EventChannel 使用的是 MethodCodec。

平台数据类型对照

Platform Channel 提供不同的消息解码机制,如 StandardMessageCodec 提供基本数据类型的解编码、JSONMessageCodec 支持 Json 的解编码等,在平台之间通信时都会自动转换,各平台数据类型对照如下:

BasicMessageChannel

BasicMessageChannel 主要用来数据传递,包括二进制数据,借助 BasicMessageChannel 可以实现 MethodChannel 和 EventChannel 的功能,这里用 BasicMessageChannel 实现 Android 项目使用 Flutter 资源文件的案例,关键流程如下:

  1. Flutter 端获得图片资源对应的二进制数据,这里使用 BinaryCodec,则数据格式为 ByteData;

  2. 使用 BasicMessageChannel 发送图片对应的数据;

  3. 在 Android 端使用 ByteBuffer 接收,并将其转换成 ByteArray,然后解析成 Bitmap 显示出来。

Flutter 端关键代码如下:

1// 创建BasicMessageChannel 
2_basicMessageChannel = BasicMessageChannel("com.manu.image", BinaryCodec());
3
4// 获取assets中的图片对应的ByteData数据
5rootBundle.load('images/miao.jpg').then((value) => {
6  _sendStringMessage(value)
7});
8
9// 发送图片数据
10_sendStringMessage(ByteData byteData) async {
11  await _basicMessageChannel.send(byteData);
12}

Android 端关键代码如下:

1override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
2    super.configureFlutterEngine(flutterEngine)
3    Log.i(tag, "configureFlutterEngine")
4    // 设置消息处理器
5    BasicMessageChannel(
6        flutterEngine.dartExecutor, "com.manu.image", BinaryCodec.INSTANCE
7    ).setMessageHandler { message, reply ->
8        Log.i(tag, "configureFlutterEngine > message:$message")
9        // 数据转换:ByteBuffer->ByteArray
10        val byteBuffer = message as ByteBuffer
11        imageByteArray = ByteArray(byteBuffer.capacity())
12        byteBuffer.get(imageByteArray)
13    }
14
15    // 用于设置Flutter跳转Android的方法处理器
16    MethodChannel(flutterEngine.dartExecutor, channel).setMethodCallHandler { call, result ->
17        Log.i(tag, "configureFlutterEngine > method:${call.method}")
18        if ("startBasicMessageChannelActivity" == call.method) {
19            // 携带图片数据
20            BasicMessageChannelActivity.startBasicMessageChannelActivity(this,imageByteArray)
21        }
22    }
23}
24
25// 显示来自Flutter assets中的图片
26val imageByteArray = intent.getByteArrayExtra("key_image")
27val bitmap = BitmapFactory.decodeByteArray(imageByteArray,0,imageByteArray.size)
28imageView.setImageBitmap(bitmap)

另外,BasicMessageChannel 结合 BinaryCodec 是支持大内存数据块的传递的。

MethodChannel

MethodChannel 主要用来方法的传递,自然可以传递 Native 方法和 Dart 方法,即可以通过 MethodChannel 在 Flutter 中调用 Android 原生方法,在 Android 中调用 Dart 方法,互相调用都是通过 MethodChannel 的 invokeMethod 方法调用的,通信时必须使用相同的通道标识符,具体如下:

  1. Flutter调用Android方法

下面通过 MethodChannel 实现从 Flutter 跳转到 Android 原生界面 MainActivity,Android 端如下:

1/**
2 * @desc FlutterActivity
3 * @author jzman
4 */

5val tag = AgentActivity::class.java.simpleName;
6
7class AgentActivity : FlutterActivity() {
8    val tag = AgentActivity::class.java.simpleName;
9    private val channel = "com.manu.startMainActivity"
10    private var platform: MethodChannel? = null;
11
12    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
13        super.configureFlutterEngine(flutterEngine)
14        Log.d(tag,"configureFlutterEngine")
15        platform = MethodChannel(flutterEngine.dartExecutor, channel)
16        // 设置方法处理器
17        platform!!.setMethodCallHandler(StartMethodCallHandler(this@AgentActivity))
18    }
19
20    companion object{
21        /**
22         * 重新创建NewEngineIntentBuilder才能保证生效
23         */

24        fun withNewEngine(): MNewEngineIntentBuilder? {
25            return MNewEngineIntentBuilder(AgentActivity::class.java)
26        }
27    }
28
29    /**
30     * 自定义NewEngineIntentBuilder
31     */

32    class MNewEngineIntentBuilder(activityClass: Class<out FlutterActivity?>?) :
33        NewEngineIntentBuilder(activityClass!!)
34
35    /**
36     * 实现MethodCallHandler
37     */

38    class StartMethodCallHandler(activity:Activity) : MethodChannel.MethodCallHandler{
39        private val context:Activity = activity
40        override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
41            if ("startMainActivity" == call.method) {
42                Log.i(tag,"arguments:"+call.arguments)
43                startMainActivity(context)
44                // 向Flutter回调执行结果
45                result.success("success")
46            } else {
47                result.notImplemented()
48            }
49        }
50    }
51}

如上还可以使用 MethodChannel.Result 对象向Flutter回调执行结果,Flutter 端如下:

1/// State
2class _PageState extends State<PageWidget{
3  MethodChannel platform;
4
5  @override
6  void initState() {
7    super.initState();
8    platform = new MethodChannel('com.manu.startMainActivity');
9  }
10
11  @override
12  Widget build(BuildContext context) {
13    return Container(
14      width: double.infinity,
15      margin: EdgeInsets.fromLTRB(8880),
16      child: RaisedButton(
17        onPressed: () {
18          _startMainActivity();
19        },
20        child: Text("Flutter to Android"),
21      ),
22    );
23  }
24
25  /// 跳转到原生Activity
26  void _startMainActivity() {
27    platform.invokeMethod('startMainActivity''flutter message').then((value) {
28      // 接收返回的数据
29      print("value:$value");
30    }).catchError((e) {
31      print(e.message);
32    });
33  }
34}

  1. Android调用Dart方法

下面通过 MethodChannel 调用 Flutter 中的 Dart 方法 getName,Android 端代码如下:

1/**
2 * @desc MainActivity
3 * @author jzman
4 */

5class MainActivity : FlutterActivity() {
6    private val tag = MainActivity::class.java.simpleName;
7    private val channel = "com.manu.startMainActivity"
8    private var methodChannel: MethodChannel? = null
9    override fun onCreate(savedInstanceState: Bundle?) {
10        super.onCreate(savedInstanceState)
11        setContentView(R.layout.activity_main)
12
13        btnGetDart.setOnClickListener {
14            getDartMethod()
15        }
16    }
17
18    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
19        super.configureFlutterEngine(flutterEngine)
20        Log.i(tag,"configureFlutterEngine")
21        methodChannel = MethodChannel(flutterEngine.dartExecutor,channel)
22    }
23
24    private fun getDartMethod(){
25        methodChannel?.invokeMethod("getName",nullobject :MethodChannel.Result{
26            override fun success(result: Any?) {
27                Log.i(tag,"success: "+result.toString())
28                Toast.makeText(this@MainActivity,result.toString(),Toast.LENGTH_LONG).show()
29            }
30
31            override fun error(errorCode: String,errorMessage: String?,errorDetails: Any?) {
32                Log.i(tag,"error")
33            }
34
35            override fun notImplemented() {
36                Log.i(tag,"notImplemented")
37            }
38        })
39    }
40
41    companion object{
42        fun startMainActivity(context: Context) {
43            val intent = Intent(context, MainActivity::class.java)
44            context.startActivity(intent)
45        }
46    }
47}

Flutter 端如下:

1/// State
2class _PageState extends State<PageWidget{
3  MethodChannel platform;
4
5  @override
6  void initState() {
7    super.initState();
8    platform = new MethodChannel('com.manu.startMainActivity');
9
10    // 监听Android调用Flutter方法
11    platform.setMethodCallHandler(platformCallHandler);
12  }
13
14  @override
15  Widget build(BuildContext context) {
16    return Container();
17  }
18  /// FLutter Method
19  Future platformCallHandler(MethodCall call) async{
20    switch(call.method){
21      case "getName":
22        return "name from flutter";
23        break;
24    }
25  }
26}

EventChannel

EventChannel 主要用于 Flutter 到原生之间的单向调用,其使用方式类似 Android 中的广播,原生界面负责 Event 的发送,Flutter 端注册监听即可,不多说直接看代码,Android 端代码如下:

1/// Android
2class MFlutterFragment : FlutterFragment() {
3    // 这里用Fragment,Activity也一样
4    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
5        super.configureFlutterEngine(flutterEngine)
6        Log.d(tag,"configureFlutterEngine")
7        EventChannel(flutterEngine.dartExecutor,"com.manu.event").setStreamHandler(object:
8            EventChannel.StreamHandler{
9            override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
10                Log.i(tag,"configureFlutterEngine > onListen")
11                // EventSink发送事件通知
12                events?.success("event message")
13            }
14
15            override fun onCancel(arguments: Any?) {
16                Log.i(tag,"configureFlutterEngine > onCancel")
17            }
18        })
19    }
20
21    companion object{
22        fun withNewEngine(): NewEngineFragmentBuilder? {
23            return MNewEngineIntentBuilder(
24                MFlutterFragment::class.java
25            )
26        }
27    }
28
29    class MNewEngineIntentBuilder(activityClass: Class<out FlutterFragment?>?) :
30        NewEngineFragmentBuilder(activityClass!!)
31}

Flutter 端如下:

1/// State
2class EventState extends State<EventChannelPage{
3  EventChannel _eventChannel;
4  String _stringMessage;
5  StreamSubscription _streamSubscription;
6
7  @override
8  void initState() {
9    super.initState();
10    _eventChannel = EventChannel("com.manu.event");
11    // 监听Event事件
12    _streamSubscription =
13        _eventChannel.receiveBroadcastStream().listen((event) {
14      setState(() {
15        _stringMessage = event;
16      });
17    }, onError: (error) {
18      print("event error$error");
19    });
20  }
21
22  @override
23  void dispose() {
24    super.dispose();
25    if (_streamSubscription != null) {
26      _streamSubscription.cancel();
27      _streamSubscription = null;
28    }
29  }
30
31  @override
32  Widget build(BuildContext context) {
33    return Scaffold(
34        appBar: AppBar(
35          title: Text("EventChannel"),
36          centerTitle: true,
37        ),
38        body: Center(
39          child: Text(_stringMessage == null ? "default" : _stringMessage),
40        ));
41  }
42}

以上就是 Flutter 平台通道的使用,可以在公众号回复关键字【Channel】获取源码。

推荐阅读:


浏览 48
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报