自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Flutter混編工程之高速公路Pigeon

網(wǎng)絡(luò) 通信技術(shù)
Pigeon的存在就是為了解決多端通信的開發(fā)成本。其核心原理就是通過一套協(xié)議來生成多端的代碼,這樣多端只需要維護一套協(xié)議即可,其它代碼都可以通過Pigeon來自動生成,這樣就保證了多端的統(tǒng)一。

前面我們講到了Flutter與原生通信使用的是BasicMessageChannel,完全實現(xiàn)了接口解耦,通過協(xié)議來進行通信,但是這樣的一個問題是,多端都需要維護一套協(xié)議規(guī)范,這樣勢必會導(dǎo)致協(xié)作開發(fā)時的通信成本,所以,F(xiàn)lutter官方給出了Pigeon這樣一個解決方案。

Pigeon的存在就是為了解決多端通信的開發(fā)成本。其核心原理就是通過一套協(xié)議來生成多端的代碼,這樣多端只需要維護一套協(xié)議即可,其它代碼都可以通過Pigeon來自動生成,這樣就保證了多端的統(tǒng)一。

官方文檔如下所示。

https://pub.flutter-io.cn/packages/pigeon/install

引入

首先,需要dev_dependencies中引入Pigeon:

dev_dependencies:
pigeon: ^1.0.15

接下來,在Flutter的lib文件夾同級目錄下,創(chuàng)建一個.dart文件,例如schema.dart,這里就是通信的協(xié)議文件。

例如我們需要多端統(tǒng)一的一個實體:Book,如下所示。

import 'package:pigeon/pigeon.dart';

class Book {
String? title;
String? author;
}

@HostApi()
abstract class NativeBookApi {
List<Book?> getNativeBookSearch(String keyword);

void doMethodCall();
}

這就是我們的協(xié)議文件,其中@HostApi,代表從Flutter端調(diào)用原生側(cè)的方法,如果是@FlutterApi,那么則代表從原生側(cè)調(diào)用Flutter的方法。

生成

執(zhí)行下面的指令,就可以讓Pigeon根據(jù)協(xié)議來生成相應(yīng)的代碼,下面的這些配置,需要指定一些文件目錄和包名等信息,我們可以將它保存到一個sh文件中,這樣更新后,只需要執(zhí)行下這個sh文件即可。

flutter pub run pigeon \
--input schema.dart \
--dart_out lib/pigeon.dart \
--objc_header_out ios/Runner/pigeon.h \
--objc_source_out ios/Runner/pigeon.m \
--java_out ./android/app/src/main/java/dev/flutter/pigeon/Pigeon.java \
--java_package "dev.flutter.pigeon"

這里面比較重要的就是導(dǎo)入schema.dart文件,作為協(xié)議,再指定Dart、iOS和Android代碼的輸出路徑即可。

正常情況下,生成完后的代碼就可以直接使用了。

Pigeon生成的代碼是Java和OC,主要是為了能夠兼容更多的項目。你可以將它轉(zhuǎn)化為Kotlin或者Swift。

使用就以上面這個例子,我們來看下如何根據(jù)Pigeon生成的代碼來進行跨端通信。

首先,在Android代碼中,會生成一個同名協(xié)議的接口,NativeBookApi,對應(yīng)上面HostApi注解標記的協(xié)議名。在FlutterActivity的繼承類中,創(chuàng)建這個接口的實現(xiàn)類。

private class NativeBookApiImp(val context: Context) : Api.NativeBookApi {

override fun getNativeBookSearch(keyword: String?): MutableList<Api.Book> {
val book = Api.Book().apply {
title = "android"
author = "xys$keyword"
}
return Collections.singletonList(book)
}

override fun doMethodCall() {
context.startActivity(Intent(context, FlutterMainActivity::class.java))
}
}

這里順便提一下,engine使用FlutterEngineGroup的方式進行創(chuàng)建,如果是其它方式,按照不同的方法獲取engine對象即可。

class SingleFlutterActivity : FlutterActivity() {

val engine: FlutterEngine by lazy {
val app = activity.applicationContext as QDApplication
val dartEntrypoint =
DartExecutor.DartEntrypoint(
FlutterInjector.instance().flutterLoader().findAppBundlePath(), "main"
)
app.engines.createAndRunEngine(activity, dartEntrypoint)
}

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
Api.NativeBookApi.setup(flutterEngine.dartExecutor, NativeBookApiImp(this))
}

