Flutter学习之原生通信-MethodChannel

1,339 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情

  • 本文主要介绍 flutter与原生端的通信方式之一的MethodChannel,通过和原生客户端的交互方式。

MethodChannel:Flutter 与Native端相互调用,调用后可以返回结果,可以 Native 端主动调用,也可以Flutter主动调用,属于双向通信。此方式为最常用的方式, Native 端调用需要在主线程中执行。
下图是官方的架构图 image.png 消息和响应是异步传递的,以确保用户界面保持响应。

注意:  尽管 Flutter 与 Dart 异步发送消息,但无论何时调用通道方法,都必须在平台的主线程上调用该方法。 有关更多信息,请参阅线程部分。

在客户端,MethodChannel启用发送与方法调用相对应的消息。在平台方面, MethodChannelAndroid ( MethodChannelAndroid) 和 FlutterMethodChanneliOS ( MethodChanneliOS) 允许接收方法调用并返回结果。这些类允许您使用非常少的“样板”代码开发平台插件。

1. 平台线程

上面说了Flutter 与 Dart 异步发送消息,但无论何时调用通道方法,都必须在平台的主线程上调用该方法。因此我们看下Android 端从子线程跳转到主线程方式

  • Kotlin 代码:
Handler(Looper.getMainLooper()).post {
  
}
  • Java 代码:
new Handler(Looper.getMainLooper()).post(new Runnable() {
  @Override
  public void run() {
    
  }
});

  • 获取Activity activity.runOnUiThread {

} iOS 端从子线程跳转到主线程方式:

  • OC代码

// 1.NSThread
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];

- (void)updateUI {
}

// 2.NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
 
    }];

// 3.GCD
dispatch_async(dispatch_get_main_queue(), ^{

});

  • Swfit代码
// gcd
DispatchQueue.main.async {
  
}
//其他类似

2. MethodChannel

2.1 Flutter

在flutter端我们创建一个MethodChannel通道进行通信,其中com.flutter.test.MethodChannel就是MethodChannel的名称,与之交互的客户端要对应。

static const channel = MethodChannel('com.flutter.test.MethodChannel');

发送消息

var reslut = await platform.invokeMethod('userInfo', {'name': 'Jack', 'city': "New York"});
  • 第一个参数表示method,方法名称,原生端会解析此参数。
  • 第二个参数表示参数,类型任意,多个参数通常使用Map
  • 返回 Future,原生端返回的数据。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class MethodChannelDemo extends StatefulWidget {
  @override
  _MethodChannelDemoState createState() => _MethodChannelDemoState();
}

class _MethodChannelDemoState extends State<MethodChannelDemo> {
  var channel = const MethodChannel('com.flutter.test.MethodChannel');

  var _data;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
           SizedBox(
            height: 150,
          ),
          RaisedButton(
            child: const Text('发送数据到原生'),
            onPressed: () async {
              var result = await channel.invokeMethod('sendData', {'name': 'Jack', 'age': 18});
              var name = result['name'];
              var age = result['age'];
              setState(() {
                _data = '$name,$age';
              });
            },
          ),

          Text('原生返回数据:$_data')
        ],
      ),
    );
  }
}

2.2 ios

我们在iOS客户端创建一个MethodChannelDemo对象

import UIKit

import Flutter


class MethodChannelDemo: NSObject {


    init(messenger: FlutterBinaryMessenger) {

        let channel = FlutterMethodChannel(name: "com.flutter.test.MethodChannel", binaryMessenger: messenger)

        channel.setMethodCallHandler { (call:FlutterMethodCall, result:@escaping FlutterResult) in

            if (call.method == "sendData") {

                if let dict = call.arguments as? Dictionary<String, Any> {

                    let name:String = dict["name"] as? String ?? ""

                    let age:Int = dict["age"] as? Int ?? 0

                    result(["name":"hello,\(name)","age":age-1])

                }

            }

        }

    }

}

之后在AppDelegate之中注册

override func application(

    _ application: UIApplication,

    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?

  ) -> Bool {

      let controller : FlutterViewController = window?.rootViewController as! FlutterViewController

         MethodChannelDemo(messenger: controller.binaryMessenger)

         GeneratedPluginRegistrant.register(with: self)

      

    GeneratedPluginRegistrant.register(with: self)

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)

  }

运行结果

image.png 点击后,我们对age 处理减1.

image.png

我们继续看下客户端给flutter端发送消息除了result这种被动的,看下如何主动进行发送,点击的时候发生定时器进行发送数据



class MethodChannelDemo {

    var count =  0

    var channel:FlutterMethodChannel


    var timer :Timer!


    init(messenger: FlutterBinaryMessenger) {

         

    

            channel = FlutterMethodChannel(name: "com.flutter.test.MethodChannel", binaryMessenger: messenger)

        channel.setMethodCallHandler { [self]  (call:FlutterMethodCall, result:@escaping FlutterResult) in

                if (call.method == "sendData") {

                    if let dict = call.arguments as? Dictionary<String, Any> {

                        let name:String = dict["name"] as? String ?? ""

                        let age:Int = dict["age"] as? Int ?? 0

                        

                        result(["name":"hello,\(name)","age":age-1])

                    }

                    startTimer()

                }

            }



    }

    func startTimer() {

           timer = Timer.scheduledTimer(timeInterval:1, target: self, selector:#selector(self.tickDown),userInfo:nil,repeats: true)

      }

      @objc func tickDown(){

          count += 1

          let args = ["count":count]

          channel.invokeMethod("timer", arguments:args)

      }

}

我们在flutter中接收数据,刷新状态

var _nativeData;
@override
void initState() {
  super.initState();
  channel.setMethodCallHandler((call) async {
    setState(() {
      _nativeData = call.arguments['count'];
    });
  });
}

点击发送数据

image.png

android的流程也是类似的,可以看下,这里就不展示了。

3.小结

MethodChannel流程就是通过注册进行回调交互,了解下流程就好,具体的可以看下官方文档。后面也是推荐插件进行处理和原生客户端的通信。