Flutter桌面開發 - windows外掛開發
theme: vuepress
通過此篇文章,你將瞭解到: 1. Flutter外掛的基本介紹; 2. windows外掛開發的真實踩坑經驗。
⚠️本文為稀土掘金技術社群首發簽約文章,14天內禁止轉載,14天后未獲授權禁止轉載,侵權必究!
前言
我們都知道,Flutter的定位更多是作為一個跨平臺的UI框架,對於原生平臺的功能,開發過程中經常需要外掛來提供。不幸的是Windows的生態又極其不完整,外掛開發必不可少。但網上windows的文章少之又少,所以本篇文章,我們一起來聊聊外掛開發的一些技巧。
外掛介紹
Flutter的外掛主要分兩種:package和plugin。 - Package是純dart程式碼的庫,不涉及原生平臺的程式碼; - Plugin是原生外掛庫,是一種特殊的Package。Plugin需要開發者分別在各原生平臺實現對應的能力。
其中Plugin是我們要著重講的,既然是原生平臺實現,那跟dart層就勢必需要通訊
。Flutter Plugin的通訊主要有:methodChannel、eventChannel、basicMessageChannel。
- MethodChannel:同步呼叫的通道,呼叫後可以通過result返回結果。可以 Native 端主動呼叫,也可以Flutter主動呼叫,屬於雙向通訊。這種通訊方式是我們日常開發中為最常用的方式, 關鍵點是Native 端的呼叫需要在主執行緒中執行
。
- EventChannel:非同步事件通知的通道,一般是Native端主動發出通知,Flutter接收通訊資訊。
- BasicMessageChannel:長連結的通道,雙端可以隨時發出訊息,對方收到訊息後可以使用reply進行回覆。一般常用於需要雙向通訊可不知道何時需要傳送的場景。
windows外掛編寫
Flutter Android的生態算是比較完整的,而且網上95%的外掛文章,都是以移動端為主,對於不熟悉Windows開發的同學極度不友好。因此本篇文章我們不講Android端的實現,重點講Windows端的實踐,不過我也不是C++技術棧的,只能淺淺分享我踩過的坑。
1. 如何建立通訊通道?
C++
// MethodChannel
void XXXPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows* registrar) {
// 建立一個MethodChannel
auto channel =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "usb_tool",
&flutter::StandardMethodCodec::GetInstance());
// 建立外掛物件
auto plugin = std::make_unique<XXXPlugin>();
// 把通道設定給外掛,同時傳入訊息的處理入口
channel->SetMethodCallHandler(
[plugin_pointer = plugin.get()](const auto& call, auto result) {
plugin_pointer->HandleMethodCall(call, std::move(result));
});
}
```C++
// EventChannel
// 建立事件流處理物件
auto eventHandler = std::make_unique<
StreamHandlerFunctions最後我們還需要把外掛註冊進專案中
C++
registrar->AddPlugin(std::move(plugin));
```
2. 如何處理訊息?
在上面建立的過程中,其實已經把處理方法的傳遞給外掛了。
```C++
// MethodChannel的處理
// result即通訊的物件
void XXXPlugin::HandleMethodCall(
const flutter::MethodCall
if (IsWindows10OrGreater()) {
version_stream << "10+";
}
else if (IsWindows8OrGreater()) {
version_stream << "8";
}
else if (IsWindows7OrGreater()) {
version_stream << "7";
}
// 通過result->Succes回覆訊息
result->Success(flutter::EncodableValue(version_stream.str()));
} else {
result->NotImplemented();
}
}
C++
// 主動向Flutter端傳送訊息
std::unique_ptr < flutter::StreamHandlerError
// Flutter取消監聽時觸發
std::unique_ptr < flutter::StreamHandlerError
Windows外掛的一些坑
這是本篇文章的重點。我們都知道Flutter是單執行緒的機制,來到原生平臺也一樣,Platform是執行在Flutter的主執行緒的,自然是不能做任何耗時的,不然會卡住主執行緒,系統會把我們認為無響應的應用,從而殺死應用。
我們經常會在使用windows外掛時,感覺點選卡頓,其實就是很多外掛沒有做這個處理,導致事件佇列等待排程。這主要是因為在windows的開發習慣上,耗時操作會丟到子執行緒非同步執行,然後主執行緒如何等待執行結果?使用while一直去查詢是否執行完成,這在windows上成為掛起。
不過一個有趣的現象是:當有耗時操作的時候,Flutter的動畫是可以流程播放的,但是點選事件卻卡住了,這時候C++的同學就會扯,你看動畫都是流程的,問題肯定出在Flutter上?其實是因為動畫在Flutter中屬於微任務,它的優先順序是高於事件佇列的。
而while也是分配到事件佇列中,所以動畫優先執行,點選卻需要一直等到while結束。
在Android中,為了避免這個問題,我們一般會使用協程,把耗時操作丟給協程,讓系統幫我們進行任務排程,通過await拿到執行完之後的結果,再把結果返回給dart層。整個機制其實還是保留了flutter的單執行緒機制,從而避免了卡頓問題。
在Windows端,其實也有協程這個概念,比如WinRT、C++都有提供協程的能力。但問題在於協程這個東西,對於C++來說太新了,同時C++的歷史包袱實在太重,到現在還是用著很老版本的庫。這就導致很多C++的庫沒辦法遷移到協程這種方式,至少在我現在的業務中,切換成本極高,幾乎沒辦法完成。
但問題總得解決,目前我們主要使用非同步通知的方式,來解決這個問題。此非同步是真非同步
,非flutter單執行緒任務排程的非同步。我們會把耗時的操作丟給子執行緒
,但是我們不再通過while進行非同步轉同步
,而是在子執行緒中,主動通過channel去通知會Dart層。
C++
if (*method == "getAsync") {
async_pipe_stream_->Get(request, std::bind(&XXXPlugin::OnResponse, this, std::placeholders::_1, *uuid));
// 直接返回true,但真正的執行結果再OnResponse中主動返回
result->Success(EncodableValue(true));
return;
}
在外掛的dart程式碼中,我們需要主動建立一個MethodChannel的接收器,非同步接收到後,通過執行業務端傳入的回撥通知回去。
```Dart
class NativePlugin {
static const MethodChannel _channel =
MethodChannel('com.open.flutter/xxx/xxx');
static NativePlugin? _instance;
// 獲取例項,單例 static NativePlugin getInstance({String defaultToken = _token}) { _instance ??= NativePlugin._internal(defaultToken); return _instance!; }
// 私有命名建構函式,做一次初始化 NativePlugin._internal(String defaultToken) { _defaultToken = defaultToken;
_channel.setMethodCallHandler((MethodCall call) async {
if (call.method == 'onResponse') {
final arguments = Map<String, dynamic>.from(call.arguments);
// 執行業務端傳入的回撥
await _onResponse(arguments);
}
});
} ``` 外掛的Flutter層需要接收/維護回撥列表,不過此方式有隱患,傳入的回撥容易造成閉包問題,增加一些記憶體洩露的風險;但是對於沒辦法使用協程的C++外掛來說,此方案確實可以解決不少問題。親測可用的!
寫在最後
這篇文章,適合熟悉Flutter外掛開發,但是想接觸C++的同學學習討論。
此專欄從視窗管理、解析度適配、桌面小工具、專案框架、外掛編寫;下次我們講講如何進行打包!
- Flutter桌面開發 - windows外掛開發
- Flutter桌面開發-專案工程化框架搭建
- Flutter桌面小工具 -- 靈動島【Windows Android版本】
- Flutter資源下載實現斷點續傳
- Flutter桌面應用如何進行多解析度適配
- Flutter - 桌面應用視窗化實戰
- 你真的敢落地Flutter桌面端嗎?
- Flutter 桌面端實踐之識別外接媒體裝置
- Flutter【移動端】如何進行多渠道打包釋出
- Flutter實現新手引導蒙層的兩種方式
- 移動端音視訊需求實現方案探索
- Flutter實現酷狗流暢Tabbar效果
- Flutter實現動態化更新-技術預研
- Flutter動畫-實現閃爍星星
- 我該如何給Flutter webview新增透明背景?
- Flutter輸入框獲取剪下板-合規問題踩坑