欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Flutter桌面開(kāi)發(fā)windows插件開(kāi)發(fā)

 更新時(shí)間:2022年11月28日 09:35:28   作者:Karl_wei  
這篇文章主要為大家介紹了Flutter桌面開(kāi)發(fā)windows插件開(kāi)發(fā)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

通過(guò)此篇文章,你將了解到:

Flutter插件的基本介紹;

windows插件開(kāi)發(fā)的真實(shí)踩坑經(jīng)驗(yàn)。

我們都知道,F(xiàn)lutter的定位更多是作為一個(gè)跨平臺(tái)的UI框架,對(duì)于原生平臺(tái)的功能,開(kāi)發(fā)過(guò)程中經(jīng)常需要插件來(lái)提供。不幸的是Windows的生態(tài)又極其不完整,插件開(kāi)發(fā)必不可少。但網(wǎng)上windows的文章少之又少,所以本篇文章,我們一起來(lái)聊聊插件開(kāi)發(fā)的一些技巧。

插件介紹

Flutter的插件主要分兩種:package和plugin。

  • Package是純dart代碼的庫(kù),不涉及原生平臺(tái)的代碼;
  • Plugin是原生插件庫(kù),是一種特殊的Package。Plugin需要開(kāi)發(fā)者分別在各原生平臺(tái)實(shí)現(xiàn)對(duì)應(yīng)的能力。

其中Plugin是我們要著重講的,既然是原生平臺(tái)實(shí)現(xiàn),那跟dart層就勢(shì)必需要通訊。Flutter Plugin的通訊主要有:methodChannel、eventChannel、basicMessageChannel。

  • MethodChannel:同步調(diào)用的通道,調(diào)用后可以通過(guò)result返回結(jié)果??梢?Native 端主動(dòng)調(diào)用,也可以Flutter主動(dòng)調(diào)用,屬于雙向通信。這種通信方式是我們?nèi)粘i_(kāi)發(fā)中為最常用的方式, 關(guān)鍵點(diǎn)是Native 端的調(diào)用需要在主線程中執(zhí)行。
  • EventChannel:異步事件通知的通道,一般是Native端主動(dòng)發(fā)出通知,F(xiàn)lutter接收通信信息。
  • BasicMessageChannel:長(zhǎng)鏈接的通道,雙端可以隨時(shí)發(fā)出消息,對(duì)方收到消息后可以使用reply進(jìn)行回復(fù)。一般常用于需要雙向通信可不知道何時(shí)需要發(fā)送的場(chǎng)景。

windows插件編寫

Flutter Android的生態(tài)算是比較完整的,而且網(wǎng)上95%的插件文章,都是以移動(dòng)端為主,對(duì)于不熟悉Windows開(kāi)發(fā)的同學(xué)極度不友好。因此本篇文章我們不講Android端的實(shí)現(xiàn),重點(diǎn)講Windows端的實(shí)踐,不過(guò)我也不是C++技術(shù)棧的,只能淺淺分享我踩過(guò)的坑。

  • 如何創(chuàng)建通信通道?
// MethodChannel
void XXXPlugin::RegisterWithRegistrar(
	flutter::PluginRegistrarWindows* registrar) {
        // 創(chuàng)建一個(gè)MethodChannel
	auto channel =
		std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
			registrar->messenger(), "usb_tool",
			&flutter::StandardMethodCodec::GetInstance());
        // 創(chuàng)建插件對(duì)象
	auto plugin = std::make_unique<XXXPlugin>();
        // 把通道設(shè)置給插件,同時(shí)傳入消息的處理入口
	channel->SetMethodCallHandler(
		[plugin_pointer = plugin.get()](const auto& call, auto result) {
		plugin_pointer->HandleMethodCall(call, std::move(result));
	});
}
// EventChannel
// 創(chuàng)建事件流處理對(duì)象
auto eventHandler = std::make_unique<
StreamHandlerFunctions<EncodableValue>>(
	[plugin_pointer = plugin.get()](
		const EncodableValue* arguments,
		std::unique_ptr<EventSink<EncodableValue>>&& events)
		-> std::unique_ptr<StreamHandlerError<EncodableValue>> {
			return plugin_pointer->OnListen(arguments, std::move(events));
	},
	[plugin_pointer = plugin.get()](const EncodableValue* arguments)
		-> std::unique_ptr<StreamHandlerError<EncodableValue>> {
			return plugin_pointer->OnCancel(arguments);
	});
