Flutter 混合開發與原生通信MethodChannel

2020-11-05 以碼為夢

作者:老孟Flutter

平臺通信的3種方式

Flutter 與 Native 端通信有如下3個方法:

  • MethodChannel:Flutter 與 Native 端相互調用,調用後可以返回結果,可以 Native 端主動調用,也可以Flutter主動調用,屬於雙向通信。此方式為最常用的方式, Native 端調用需要在主線程中執行。
  • BasicMessageChannel:用於使用指定的編解碼器對消息進行編碼和解碼,屬於雙向通信,可以 Native 端主動調用,也可以Flutter主動調用。
  • EventChannel:用於數據流(event streams)的通信, Native 端主動發送數據給 Flutter,通常用於狀態的監聽,比如網絡變化、傳感器數據等。


通信架構圖

此圖為官方的架構圖

Flutter 與 Native 端通信是異步的。


通信與平臺線程

Native 端主動發送數據給 Flutter時,Native 端代碼需要在主線程中執行,Android 端從子線程跳轉到主線程方式:

Kotlin 代碼:

Handler(Looper.getMainLooper()).post { }

Java 代碼:

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

如果可以獲取到當前 Activity,也可以使用如下方式:

activity.runOnUiThread { }

iOS 端從子線程跳轉到主線程方式:

Objective-C 代碼:

dispatch_async(dispatch_get_main_queue(), ^{ });

Swift 代碼:

DispatchQueue.main.async { }


MethodChannel

Flutter 端

Flutter 端創建 MethodChannel 通道,用於與原生端通信:

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

com.flutter.guide.MethodChannel 是 MethodChannel 的名稱,原生端要與之對應。

發送消息:

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

  • 第一個參數表示method,方法名稱,原生端會解析此參數。
  • 第二個參數表示參數,類型任意,多個參數通常使用Map
  • 返回 Future,原生端返回的數據。

完整代碼:

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 端

android 下創建 MethodChannelDemo

package com.flutter.guideimport io.flutter.plugin.common.BinaryMessengerimport io.flutter.plugin.common.MethodCallimport 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) { }}

onMethodCall 方法在 Flutter 端調用 invokeMethod 方法回調,解析方法如下:

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) }}

  • call.method 字符串就是 invokeMethod 方法傳入的 method
  • call.argument 是 invokeMethod 傳入的參數,由於 Flutter 端傳入的是 Map,所以上面的解析按照 Map 解析。
  • result.success() 是返回給 Flutter 的結果。

Flutter 端解析:

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

兩端的解析要相互對應。

MainActivity 啟動:

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


iOS 端

ios 下創建 MethodChannelDemo,按如下方式:

import Flutterimport UIKitpublic 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]) } } } }}


AppDelegate 啟動:

import UIKitimport 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) }}

原生端主動發送消息給Flutter

Flutter 端接收數據

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

Android 發送數據

原生端啟動定時器,每隔一秒向 Flutter 發送數據,Android 端代碼:

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) } }}

注意:Android 端發送數據要在主現場中調用,即:

activity.runOnUiThread { var map = mapOf("count" to count++) channel.invokeMethod("timer", map) } 複製代碼

啟動修改如下:

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


iOS 發送數據

iOS 端啟動定時器代碼如下:

import Flutterimport UIKitpublic 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) }}


交流

最後分享一份Flutter的學習文檔

有需要的朋友可以【私信】或者【評論】獲取

