[Flutter hybrid development] and native communication-MethodChannel

Author: Laomeng Flutter

3 ways of platform communication

There are three ways to communicate between Flutter and Native:

  • MethodChannel : Flutter and Native can call each other, and the result can be returned after the call. It can be actively called by Native or Flutter, which belongs to two-way communication. This method is the most commonly used method. Native calls need to be executed in the main thread.
  • BasicMessageChannel : Used to encode and decode the message using the specified codec. It belongs to two-way communication and can be actively called by the Native or Flutter.
  • EventChannel : Used for the communication of data streams (event streams). The Native side actively sends data to Flutter, which is usually used for status monitoring, such as network changes and sensor data.

Communication architecture diagram

This picture is the official architecture diagram
Insert picture description here

Flutter communicates with the Native side asynchronously.

Communication and platform threads

When the Native side actively sends data to Flutter, the Native side code needs to be executed in the main thread, and the Android side jumps from the child thread to the main thread:

Kotlin code:

Handler(Looper.getMainLooper()).post {

}

Java code:

new Handler(Looper.getMainLooper()).post(new Runnable() {
  @Override
  public void run() {

  }
});

If you can get the current Activity, you can also use the following methods:

activity.runOnUiThread {

}

The iOS terminal jumps from the child thread to the main thread:

Objective-C code:

dispatch_async(dispatch_get_main_queue(), ^{

});

Swift Code:

DispatchQueue.main.async {

}

MethodChannel

Flutter side

The Flutter side creates a MethodChannel channel to communicate with the native side:

var channel = MethodChannel('com.flutter.guide.MethodChannel');

com.flutter.guide.MethodChannel is the name of MethodChannel, and the native side should correspond to it.

Send a message:

var result = await channel.invokeMethod('sendData',{'name': 'laomeng', 'age': 18})
  • The first parameter represents method , the name of the method , and the native side will parse this parameter.
  • The second parameter represents the parameter, the type is arbitrary, and multiple parameters usually use Map .
  • Return Future , the data returned by the native terminal.

Complete code:

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

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

  var _data;

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

Android side

Create MethodChannelDemo under android :

package com.flutter.guide

import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

/**
 * des:
 */
class MethodChannelDemo(messenger: BinaryMessenger): MethodChannel.MethodCallHandler {

    private var channel: MethodChannel

    init {
        channel = MethodChannel(messenger, "com.flutter.guide.MethodChannel")
        channel.setMethodCallHandler(this)
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {

    }
}

The onMethodCall method calls the invokeMethod method callback on the Flutter side, and the analysis method is as follows:

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
    if (call.method == "sendData") {
        val name = call.argument("name") as String?
        val age = call.argument("age") as Int?

        var map = mapOf("name" to "hello,$name",
                "age" to "$age"
        )
        result.success(map)
    }
}
  • The call.method string is the method passed by the invokeMethod method .
  • call.argument is the parameter passed in by invokeMethod. Since the Flutter end passes in Map, the above analysis is based on Map.
  • result.success() is the result returned to Flutter.

Flutter side analysis:

var result = await channel
    .invokeMethod('sendData', {'name': 'laomeng', 'age': 18});
var name = result['name'];
var age = result['age'];

The analysis at both ends must correspond to each other.

Start in MainActivity :

class MainActivity : FlutterActivity() {

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannelDemo(flutterEngine.dartExecutor.binaryMessenger)
    }
}

iOS edge

Create MethodChannelDemo under ios , as follows:

import Flutter
import UIKit

public class MethodChannelDemo {

    init(messenger: FlutterBinaryMessenger) {
        let channel = FlutterMethodChannel(name: "com.flutter.guide.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 ?? -1
                    result(["name":"hello,\(name)","age":age])
                }
            }
        }
    }
}

Start in AppDelegate :

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  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)

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

The native end actively sends messages to Flutter

Flutter receives data

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

Android send data

The native end starts the timer and sends data to Flutter every second. The Android end code:

class MethodChannelDemo(var activity: Activity, messenger: BinaryMessenger) : MethodChannel.MethodCallHandler {

    private var channel: MethodChannel
    private var count = 0

    init {
        channel = MethodChannel(messenger, "com.flutter.guide.MethodChannel")
        channel.setMethodCallHandler(this)
        startTimer()
    }

    fun startTimer() {
        var timer = Timer().schedule(timerTask {
            activity.runOnUiThread {
                var map = mapOf("count" to count++)
                channel.invokeMethod("timer", map)
            }
        }, 0, 1000)

    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        if (call.method == "sendData") {
            val name = call.argument("name") as String?
            val age = call.argument("age") as Int?

            var map = mapOf("name" to "hello,$name",
                    "age" to "$age"
            )
            result.success(map)
        }
    }
}

Note: The data sent by the Android side should be called in the main site , namely:

activity.runOnUiThread { var map = mapOf(“count” to count++) channel.invokeMethod(“timer”, map) } 复制代码

Start the modification as follows:

class MainActivity : FlutterActivity() {

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannelDemo(this,flutterEngine.dartExecutor.binaryMessenger)
        flutterEngine.plugins.add(MyPlugin())
    }
}

iOS send data

The code for starting the timer on iOS is as follows:

import Flutter
import UIKit

public class MethodChannelDemo {
    var count =  0
    var channel:FlutterMethodChannel
    init(messenger: FlutterBinaryMessenger) {
        channel = FlutterMethodChannel(name: "com.flutter.guide.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 ?? -1
                    result(["name":"hello,\(name)","age":age])
                }
            }
        }
        startTimer()
    }

    func startTimer() {
        var timer = Timer.scheduledTimer(timeInterval:1, target: self, selector:#selector(self.tickDown),userInfo:nil,repeats: true)
    }
    @objc func tickDown(){
        count += 1
        var args = ["count":count]
        channel.invokeMethod("timer", arguments:args)
    }
}

communicate with

Finally, share a Flutter learning document

Friends in need can click to get

Guess you like

Origin blog.csdn.net/ajsliu1233/article/details/109242510