// 創(chuàng)建EventChannel對(duì)象
auto eventChannel = std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
	registrar->messenger(), eventChannelName,
	&flutter::StandardMethodCodec::GetInstance());
// 把通道設(shè)置給插件
eventChannel->SetStreamHandler(std::move(eventHandler));

最后我們還需要把插件注冊(cè)進(jìn)項(xiàng)目中

registrar->AddPlugin(std::move(plugin));
  • 如何處理消息? 在上面創(chuàng)建的過(guò)程中,其實(shí)已經(jīng)把處理方法的傳遞給插件了。
// MethodChannel的處理
// result即通信的對(duì)象
void XXXPlugin::HandleMethodCall(
	const flutter::MethodCall<flutter::EncodableValue>& method_call,
	std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
        // 匹配通信的接口
	if (method_call.method_name().compare("getPlatformVersion") == 0) {
		std::ostringstream version_stream;
		version_stream << "Windows ";
		if (IsWindows10OrGreater()) {
			version_stream << "10+";
		}
		else if (IsWindows8OrGreater()) {
			version_stream << "8";
		}
		else if (IsWindows7OrGreater()) {
			version_stream << "7";
		}
                // 通過(guò)result->Succes回復(fù)消息
		result->Success(flutter::EncodableValue(version_stream.str()));
	} else {
		result->NotImplemented();
	}
}
// 主動(dòng)向Flutter端發(fā)送消息
std::unique_ptr < flutter::StreamHandlerError<flutter::EncodableValue>> XXXPlugin::OnListen(const flutter::EncodableValue* arguments,
	std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&& events) {
	// 主動(dòng)發(fā)送
	events_.reset(events.release());
	return nullptr;
}
// Flutter取消監(jiān)聽(tīng)時(shí)觸發(fā)
std::unique_ptr < flutter::StreamHandlerError<flutter::EncodableValue>> UsbToolPlugin::OnCancel(const flutter::EncodableValue* arguments) {
	return nullptr;
}

BasicMessageChannel我暫時(shí)還沒(méi)有用過(guò),這里就不做記錄了。但是看C++的api,還是很簡(jiǎn)單就能找到的。至于Flutter端的,無(wú)需多言。只要通信層連通了,其他想怎么玩都可以。

Windows插件的一些坑

這是本篇文章的重點(diǎn)。我們都知道Flutter是單線程的機(jī)制,來(lái)到原生平臺(tái)也一樣,Platform是運(yùn)行在Flutter的主線程的,自然是不能做任何耗時(shí)的,不然會(huì)卡住主線程,系統(tǒng)會(huì)把我們認(rèn)為無(wú)響應(yīng)的應(yīng)用,從而殺死應(yīng)用。

我們經(jīng)常會(huì)在使用windows插件時(shí),感覺(jué)點(diǎn)擊卡頓,其實(shí)就是很多插件沒(méi)有做這個(gè)處理,導(dǎo)致事件隊(duì)列等待調(diào)度。這主要是因?yàn)樵趙indows的開(kāi)發(fā)習(xí)慣上,耗時(shí)操作會(huì)丟到子線程異步執(zhí)行,然后主線程如何等待執(zhí)行結(jié)果?使用while一直去查詢是否執(zhí)行完成,這在windows上成為掛起。