override fun provideFlutterEngine(context: Context): FlutterEngine? {
return engine
}

override fun onDestroy() {
super.onDestroy()
engine.destroy()
}
}

初始化Pigeon的核心方法就是NativeBookApi中的setup方法,傳入engine和協(xié)議的實現(xiàn)即可。

接下來,我們來看下如何在Flutter中調(diào)用這個方法,在有Pigeon之前,我們都是通過Channel,創(chuàng)建String類型的協(xié)議名來通信的,現(xiàn)在有了Pigeon之后,這些容易出錯的String就都被隱藏起來了,全部變成了正常的方法調(diào)用。

在Flutter中,Pigeon自動創(chuàng)建了NativeBookApi類,而不是Android中的接口,在類中已經(jīng)生成了getNativeBookSearch和doMethodCall這些協(xié)議中定義的方法。

List<Book?> list = await api.getNativeBookSearch("xxx");
setState(() => _counter = "${list[0]?.title} ${list[0]?.author}");

通過await就可以很方便的進行調(diào)用了。可見,通過Pigeon進行封裝后,跨端通信完全被協(xié)議所封裝了,同時也隱藏了各種String的處理,這樣就進一步降低了人工出錯的可能性。

優(yōu)化

在實際的使用中,F(xiàn)lutter調(diào)用原生方法來獲取數(shù)據(jù),原生側(cè)處理好數(shù)據(jù)后回傳給Flutter,所以在Pigeon生成的Android代碼中,協(xié)議函數(shù)的實現(xiàn)是一個帶返回值的方法,如下所示。

override fun getNativeBookSearch(keyword: String?): MutableList<Api.Book> {
val book = Api.Book().apply {
title = "android"
author = "xys$keyword"
}
return Collections.singletonList(book)
}

這個方法本身沒有什么問題,假如是網(wǎng)絡(luò)請求,可以使用OKHttp的success和fail回調(diào)來進行處理,但是,如果要使用協(xié)程呢?

由于協(xié)程破除了回調(diào),所以無法在Pigeon生成的函數(shù)中使用,這時候,就需要修改協(xié)議,給方法增加一個@async注解,將它標記為一個異步函數(shù)。

我們修改協(xié)議,并重新生成代碼。

@HostApi()
abstract class NativeBookApi {
@async
List<Book?> getNativeBookSearch(String keyword);

void doMethodCall();
}

這時候你會發(fā)現(xiàn),NativeBookApi的實現(xiàn)函數(shù)中,帶返回值的函數(shù)已經(jīng)變成了void,同時提供了一個result變量來處理返回值的傳遞。

override fun getNativeBookSearch(keyword: String?, result: Api.Result<MutableList<Api.Book>>?)

這樣使用就非常簡單了,將返回值通過result塞回去就好了。

有了這個方法,我們就可以將Pigeon和協(xié)程配合起來使用,開發(fā)體驗瞬間上升。

private class NativeBookApiImp(val context: Context, val lifecycleScope: LifecycleCoroutineScope) : Api.NativeBookApi {
override fun getNativeBookSearch(keyword: String?, result: Api.Result<MutableList<Api.Book>>?) {
lifecycleScope.launch {
try {
val data = RetrofitClient.getCommonApi().getXXXXList().data
val book = Api.Book().apply {
title = data.tagList.toString()
author = "xys$keyword"
}
result?.success(Collections.singletonList(book))
} catch (e: Exception) {
e.printStackTrace()
}
}
}

override fun doMethodCall() {
context.startActivity(Intent(context, FlutterMainActivity::class.java))
}
}

協(xié)程+Pigeon YYDS。

這里只介紹了Flutter調(diào)用Android的場景,實際上Android調(diào)用Flutter也只是換了個方向而已,代碼都是類似的,這里不贅述了,那iOS呢?——我寫Flutter,關(guān)iOS什么事。

拆解

在了解了Pigeon如何使用之后,我們來看下,這只「鴿子」到底做了些什么。

從宏觀上來看,不管是Dart端還是Android端,都是生成了三類東西。

  • 數(shù)據(jù)實體類,例如上面的Book類
  • StandardMessageCodec,這是BasicMessageChannel的傳輸編碼類
  • 協(xié)議接口\類,例如上面的NativeBookApi

在Dart中,數(shù)據(jù)實體會自動幫你生成encode和decode的代碼,這樣你獲取出來的數(shù)據(jù)就不再是Channel中的Object類型了,而是協(xié)議中定義的類型,極大的方便了開發(fā)者。

