1. 程式人生 > >Flutter 即學即用系列博客——09 MethodChannel 實現原生與 Flutter 通信(二)

Flutter 即學即用系列博客——09 MethodChannel 實現原生與 Flutter 通信(二)

基礎 看到了 實用 rev strip http end encode enc

技術分享圖片

前言

上一篇我們講解了如何通過 EventChannel 實現 Android -> Flutter 的通信。

並且也看到了 Flutter 內部 EventChannel 源碼也是對 MethodChannel 的封裝。

因此這篇我們來說下如何通過 MethodChannel 實現 Android -> Flutter 的通信。

至於 Flutter -> Android 的通信,沒看過的小夥伴建議看下之前的文章 Flutter 即學即用系列博客——08 MethodChannel 實現 Flutter 與原生通信。

既然我們之前寫過 Flutter -> Android 的 MethodChannel,那麽我們現在要寫 Android -> Flutter 的 MethodChannel,可以仿照一下原先的寫法。

步驟如下:

第一步:Flutter UI 修改

我們的代碼在上一篇的基礎上做修改,在列上面增加一個文本用於確認收到了 Android 的請求。

String _arguments = 'unknown';
Text(_arguments),

第二步:在 Android 端寫 invokeMethod 引用 Flutter 方法

methodChannel.invokeMethod("getContent", "arguments", new MethodChannel.Result() {
    @Override
    public void success(@Nullable Object o) {
        Log.e(TAG, "success="+o);
    }

    @Override
    public void error(String s, @Nullable String s1, @Nullable Object o) {
        Log.e(TAG, "error="+s);
    }

    @Override
    public void notImplemented() {
        Log.e(TAG, "notImplemented");
    }
});

參數說明:
第一個為方法名。用於 Flutter 區分 Android 的不同請求。
第二個為參數值。用於 Android 需要給 Flutter 傳遞的額外數據。
第三個為 Android -> Flutter 請求的結果回調。
回調有三種情況:
1)調用成功
2)調用失敗
3)Flutter 未實現對應方法

第三步:在 Flutter 調用對應 MethodChannel 的 setMethodCallHandler

methodChannel.setMethodCallHandler((MethodCall call){
    if (call?.method == 'getContent') {
      setState(() {
        _arguments = call?.arguments ?? '';
      });
    }
});

看到這裏的 MethodCall 你應該很熟悉了,通過 call.method 可以知道 Android 要獲取的方法名,通過 call.arguments 可以拿到 Android 傳遞過來的數據。

這裏的 getContent 對應 Android 的 invokeMethod。

為了確認我們獲取到了,我們將 Android 傳遞過來的參數顯示出來。

第四步:運行

可以看到效果如下:

初始顯示 unknown
技術分享圖片

點擊後顯示原生傳過來的內容
技術分享圖片

同時控制臺顯示打印信息如下:

success=null

我們發現 Android 確實回調成功了,但是另一個問題隨之而來,Flutter 如何將內容回調給 Android?

解決問題一時爽,一直解決問題一直爽。

也是很簡單的,就是我們寫一個異步方法將信息帶回去即可。

在 setState 方法後面添加下面代碼:

return returnToRaw();

具體方法實現如下:

Future<String> returnToRaw() async {
  return 'received your message';
}

這個時候再運行點擊按鈕會發現控制臺打印如下信息:

success=received your message

可以看到成功收到返回值了。

這裏演示返回的是字符串,因此異步方法返回類型是 Future 。如果你要返回其他類型,可以自行修改。

如果希望回調 notImplemented,不要在 Flutter 調用 MethodChannel 的 setMethodCallHandler 或者 setMethodCallHandler 的參數設置為 null 即可。

    //方法一
//    methodChannel.setMethodCallHandler((MethodCall call){
//        if (call?.method == 'getContent') {
//          setState(() {
//            _arguments = call?.arguments ?? '';
//          });
//          return returnToRaw();
//        }
//    });
    //方法二
    methodChannel.setMethodCallHandler(null);

