前言
上一篇我們講解了如何通過 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:
初始顯示 unknown
點擊後顯示原生傳過來的內容:
點擊後顯示原生傳過來的內容
同時控制檯顯示打印信息如下:
success=null
我們發現 Android 確實回調成功了,但是另一個問題隨之而來,Flutter 如何將內容回調給 Android?
解決問題一時爽,一直解決問題一直爽。
也是很簡單的,就是我們寫一個異步方法將信息帶回去即可。
在 setState 方法後面添加下面代碼:
return returnToRaw();
具體方法實現如下:
Future<string> returnToRaw() async {
return 'received your message';
}
/<string>
這個時候再運行點擊按鈕會發現控制檯打印如下信息:
success=received your message
可以看到成功收到返回值了。
這裡演示返回的是字符串,因此異步方法返回類型是 Future<string> 。如果你要返回其他類型,可以自行修改。/<string>
如果希望回調 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');
}
/<string>
這裡通過拋出 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),
);
}
/<dynamic>
再深入 _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);
}
}
/<dynamic>/<bytedata>
到這裡就比較明朗了。
可以看到錯誤基本不用我們處理,也沒有太多可介入空間。
但是成功回調,這裡核心語句是
await handler(call)
因此我們上面通過一個異步方法返回字符串給原生。
由於筆者之前對 Future 不是很熟,因此為了解決這個問題,看了 dart 源碼:
https://www.dartlang.org/tutorials/language/futures
至此,結合系列博客 08 基本就完成了 MethodChannel 相關的雙向通信講解了。
源碼位置:
https://github.com/nesger/FlutterSample/tree/feature/method_channel_reverse
閱讀更多 安卓小煜 的文章