class Book {
String? title;
String? author;

Object encode() {
final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
pigeonMap['title'] = title;
pigeonMap['author'] = author;
return pigeonMap;
}

static Book decode(Object message) {
final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
return Book()
..title = pigeonMap['title'] as String?
..author = pigeonMap['author'] as String?;
}
}

在Android中,也是做的類似的操作,可以理解為用Java翻譯了一遍。

下面是Codec,StandardMessageCodec是BasicMessageChannel的標準編解碼器,傳輸?shù)臄?shù)據(jù)需要實現(xiàn)它的writeValue和readValueOfType方法。

class _NativeBookApiCodec extends StandardMessageCodec {
const _NativeBookApiCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is Book) {
buffer.putUint8(128);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
}
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
return Book.decode(readValue(buffer)!);

default:
return super.readValueOfType(type, buffer);

}
}
}

同樣的,Dart和Android代碼幾乎一致,也很好理解,畢竟是一套協(xié)議,規(guī)則是一樣的。

下面就是Pigeon的核心了,我們來看具體的協(xié)議是如何實現(xiàn)的,首先來看下Dart中是如何實現(xiàn)的,由于我們是從Flutter中調(diào)用Android中的代碼,所以按照Channel的原理來說,我們需要在Dart中申明一個Channel,并處理其返回的數(shù)據(jù)。

如果你熟悉Channel的使用,那么這段代碼應(yīng)該是比較清晰的。

下面再來看看Android中的實現(xiàn)。Android側(cè)是事件的處理者,所以需要實現(xiàn)協(xié)議的具體內(nèi)容,這就是我們前面實現(xiàn)的接口,另外,還需要添加setMessageHandler來處理具體的協(xié)議。

這里有點意思的地方是那個Reply類的封裝。

public interface Result<T> {
void success(T result);
void error(Throwable error);
}

前面我們說了,在Pigeon中可以通過@async來生成異步接口,這個異步接口的實現(xiàn),實際上就是這里處理的。

看到這里,你應(yīng)該幾乎就了解了Pigeon到底是如何工作的了,說白了實際上就是通過build_runner來生成這些代碼,把臟活累活都自己吞下去了,我們看見的,實際上就是具體協(xié)議類的實現(xiàn)和調(diào)用。

題外話

所以說,Pigeon并不是什么非常高深的內(nèi)容,但卻是Flutter混編的一個非常重要的思想,或者說是Flutter團隊的一個指導(dǎo)思想,那就是通過「協(xié)議」「模板」來生成相關(guān)的代碼,類似的還有JSON解析的例子,實際上也是如此。

再講的多一點,Android模塊之間的解耦、模塊化操作,實際上是不是也能通過這種方式來處理呢?所以說,大道至簡,殊途同歸,軟件工程做到最后,實際上思想都是類似的,萬物斗轉(zhuǎn)星移,唯有思想永恒。


責(zé)任編輯:武曉燕 來源: 群英傳
相關(guān)推薦

2022-02-10 08:44:52

Flutter通訊Name

2010-06-01 13:25:59

視頻會議高速公路捷思銳科技

2017-12-26 17:22:14

高速公路移動支付收費

2011-06-02 16:00:52

2016-07-13 14:00:47

銳捷網(wǎng)絡(luò)高速公路機電工程

2012-08-10 10:34:39

VMware大數(shù)據(jù)云計算

2014-10-16 14:11:59

智慧城市華為

2017-02-13 20:43:36

高速公路大數(shù)據(jù)

2013-10-17 17:28:01

智能電網(wǎng)華為

2010-10-08 21:21:15

光纖

2015-06-15 15:00:25

高速公路調(diào)度指揮系統(tǒng)首發(fā)集團華為

2011-10-26 10:30:48

Wi-Fi5GHz

2010-09-01 21:23:53

無線網(wǎng)狀網(wǎng)MeshStrix

2019-12-13 10:20:52

迪普科技

2018-04-10 14:25:30

大數(shù)據(jù)高速公路數(shù)據(jù)存儲

2019-01-23 09:00:29

自動駕駛人工智能

2015-12-02 14:28:08

NGN交換網(wǎng)絡(luò)華為

2010-08-31 12:09:38

無線網(wǎng)狀網(wǎng)MeshStrix
點贊
收藏

51CTO技術(shù)棧公眾號