Flutter 混合開發(Android)Flutter跟Native相互通訊

前言

Flutter 做爲混合開發,跟native端作一些交互在所不免,好比說調用原生系統傳感器、原生端的網絡框架進行數據請求就會用到 Flutter 調用android 及android 原生調用 Flutter的方法,這裏就涉及到Platform Channels(平臺通道)android

Platform Channels (平臺通道)

Flutter 經過Channel 與客戶端之間傳遞消息,如圖:json

image.png

圖中就是經過MethodChannel的方式實現Flutter 與客戶端之間的消息傳遞。MethodChannel是Platform Channels中的一種,Flutter有三種通訊類型:bash

BasicMessageChannel:用於傳遞字符串和半結構化的信息

MethodChannel:用於傳遞方法調用(method invocation)一般用來調用native中某個方法

EventChannel: 用於數據流(event streams)的通訊。有監聽功能,好比電量變化以後直接推送數據給flutter端。
複製代碼

爲了保證UI的響應,經過Platform Channels傳遞的消息都是異步的。網絡

更多關於channel原理能夠去看這篇文章:channel原理篇併發

Platform Channels 使用

1.MethodChannel的使用

原生客戶端寫法(以Android 爲例)

首先定義一個獲取手機電量方法app

private int getBatteryLevel() {
        return 90;
    }
複製代碼

這函數是要給Flutter 調用的方法,此時就須要經過 MethodChannel 來創建這個通道了。框架

首先新增一個初始化 MethodChannel 的方法異步

private String METHOD_CHANNEL = "common.flutter/battery";
private String GET_BATTERY_LEVEL = "getBatteryLevel";
private MethodChannel methodChannel;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
        initMethodChannel();
        getFlutterView().postDelayed(() ->
            methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() {
                @Override
                public void success(@Nullable Object o) {
                    Log.d(TAG, "get_message:" + o.toString());
                }

                @Override
                public void error(String s, @Nullable String s1, @Nullable Object o) {

                }

                @Override
                public void notImplemented() {

                }
            }), 5000);

    }

    private void initMethodChannel() {
        methodChannel = new MethodChannel(getFlutterView(), METHOD_CHANNEL);
        methodChannel.setMethodCallHandler(
                (methodCall, result) -> {
                    if (methodCall.method.equals(GET_BATTERY_LEVEL)) {
                        int batteryLevel = getBatteryLevel();

                        if (batteryLevel != -1) {
                            result.success(batteryLevel);
                        } else {
                            result.error("UNAVAILABLE", "Battery level not available.", null);
                        }
                    } else {
                        result.notImplemented();
                    }
                });


    }

    private int getBatteryLevel() {
        return 90;
    }

複製代碼

METHOD_CHANNEL 用於和flutter交互的標識,因爲通常狀況下會有多個channel,在app裏面須要保持惟一性async

MethodChannel 都是保存在以通道名爲Key的Map中。因此要是設了兩個名字同樣的channel,只有後設置的那個會生效。ide

onMethodCall 有兩個參數,onMethodCall 裏包含要調用的方法名稱和參數。Result是給Flutter的返回值。方法名是客戶端與Flutter統一設定。經過if/switch語句判斷 MethodCall.method 來區分不一樣的方法,在咱們的例子裏面咱們只會處理名爲「getBatteryLevel」的調用。在調用本地方法獲取到電量之後經過 result.success(batteryLevel) 調用把電量值返回給Flutter。

MethodChannel-Flutter 端

直接先看一下Flutter端的代碼

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  static const platform = const MethodChannel('common.flutter/battery');

  void _incrementCounter() {
    setState(() {
      _counter++;
      _getBatteryLevel();
    });
  }

  @override
  Widget build(BuildContext context) {
    platform.setMethodCallHandler(platformCallHandler);
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
            Text('$_batteryLevel'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
    );
  }

  String _batteryLevel = 'Unknown battery level.';

  Future<Null> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

 //客戶端調用
  Future<dynamic> platformCallHandler(MethodCall call) async {
    switch (call.method) {
      case "get_message":
        return "Hello from Flutter";
        break;
    }
  }
}

複製代碼

上面代碼解析:
首先,定義一個常量result.success(platform),和Android客戶端定義的channel一致;
接下來定義一個 result.success(_getBatteryLevel())方法,用來調用Android 端的方法,result.success(final int result = await platform.invokeMethod('getBatteryLevel');) 這行代碼就是經過通道來調用Native(Android)方法了。由於MethodChannel是異步調用的,因此這裏必需要使用await關鍵字。