不過(guò)一個(gè)有趣的現(xiàn)象是:當(dāng)有耗時(shí)操作的時(shí)候,F(xiàn)lutter的動(dòng)畫是可以流程播放的,但是點(diǎn)擊事件卻卡住了,這時(shí)候C++的同學(xué)就會(huì)扯,你看動(dòng)畫都是流程的,問(wèn)題肯定出在Flutter上?其實(shí)是因?yàn)閯?dòng)畫在Flutter中屬于微任務(wù),它的優(yōu)先級(jí)是高于事件隊(duì)列的。而while也是分配到事件隊(duì)列中,所以動(dòng)畫優(yōu)先執(zhí)行,點(diǎn)擊卻需要一直等到while結(jié)束。

在Android中,為了避免這個(gè)問(wèn)題,我們一般會(huì)使用協(xié)程,把耗時(shí)操作丟給協(xié)程,讓系統(tǒng)幫我們進(jìn)行任務(wù)調(diào)度,通過(guò)await拿到執(zhí)行完之后的結(jié)果,再把結(jié)果返回給dart層。整個(gè)機(jī)制其實(shí)還是保留了flutter的單線程機(jī)制,從而避免了卡頓問(wèn)題。

在Windows端,其實(shí)也有協(xié)程這個(gè)概念,比如WinRT、C++都有提供協(xié)程的能力。但問(wèn)題在于協(xié)程這個(gè)東西,對(duì)于C++來(lái)說(shuō)太新了,同時(shí)C++的歷史包袱實(shí)在太重,到現(xiàn)在還是用著很老版本的庫(kù)。這就導(dǎo)致很多C++的庫(kù)沒(méi)辦法遷移到協(xié)程這種方式,至少在我現(xiàn)在的業(yè)務(wù)中,切換成本極高,幾乎沒(méi)辦法完成。

但問(wèn)題總得解決,目前我們主要使用異步通知的方式,來(lái)解決這個(gè)問(wèn)題。此異步是真異步,非flutter單線程任務(wù)調(diào)度的異步。我們會(huì)把耗時(shí)的操作丟給子線程,但是我們不再通過(guò)while進(jìn)行異步轉(zhuǎn)同步,而是在子線程中,主動(dòng)通過(guò)channel去通知會(huì)Dart層。

if (*method == "getAsync") {
            async_pipe_stream_->Get(request, std::bind(&XXXPlugin::OnResponse, this, std::placeholders::_1, *uuid));
            // 直接返回true,但真正的執(zhí)行結(jié)果再OnResponse中主動(dòng)返回
            result->Success(EncodableValue(true)); 
            return;
        }

在插件的dart代碼中,我們需要主動(dòng)創(chuàng)建一個(gè)MethodChannel的接收器,異步接收到后,通過(guò)執(zhí)行業(yè)務(wù)端傳入的回調(diào)通知回去。

class NativePlugin {
  static const MethodChannel _channel =
      MethodChannel('com.open.flutter/xxx/xxx');
  static NativePlugin? _instance;
  // 獲取實(shí)例,單例
  static NativePlugin getInstance({String defaultToken = _token}) {
    _instance ??= NativePlugin._internal(defaultToken);
    return _instance!;
  }
  // 私有命名構(gòu)造函數(shù),做一次初始化
  NativePlugin._internal(String defaultToken) {
    _defaultToken = defaultToken;
    _channel.setMethodCallHandler((MethodCall call) async {
      if (call.method == 'onResponse') {
        final arguments = Map<String, dynamic>.from(call.arguments);
        // 執(zhí)行業(yè)務(wù)端傳入的回調(diào)
        await _onResponse(arguments);
      }
    });
  }

插件的Flutter層需要接收/維護(hù)回調(diào)列表,不過(guò)此方式有隱患,傳入的回調(diào)容易造成閉包問(wèn)題,增加一些內(nèi)存泄露的風(fēng)險(xiǎn);

但是對(duì)于沒(méi)辦法使用協(xié)程的C++插件來(lái)說(shuō),此方案確實(shí)可以解決不少問(wèn)題。

以上就是Flutter桌面開(kāi)發(fā)windows插件開(kāi)發(fā)的詳細(xì)內(nèi)容,更多關(guān)于Flutter windows插件開(kāi)發(fā)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論