如果希望回調 error,修改 returnToRaw 方法即可。如下:

Future<String> returnToRaw() async {
  throw PlatformException(code: 'error code');
}

這裏通過拋出 PlatformException 並將錯誤信息帶回去給 Android。

一般錯誤信息除非是手動需要拋,否則源碼會幫我們處理的。

這裏是為了演示所以手動拋出異常。

好了,至此 MethodChannel Android-> Flutter 我們也實現了。

其實不管是 Android -> Flutter 還是 Flutter-> Android,都是平臺相關代碼。

因此可以直接到 platform_channel.dart 裏面看看源碼。

除了 EventChannel、MethodChannel,還有 BasicMessageChannel 和 OptionalMethodChannel,這些就交給小夥伴們自己去研究了。

後記

這邊分享一下研究 MethodChannel 實現 Android -> Flutter 的過程遇到的坑。

希望不止是授大家以魚,更是授大家以漁

坑1:一開始將原生 MethodChannel 寫到外面,導致 Flutter 沒收到請求

因為 Flutter 是在 initState 裏面去 setMethodCallHandler 的,而 debug 模式下可能 Flutter 還沒加載完成,這個時候發送消息,Flutter 就可能沒收到。

後面改成點擊之後 Flutter -> Android,Android 再發給 Flutter。

這個問題是異步的原因導致的。

明確之後通過正確的方式就可以收到請求了。

坑2:Flutter 收到之後,如何回調回消息呢?

首先點擊進入 setMethodCallHandler 源碼,如下:

void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) {
  BinaryMessages.setMessageHandler(
    name,
    handler == null ? null : (ByteData message) => _handleAsMethodCall(message, handler),
  );
}

再深入 _handleAsMethodCall,如下:

Future<ByteData> _handleAsMethodCall(ByteData message, Future<dynamic> handler(MethodCall call)) async {
  final MethodCall call = codec.decodeMethodCall(message);
  try {
    return codec.encodeSuccessEnvelope(await handler(call));
  } on PlatformException catch (e) {
    return codec.encodeErrorEnvelope(
      code: e.code,
      message: e.message,
      details: e.details,
    );
  } on MissingPluginException {
    return null;
  } catch (e) {
    return codec.encodeErrorEnvelope(code: 'error', message: e.toString(), details: null);
  }
}

到這裏就比較明朗了。

可以看到錯誤基本不用我們處理,也沒有太多可介入空間。

但是成功回調,這裏核心語句是

await handler(call)

因此我們上面通過一個異步方法返回字符串給原生。

由於筆者之前對 Future 不是很熟,因此為了解決這個問題,看了 dart 源碼??:
https://www.dartlang.org/tutorials/language/futures

至此,結合系列博客 08 基本就完成了 MethodChannel 相關的雙向通信講解了。

技術分享圖片

源碼位置:
https://github.com/nesger/FlutterSample/tree/feature/method_channel_reverse

更多閱讀:
Flutter 即學即用系列博客
Flutter 即學即用系列博客——01 環境搭建
Flutter 即學即用系列博客——02 一個純 Flutter Demo 說明
Flutter 即學即用系列博客——03 在舊有項目引入 Flutter
Flutter 即學即用系列博客——04 Flutter UI 初窺
Flutter 即學即用系列博客——05 StatelessWidget vs StatefulWidget
Flutter 即學即用系列博客——06 超實用 Widget 集錦
Flutter 即學即用系列博客——07 RenderFlex overflowed 引發的思考
Flutter 即學即用系列博客——08 MethodChannel 實現 Flutter 與原生通信
Flutter 即學即用系列博客——09 EventChannel 實現原生與 Flutter 通信(一)

Flutter & dart
dart 如何優雅的避空
Flutter map 妙用及 .. 使用

技術分享圖片

Flutter 即學即用系列博客——09 MethodChannel 實現原生與 Flutter 通信(二)