在上面Android代碼中咱們把獲取到的電量經過result.success(batteryLevel);返回給Flutter。這裏await表達式執行完成之後電量就直接賦值給result變量了。而後經過result.success(setState); 去改變Text顯示值。到這裏爲止,是經過Flutter端調用原生客戶端方法。

MethodChannel 實際上是一個能夠雙向調用的方法,在上面的代碼中,其實咱們也體現了,經過原生客戶端調用Flutter的方法。

在原生端經過 methodChannel.invokeMethod 的方法調用

methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() {
                @Override
                public void success(@Nullable Object o) {
                    Log.d(TAG, "get_message:" + o.toString());
                }

                @Override
                public void error(String s, @Nullable String s1, @Nullable Object o) {

                }

                @Override
                public void notImplemented() {

                }
            });
複製代碼

在Flutter端就須要給MethodChannel設置一個MethodCallHandler

static const platform = const MethodChannel('common.flutter/battery');
platform.setMethodCallHandler(platformCallHandler);
Future<dynamic> platformCallHandler(MethodCall call) async {
    switch (call.method) {
      case "get_message":
        return "Hello from Flutter";
        break;
    }
  }
複製代碼

以上就是MethodChannel的相關用法了。

EventChannel

將數據推送給Flutter端,相似咱們經常使用的推送功能,有須要就推送給Flutter端,是否須要去處理這個推送由Flutter那邊決定。相對於MethodChannel是主動獲取,EventChannel則是被動推送。

EventChannel 原生客戶端寫法

private String EVENT_CHANNEL = "common.flutter/message";
private int count = 0;
private Timer timer;

private void initEventChannel() {
        new EventChannel(getFlutterView(), EVENT_CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {
            @Override
            public void onListen(Object arguments, EventChannel.EventSink events) {
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        if (count < 10) {
                            count++;
                            events.success("當前時間:" + System.currentTimeMillis());
                        } else {
                            timer.cancel();
                        }
                    }
                }, 1000, 1000);
            }

            @Override
            public void onCancel(Object o) {

            }
        });
    }
複製代碼

在上面的代碼中,咱們作了一個定時器,每秒向Flutter推送一個消息,告訴Flutter咱們當前時間。爲了防止一直倒計時,我這邊作了個計數,超過10次就中止發送。

EventChannel Flutter端

String message = "not message";
static const eventChannel = const EventChannel('common.flutter/message');
@override
  void initState() {
    super.initState();
    eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
  }

void _onEvent(Object event) {
    setState(() {
      message =
      "message: $event";
    });
  }

  void _onError(Object error) {
    setState(() {
      message = 'message: unknown.';
    });
  }
複製代碼

上面的代碼就是Flutter端接收原生客戶端數據,經過_onEvent 來接收數據,將數據顯示Text。這個實現相對簡單,若是要達到業務分類,須要將數據封裝成json,經過json數據包裝一些對應業務標識和數據來作區分。

BasicMessageChannel

BasicMessageChannel (主要是傳遞字符串和一些半結構體的數據)

BasicMessageChannel Android端

private void initBasicMessageChannel() {
        BasicMessageChannel<Object> basicMessageChannel = new BasicMessageChannel<>(getFlutterView(), BASIC_CHANNEL, StandardMessageCodec.INSTANCE);
        //主動發送消息到flutter 並接收flutter消息回覆
            basicMessageChannel.send("send basic message", (object)-> {
                Log.e(TAG, "receive reply msg from flutter:" + object.toString());
            });

        //接收flutter消息 併發送回復
        basicMessageChannel.setMessageHandler((object, reply)-> {
            Log.e(TAG, "receive msg from flutter:" + object.toString());
            reply.reply("reply:got your message");

        });

    }
複製代碼

BasicMessageChannel Flutter端

static const basicChannel = const BasicMessageChannel('common.flutter/basic', StandardMessageCodec());
//發送消息到原生客戶端 而且接收到原生客戶端的回覆
  Future<String> sendMessage() async {
    String reply = await basicChannel.send('this is flutter');
    print("receive reply msg from native:$reply");
    return reply;
  }

  //接收原生消息 併發送回復
  void receiveMessage() async {
    basicChannel.setMessageHandler((msg) async {
      print("receive from Android:$msg");
      return "get native message";
    });
複製代碼

上面例子中用到的編解碼器爲StandardMessageCodec ,例子中通訊都是String,用StringCodec也能夠。

以上就是Flutter提供三種platform和dart端的消息通訊方式。

相關文章
相關標籤/搜索