相關焦點

  • 一篇看懂Android與Flutter之間的通信
    Flutter作為一種跨平臺解決方案,經常會作為一個模塊嵌入到原生Android與iOS應用中,Flutter與Android原生端的通信必不可少。所以本文就來講述一下Android如何與flutter進行通信。
  • 教你如何使用Flutter和原生App混合開發
    18年底的時候用flutter做了個小項目,發現flutter確實挺好用的。於是嘗試在公司找個小項目上馬,進行混合開發試試。  方案選擇  目前主流的混合開發方案有兩種集成方式:    也就是谷歌官方提供的方案,項目地址如下所示:  https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps  Flutter項目單獨開發
  • 閒魚對Flutter-Native混合工程解耦的探索
    閒魚Flutter現狀閒魚是第一個使用Flutter混合開發的大型應用,但閒魚客戶端開發最深入體會的痛點就是編譯時長影響開發體驗。在Flutter+Native這種開發模式下,Native編譯速度慢,模塊開發無法突破。閒魚集成了集團眾多中間件,很多功能無法通過flutter直接調用,需使用各種channel到native去調用對應功能。
  • 最新版flutter和android原生交互,必須掌握
    我之前也寫過一篇文章,講解了flutter 與android的原生交互,那個flutter版本是1.9.x,最新穩定版是1.17.5。我們就拿這個版本來講解下flutter與android原生交互。/plugins/GeneratedPluginRegistrant,如果有這個類那就用我下面的方法來實現與android原生的交互。
  • 閒魚對 Flutter-Native 混合工程解耦的探索
    閒魚Flutter現狀閒魚是第一個使用Flutter混合開發的大型應用,但閒魚客戶端開發最深入體會的痛點就是編譯時長影響開發體驗。在Flutter+Native這種開發模式下,Native編譯速度慢,模塊開發無法突破。閒魚集成了集團眾多中間件,很多功能無法通過flutter直接調用,需使用各種channel到native去調用對應功能。
  • 為什麼我們應該使用 Flutter?環信Flutter SDK初體驗
    感興趣的同學可以關注 GitHub:https://github.com/flutter/flutterFlutter 的優勢相比較目前的混合開發方案,Flutter 提供了大量的文檔,能非常快速且友好的讓你加入到這個大家庭。
  • 為什麼我們應該使用 Flutter?
    感興趣的同學可以關注 GitHub:https://github.com/flutter/flutterFlutter 的優勢相比較目前的混合開發方案,Flutter 提供了大量的文檔,能非常快速且友好的讓你加入到這個大家庭。
  • 為什麼我們應該使用 Flutter? - CSDN
    感興趣的同學可以關注 GitHub:https://github.com/flutter/flutterFlutter 的優勢相比較目前的混合開發方案,Flutter 提供了大量的文檔,能非常快速且友好的讓你加入到這個大家庭。
  • Flutter與Android iOS 的雙向通信
    通過 Flutter 來進行移動應用開發,打包 Android 、iOS 雙平臺應用程式,在調用如相機、藍牙、錄音、鬧鐘、屏保等等系列功能時,需要與原生Android、iOS進行消息通信,或者可描述為把數據由 Flutter 傳向 Android 、iOS,或者由原生的 Android 、iOS傳向 Flutter。
  • Flutter混合開發——一種另類卻高效的的原生View嵌入方法
    前言隨著使用Flutter開發的深入加之其生態還不完善,必然會涉及到使用原生View的情況。為此,Flutter也為我們提供了PlatformView方便我們嵌入原生View,以實現一些flutter暫時不支持的功能,但由此也引發了一些性能問題。
  • Flutter 二維碼掃描插件
    /services.dart';class FlutterPluginQrcode { static const MethodChannel _channel = const MethodChannel('flutter_plugin_qrcode'); static Future<String> get getQRCode async { final
  • Flutter混合開發探索與實踐
    公寓PMS是一款給公寓管家提供房源管理的APP,前期功能已使用Native開發上線。我們在該項目中使用了Flutter開發,需要實現以下功能:將Flutter集成到已有Native項目中;實現Flutter與Native頁面混合管理;實現Flutter與Native通信,復用已有Native資源;實現Dart側代碼開發框架。
  • Flutter持久化存儲之key-value存儲
    前言應用開發時會有很多的數據存儲需求,這個時候就需要用到持久化存儲技術,與iOS、安卓一樣,Flutter中也有很多種持久化存儲方式,比如key-value存儲、文件存儲、資料庫存儲等,但其實質都是通過平臺對應的模塊來實現的,本篇我們將帶大家一起了解key-value存儲的應用。
  • 混合開發@原生和H5通信那點事兒
    那麼今天呢,主要想和大家聊聊混合開發中(原生內嵌H5)原生和H5通信那點事兒。背景目前原生Webview內嵌H5的開發模式是非常常見的,現在絕大部分產品迭代比較快,如果使用純原生開發,雖然能保證性能,提升用戶體驗,但是滿足不了快節奏的產品迭代周期,APP頻繁更新迭代,難免會引起用戶反感。
  • Flutter篇:跨平臺技術詳解篇
    ,通過原生的網頁加載控制項WebView,稱這種h5+原生的開發模式為混合開發 ,採用混合模式開發的APP我們稱之為混合應用或Hybrid APP,如果一個應用的大多數功能都是H5實現的話,我們稱其為Web APP。
  • 實操|在 Flutter 中創建通信橋
    我在之前的一篇文章中解釋了如何在 Android 和 iOS 中創建通信橋,作為後續,我認為解釋一下如何在 Flutter 中創建通信橋也是一個不錯的想法。雖然這可能看起來是一件很簡單的事情,但你很快就會意識到,要使這個功能正常工作需要一些工作。首先,重要的是意識到(在撰寫本文時)Flutter 還沒有內置對嵌入式 WebView 的支持。
  • Flutter中嵌套Android布局
    本文具體demo效果如下:開發首先創建flutter項目,在項目中定義好flutter需要展示布局: @override Widget build(BuildContext context定義通信渠道 static const String _channel = 'increment'; static const String _pong = 'pong'; static const String _emptyMessage = ''; static const BasicMessageChannel<String> platform
  • 谷歌宣布:Flutter推首個發布預覽版
    據維基百科介紹,Flutter是一個由谷歌開發的開源移動應用軟體開發工具包,用於為Android和iOS開發應用,同時也將是Google Fuchsia下開發應用的主要工具。Flutter框架包含了兩套匹配特定設計語言的組件。稱作Material Design的組件實現的是同名的谷歌設計語言,稱作Cupertino的組件模仿了蘋果iOS的設計。
  • Flutter到底香不香?
    今晚停電,導致沒法寫代碼,聊一聊flutter,早就想聊了,一直沒時間。每個新的框架的出現都會引來大家的討論,flutter也不例外。大公司對於新技術會比較慎重的,穩定才是王道。而對於我這種個人開發者來說,嘗試新框架機會就多些。我近3年除了工作需要以為,基本沒有用原生來寫產品。
  • 一套代碼 iOS、Android 兩端運行,Google Flutter 對開發者意味著...
    其目標是為了解決移動中的兩個重要問題:一是實現原生應用的性能和與平臺的集成,二是提供一個多平臺,可移植的 UI 工具包來支持高效的應用開發。▌何為 Flutter?「Flutter 可幫助你更容易、更快速的開發出界面美觀的移動應用。」Google 官方如是說。