上篇博客介紹了dart提供的非常靈活的類操作,接下來介紹dart中的并發(fā)編程倘零、異步操作以及dart中的事件驅(qū)動
并發(fā)編程
我們知道dart是個單線程的語言唱遭,和js一樣,所以dart中不存在多線程操作呈驶,那么我們?nèi)绻龅蕉嗳蝿?wù)并行的場景拷泽,該如何去做呢?dart中提供了一個類似于java新線程但不能共享內(nèi)存的獨立運行的worker 袖瞻,屬于一個新的獨立的Dart執(zhí)行環(huán)境--isolate司致,像我們執(zhí)行任務(wù)的時候默認的main方法就是一個默認的isolate,可以看出如果我們想在dart中執(zhí)行多個并行的任務(wù)聋迎,可以選擇創(chuàng)建多個isolate來完成脂矫,那么isolate之間如何交互?isolate自身的任務(wù)又是如何處理的霉晕?
Isolate.spawn
在dart中庭再,一個Isolate對象其實就是一個isolate執(zhí)行環(huán)境的引用,一般來說我們都是通過當前的isolate去控制其他的isolate完成彼此之間的交互牺堰,而當我們想要創(chuàng)建一個新的Isolate可以使用Isolate.spawn方法獲取返回的一個新的isolate對象拄轻,我們來看下spawn方法的源碼
spawn<T>(
void entryPoint(T message),
T message, {
bool paused: false,
bool errorsAreFatal,
SendPort onExit,
SendPort onError
}
) → Future<Isolate>//可以看出來最終會返回一個Isolate對象,至于Future是什么伟葫,接下來會介紹
一般來說我們使用spawn創(chuàng)建的isolate自身帶有控制接口(control port )和可控制對象的能力(capability )恨搓,當然我們也可以不擁有這個能力,那么isolate之間是如何進行交互操作的筏养?我們看下流程圖
從上圖中我們可以看到兩個isolate之間使用SendPort相互發(fā)送消息斧抱,而isolate中也存在了一個與之對應(yīng)的ReceivePort接受消息用來處理,但是我們需要注意的是渐溶,ReceivePort和SendPort在每個isolate都有一對辉浦,只有同一個isolate中的ReceivePort才能接受到當前類的SendPort發(fā)送的消息并且處理(可以看出來谷歌這么設(shè)計的意義就是防止多個isolate之間接受器混亂),而isolate的spawn就是用來創(chuàng)建帶有控制能力的isolate掌猛,第二個參數(shù)就可以選擇傳遞當前Isolate的SendPort盏浙,交給新創(chuàng)建的實例,這樣新創(chuàng)建的實例就可以發(fā)送消息給原來的isolate荔茬,實現(xiàn)兩者之間通訊,接下來我們看一下Isolate交互的實例:
import 'dart:isolate';
int i;
void main() {
i = 10;
SendPort childSendPort;
//創(chuàng)建一個消息接收器--這里創(chuàng)建的是默認的main的isolate的竹海,我們可以稱之為主進程
ReceivePort receivePort = new ReceivePort();
//創(chuàng)建新的具有發(fā)送器的isolate,第一個參數(shù)是具有內(nèi)存隔離的新的isolate的具體業(yè)務(wù)邏輯函數(shù)慕蔚,第二個是創(chuàng)建的isolate的時候傳遞的參數(shù),一般我們傳遞當前isolate的發(fā)送器
Isolate.spawn(isolateMain, receivePort.sendPort);
//主進程接受持有主進程發(fā)送器的isolate發(fā)過來的消息
receivePort.listen((message) {
//其他的isolate可以選擇發(fā)過來自身的sendPort給主進程,則主進程isolate也可以向創(chuàng)建的isolate發(fā)送消息斋配,完成交互操作
if (message is SendPort) {
message.send("已收到子Isolate的發(fā)送器!!");
childSendPort =message;
} else {
print("接到子isolate消息:" + message);
//進行一次回復(fù)
if(childSendPort != null){
childSendPort.send('已收到你的消息');
}
}
});
}
/// 內(nèi)存隔離的新的isolate的具體業(yè)務(wù)邏輯函數(shù)
void isolateMain(SendPort sendPort) {
// isolate是內(nèi)存隔離的孔飒,i的值是在其他isolate定義的(默認都是主isolate環(huán)境)所以這里獲得null
print(i);//輸出:--->null
//當前isolate的消息接收器
ReceivePort receivePort = new ReceivePort();
//創(chuàng)建當前子isolate的時候傳遞的第二個參數(shù)(這里我們認為是該iso的發(fā)送器)灌闺,使用主iso的發(fā)送器將自身子iso的發(fā)送器發(fā)送過去,完成交互
sendPort.send(receivePort.sendPort);
// 測試向主isolate發(fā)送消息
sendPort.send("你收到我的消息了嗎?");
receivePort.listen((message) {
print("接到主isolate消息:" + message);
});
}
事件驅(qū)動
我們在上面有提到坏瞄,每一個isolate相當于一個完全獨立的dart執(zhí)行環(huán)境桂对,那么當前的環(huán)境中如果存在一些任務(wù),如果完全按照順序執(zhí)行鸠匀,豈不是會因為某個任務(wù)處理的時間過于久蕉斜,后面的任務(wù)來不及執(zhí)行?在單線程語言中缀棍,如果不做任何處理宅此,的確會出現(xiàn)這種問題,熟悉js的人都知道js的異步操作很優(yōu)秀爬范,原因在于js有著不錯的事件驅(qū)動進行任務(wù)調(diào)度父腕,提高任務(wù)執(zhí)行的效率,尤其是最近熱門的node.js青瀑,更是把事件驅(qū)動做到極致璧亮,而dart作為一個優(yōu)秀的單線程語言,自然不可能缺少事件驅(qū)動這個優(yōu)秀的特性斥难,在dart中事件驅(qū)動是存在于isolate中的杜顺,也就是說,我們每一個新的isolate都有一個獨立的完整的event-loop蘸炸,而每一個Loop中又包含了兩個隊列躬络,其中一個隊列叫microtask queue(微任務(wù)隊列),該隊列的執(zhí)行優(yōu)先級最高搭儒,而另外一個隊列event queue(事件隊列)屬于普通的事件隊列穷当,兩個隊列依靠著固定的執(zhí)行流程完成了整個的dart任務(wù)執(zhí)行機制,事件驅(qū)動的執(zhí)行流程圖如下:
從上圖中我們可以很清晰的看出來兩個隊列之間的微妙的執(zhí)行流程:
- microtask-queue的執(zhí)行優(yōu)先于event-queue淹禾,并且在microtask-queue中是輪詢執(zhí)行的馁菜,也就是說,microtask-queue中所有的任務(wù)執(zhí)行完成以后才會去event-queue中執(zhí)行任務(wù)
- event-queue中的任務(wù)執(zhí)行級別最低铃岔,每一個任務(wù)執(zhí)行完畢以后汪疮,都會重新去輪詢一次microtask-queue,如果這個時候microtask-queue中有了新的任務(wù)毁习,那么不用說智嚷,肯定會把microtask-queue再次全部執(zhí)行完再回到event-queue中執(zhí)行
并且我們平時執(zhí)行的任務(wù)絕大多數(shù)都是在event-queue中執(zhí)行的,所以我們正常的任務(wù)如果想要執(zhí)行纺且,microtask-queue中盡量不要加入太多復(fù)雜的業(yè)務(wù)操作盏道,但同時我們也可以看出來,dart中存在著‘插隊’機制载碌,即我們希望某個任務(wù)優(yōu)先于其他任務(wù)先去執(zhí)行猜嘱,我們可以選擇將任務(wù)丟進microtask-queue優(yōu)先處理衅枫,接下來我們先看一個案例:
import 'dart:io';
void main(){
new File("C:\Users\Administrator\Desktop\http通用類.txt").readAsString().then((content){
print(content);//按理來說應(yīng)該會輸出文件中每一行的數(shù)據(jù)的,但是一直不輸出
});
while(true){}
}
從上面的案例的結(jié)果可以看出來朗伶,程序一直阻塞著弦撩,永遠不執(zhí)行文件io輸出的內(nèi)容,這是為什么呢论皆?原因很簡單益楼,因為io流是異步的操作,并且than方法會把任務(wù)加入到event-queue纯丸,這個時候main函數(shù)的while循環(huán)早于文件io執(zhí)行偏形,就會一直阻塞程序,所以在dart中合理分配microtask-queue和event-queue很重要觉鼻,同樣因為我們這里用了異步的任務(wù)俊扭,導(dǎo)致了任務(wù)隊列執(zhí)行順序的變化,所以合理運用同步任務(wù)和異步任務(wù)在dart開發(fā)中也格外重要坠陈。接下來我們學(xué)習dart中的異步任務(wù)和執(zhí)行器Future萨惑。
Future:
在java開發(fā)中,我們進行并發(fā)編程的時候仇矾,經(jīng)常會使用future來處理任務(wù)庸蔼,獲取返回的結(jié)果,或者延遲處理任務(wù)等操作贮匕,而在dart中姐仅,我們執(zhí)行一個任務(wù)同樣也可以使用future來處理,而Future默認情況下執(zhí)行的是一個event-queue任務(wù)刻盐,也就是和我們正常執(zhí)行的業(yè)務(wù)邏輯一樣的任務(wù)級別掏膏,然而future提供了多個api和特性來完成任務(wù)隊列的使用,例如我們在js開發(fā)的時候經(jīng)常使用ajax的then函數(shù)進行回調(diào)敦锌,then中的參數(shù)即為當前任務(wù)執(zhí)行完的參數(shù)馒疹,我們可以在then中處理接下來的業(yè)務(wù),future也具有同樣的編程特性乙墙,then函數(shù)和catch函數(shù)分別處理任務(wù)返回的結(jié)果以及出現(xiàn)的異常颖变,同樣的,future作為一個任務(wù)操作者听想,也提供了延遲任務(wù)和可以加入microtask-queue的特殊任務(wù)(Future.microtask函數(shù))腥刹,這樣我們就可以通過future完成一系列的任務(wù)操作,接下來我們看一個Future操作的案例,從而熟悉Future的常用操作:
import 'dart:async';
void main(){
Future.delayed(new Duration(seconds:3),(){
//3s以后執(zhí)行的普通任務(wù)
}).then((value){
//then函數(shù)中可以獲取最終執(zhí)行完返回的結(jié)果哗魂,并且需要注意的是then可以連續(xù)回調(diào)使用完成一系列操作
}).catchError((error){
//catchError函數(shù)中會捕捉當前任務(wù)執(zhí)行流程中的所有的異常肛走,包括每一步的then函數(shù)中出現(xiàn)的異常,一旦有異常就會停止任務(wù)執(zhí)行录别,直接進入當前函數(shù)
});
//future內(nèi)部就是調(diào)用了scheduleMicrotask函數(shù)朽色,用來將當前任務(wù)加入到microtask-queue中,實現(xiàn)'插隊'功能
Future.microtask((){
//優(yōu)先執(zhí)行的業(yè)務(wù)邏輯
});
Future.sync((){
//同步運行的任務(wù):同步運行指的是構(gòu)造Future的時候傳入的函數(shù)是同步運行的,當前傳遞的函數(shù)可以一起執(zhí)行组题,和then不同的是葫男,then回調(diào)進來的函數(shù)是調(diào)度到微任務(wù)隊列異步執(zhí)行的
});
//scheduleMicrotask((){
// print('a microtask');
//});
}
從上面的代碼中我們不難發(fā)現(xiàn)一個特點,F(xiàn)uture既然有豐富的任務(wù)相關(guān)的處理Api崔列,開發(fā)的過程中肯定會比較頻繁的使用梢褐,但是我們對Future熟悉以后,就會發(fā)現(xiàn)一個特點赵讯,F(xiàn)uture的很多Api都是類似構(gòu)造者模式(js中的promise模式)盈咳,我們可以無限的繼續(xù)回調(diào)下去,尤其是then函數(shù)边翼,我們在開發(fā)的過程中可能存在一個比較復(fù)雜的業(yè)務(wù)鱼响,如果不能使用promise的方式處理的話會是什么情況?我們來看一個假設(shè)的情況:
//我們先定義幾個連續(xù)的異步操作任務(wù),首先調(diào)用登錄-->獲取用戶信息-->保存信息
Future<String> login(String userName, String pwd){
//用戶登錄
}
Future<String> getUserInfo(String id){
//獲取用戶信息
}
Future saveUserInfo(String userInfo){
// 保存用戶信息
}
接著我們來按照這些方法進行業(yè)務(wù)開發(fā):
login("admin","123456").then((id){
getUserInfo(id).then((user){
saveUserInfo(userInfo).then((){
//這里再去執(zhí)行其他任務(wù)组底。丈积。。债鸡。
});
});
});
是不是發(fā)現(xiàn)了問題江滨?一個回調(diào)內(nèi)部調(diào)用另外一個,嵌套調(diào)用的次數(shù)太多了厌均,我們可能陷入了一個恐怖的回調(diào)地獄中唬滑,那么promise方式下的代碼開發(fā)就輕松太多了
void main(){
Future((){
//用戶登錄
}).then((value){
//獲取用戶信息
})
.then((value){
//保存用戶信息
})
.then((value){
//業(yè)務(wù)1
})
.then((value){
//業(yè)務(wù)2
})
.........//這里可以無限的回調(diào)處理下去
}
是不是整體看起來邏輯更清晰了?但是我們不禁犯難了棺弊,因為promise模式的確能改觀一部分問題晶密,但是當我們業(yè)務(wù)更加復(fù)雜的時候,依然會存在一次回調(diào)操作镊屎,看起來依然很復(fù)雜惹挟,那么又該如何呢?我們知道js為了改觀promise的這個弊端,在es7中引入了async/await 關(guān)鍵字來解決該問題缝驳,同樣dart中也引入了async/await可以更加優(yōu)雅的處理地獄回調(diào)
async/await
aync和await很明顯可以看出來连锯,一個是同步操作,一個是同步等待用狱,需要注意的是這兩個關(guān)鍵字不是單獨使用的运怖,需要兩個關(guān)鍵字一起配合完成整體的代碼同步操作,接下來我們通過案例來看看async/await為何能更優(yōu)雅的解決地獄回調(diào):
void main() async{
try{
//每一個方法前加入await代表當前方法是同步的夏伊,執(zhí)行完以后才會繼續(xù)執(zhí)行后續(xù)的操作
String id = await login("admin","123456");
String userInfo = await getUserInfo(id);
await saveUserInfo(userInfo);
} catch(e){
//錯誤處理
print(e);
}
}
Future<String> login(String userName, String pwd){
//用戶登錄
}
Future<String> getUserInfo(String id){
//獲取用戶信息
}
Future saveUserInfo(String userInfo){
// 保存用戶信息
}
當然摇展,dart中還提供了另外一個場景的實現(xiàn),比如我們可能需要某幾個任務(wù)在一個階段完成以后才可以執(zhí)行其他的任務(wù)溺忧,但是我們對于優(yōu)先執(zhí)行的幾個任務(wù)的執(zhí)行順序沒有強制要求咏连,但是我們要求必須是這幾個完成以后才能執(zhí)行其他的任務(wù)盯孙,這個時候,我們可以選擇按照順序去編寫Future任務(wù)祟滴,或者指定幾個Future.microtask任務(wù)優(yōu)先執(zhí)行振惰,但是在future中同樣提供了一個wait操作,可以同時執(zhí)行多個任務(wù)垄懂,等待全部完成后才會進行回調(diào)操作骑晶,案例如下:
void main(){
try{
//假設(shè)我們現(xiàn)在登錄和獲取用戶信息操作是一組,都執(zhí)行完畢以后才可以執(zhí)行保存用戶登錄成功的操作
Future.wait([loginFuture,getUserInfoFuture]).then((values){
//這里values是個數(shù)組草慧,分別是每一個任務(wù)返回的結(jié)果,
print(values[0]);//打印第一個任務(wù)的結(jié)果
saveUserInfo('admin');
});
} catch(e){
//錯誤處理
print(e);
}
}
Future loginFuture = Future<String>((){
//這里調(diào)用登錄操作
login('admin','123456');
});
String login(String userName, String pwd){
//登錄操作
}
bool getUserInfo(int id){
//獲取用戶信息
}
Future<String> getUserInfoFuture =Future((){
getUserInfo(1);
});
Future saveUserInfo(String userInfo){
// 保存用戶信息
}
注意:無論是在JavaScript還是Dart中桶蛔,async/await
都只是一個語法糖,編譯器或解釋器最終都會將其轉(zhuǎn)化為一個Promise(Future)的調(diào)用鏈
Stream
Stream操作也是dart中提供的用來處理異步操作的工具漫谷,和Future不同的是它可以接收多個異步操作的結(jié)果(無論成功或失斪欣住) ,我們可以理解為:執(zhí)行異步任務(wù)時抖剿,可以通過多次觸發(fā)成功或失敗事件來傳遞結(jié)果數(shù)據(jù)或錯誤異常 ,例如我們開發(fā)的過程中經(jīng)常見到的場景:多個文件讀寫朽寞,網(wǎng)絡(luò)下載可能會發(fā)起多個等,接下來我們根據(jù)一個案例分析一下Stream常用的操作:
void main(){
Stream.fromFutures([loginFuture,getUserInfoFuture,saveUserInfoFuture])
.listen((data){
//各個任務(wù)返回的結(jié)果的回調(diào)
},onError:((err){
//各個任務(wù)執(zhí)行失敗的回調(diào)
}),
onDone : ((){
//監(jiān)聽各個任務(wù)執(zhí)行的過程中完成的時候的回調(diào)
})
)
.onDone((){
//所有任務(wù)全部完成的回調(diào)
});
}
Future loginFuture = Future<String>((){
//這里調(diào)用登錄操作
login('admin','123456');
});
String login(String userName, String pwd){
//登錄操作
}
bool getUserInfo(int id){
//獲取用戶信息
}
Future<String> getUserInfoFuture =Future((){
getUserInfo(1);
});
Future saveUserInfoFuture = Future((){
saveUserInfo("admin");
});
void saveUserInfo(String userInfo){
}
可以看到listen方法中我們可以對每一個任務(wù)進行精細的回調(diào)處理斩郎,甚至所有任務(wù)執(zhí)行完畢以后脑融,我們還有cancel |pause | resume |onError |onDone 等回調(diào)可以分別對整個組任務(wù)執(zhí)行過程的不同階段進行精細度的處理,在上面我們使用了fromFutures方法使用Stream缩宜,除此之外肘迎,Stream使用過程中我們也經(jīng)常用fromFuture 方法用來處理單個Futrue/fromIterable 方法處理集合中的數(shù)據(jù)。當然锻煌,除了這種常規(guī)的Stream操作以外妓布,dart還提供了兩個專門操作/創(chuàng)建流的類,可以實現(xiàn)流操作的復(fù)雜操作宋梧。
StreamController
在dart中為我們設(shè)計了一個可以創(chuàng)建新的流用來處理任務(wù)的控制器類--StreamController匣沼,我們可以在StreamController上創(chuàng)建流,發(fā)送數(shù)據(jù)以及獲取數(shù)據(jù)進行處理等操作捂龄,接下來我們通過案例學(xué)習StreamController的常用操作:
//任意類型的流
StreamController controller = StreamController();
//監(jiān)聽這個流的出口释涛,當有data流出時,打印這個data
StreamSubscription subscription = controller.stream.listen((data){
print(data)倦沧;
});
//sink作為流的入口唇撬,使用了sink的add可以往當前流中塞入數(shù)據(jù),這個時候數(shù)據(jù)就可以被監(jiān)聽到
controller.sink.add(123);
controller.sink.add("abc");
//創(chuàng)建一條處理int類型的流
StreamController<int> numController = StreamController();
numController.sink.add(123);
Transforming an existing stream
在dart中展融,除了StreamController這種可以創(chuàng)建一個新的流的方式以外窖认,還提供了一個特殊的操作的工具集,可以將當前已有的流轉(zhuǎn)換為一個新的流--Transforming an existing stream,使用方法就是流自身提供的map()扑浸,where()烧给,expand(),和take()方法 首装,接下來我們看看這幾個方法有什么用處:
where:where的作用就是用來過濾當前流中一些不需要的數(shù)據(jù)创夜,比如我們當前需要獲取一個int流中的所有的奇數(shù)杭跪,這個時候就可以使用where進行過濾仙逻,做出一定的響應(yīng)事件,需要注意的是涧尿,where方法中會傳遞一個event系奉,無論我們需要還是不需要都會傳遞
take:take可以用來限制我們當前輸入到流的次數(shù),也就是觸發(fā)流傳輸?shù)氖录拇螖?shù)姑廉,比如存在一個場景缺亮,用戶輸入密碼三次以后就不可以輸入比較,這個時候就可以使用take限制數(shù)量為3桥言,即只能觸發(fā)三次
-
transform :transform是用來轉(zhuǎn)換流控制的方法萌踱,我們可以使用該方法和StreamTransformer 配合使用,實現(xiàn)我們自己對流操作的完全控制号阿,比如我們現(xiàn)在有需求對流中的數(shù)據(jù)進行不同的業(yè)務(wù)操作并鸵,案例如下:
import 'dart:async'; void main(){ StreamController<int> controller = StreamController<int>(); //transformer是對流數(shù)據(jù)控制的具體實現(xiàn),比如流中數(shù)據(jù)如何處理等 final transformer = StreamTransformer<int,String>.fromHandlers( handleData:(value, sink){ if(value==1){ //我們限制value==1的時候扔涧,觸發(fā)正確的操作 sink.add("操作正確"); } else{ sink.addError('操作失敗园担,再試一次吧'); } }); //創(chuàng)建流的時候調(diào)用transform將當前的流的操作交由控制器處理 controller.stream .transform(transformer) .listen( (data){ //控制器處理以后還能被sink.add添加進來,說明當前是正確的數(shù)據(jù)枯夜,在當前方法輸出 print(data); }, onError:(err){ //錯誤的操作會在當前方法中輸出:-->操作失敗弯汰,再試一次吧 print(err); }); controller.sink.add(50); }
Stream的多訂閱模式
在上面的案例上,我們使用過Stream用來執(zhí)行一些任務(wù),如果想要監(jiān)控Stream,可以使用listen來返回一個訂閱者StreamSubscription耿币,當然還可以使用onData方法重置數(shù)據(jù)(監(jiān)聽也會重置)禽额,但是我們需要注意的是Stream默認的情況下只能是單監(jiān)聽,例如:
import 'dart:async';
import 'dart:io';
void main(){
Stream<List<int>> stream = new File("C:/Users/Administrator/http通用類.txt").openRead();
StreamSubscription<List<int>> listen = stream.listen((List<int> bytes) {
print('開啟第一個監(jiān)聽');
});
//默認單訂閱模式下不可以開啟多個listener朴摊,即使是第一個監(jiān)聽已經(jīng)關(guān)閉停止也不可以開啟第二個監(jiān)聽,否則會輸出Bad state: Stream has already been listened to.的異常
//StreamSubscription<List<int>> listen2 = stream.listen((List<int> bytes) {
// print('開啟第二個監(jiān)聽');
//});
listen.onData((_){
print("重新開啟數(shù)據(jù)和監(jiān)聽。溪胶。。");
});
listen.onDone((){
//監(jiān)聽完成以后的操作
});
listen.onError((e,s){
//監(jiān)聽出現(xiàn)異常的操作
});
//暫停監(jiān)聽稳诚,如果后面沒有操作繼續(xù)監(jiān)聽哗脖,就會停止
listen.pause();
//暫停狀態(tài)下會恢復(fù)監(jiān)聽
listen.resume();
}
但是在開發(fā)的過程中,我們有很多場景下可能多個類都需要對這個事件進行監(jiān)聽操作或者要對這個事件進行不一樣的監(jiān)聽操作,這個時候我們就該開啟Stream的多訂閱廣播模式才避,開啟廣播模式橱夭,我們需要使用 Stream.asBroadcastStream()方法進行申明一個可以被多個監(jiān)聽監(jiān)控的Stream
import 'dart:async';
import 'dart:io';
void main(){
Stream<List<int>> stream = new File("C:/Users/Administrator/http通用類.txt").openRead().asBroadcastStream();
StreamSubscription<List<int>> listen = stream.listen((List<int> bytes) {
print("第一個監(jiān)聽");
});
//asBroadcastStream申明了一個可以多個訂閱者存在的監(jiān)聽操作,這個時候不會報任何異常桑逝,能正常輸出
StreamSubscription<List<int>> listen2 = stream.listen((List<int> bytes) {
print("第二個監(jiān)聽");
});
}
這樣的Stream就可以實現(xiàn)多個監(jiān)聽了棘劣,同樣的,我們不禁有一個思考楞遏,Stream實現(xiàn)多訂閱的情況下和我們原生安卓開發(fā)的EventBus庫的功能不是很類似嗎茬暇?那我們能否實現(xiàn)一個適合Dart的Event-bus呢?接下來我們來實現(xiàn)一個簡易的event-bus:
event_bus.dart文件代碼如下:
import 'dart:async';
class EventBus{
static EventBus _eventBus;
StreamController _streamController;
final Map<String,StreamController> _registerMap = new Map<String,StreamController>();
final String defName = "default";//默認的廣播
EventBus._();
static EventBus getDefault(){
if(_eventBus == null){
_eventBus = new EventBus._();
}
return _eventBus;
}
//使用eventBus的時候寡喝,需要注冊糙俗,可以指定注冊名,可以使用默認注冊
void register(listener,{String registerName}){
if(null ==registerName){
registerName = defName;
}
if(!_registerMap.containsKey(registerName)){
_streamController = StreamController.broadcast();
_registerMap[registerName] = _streamController;
}
_registerMap[registerName].stream.listen(listener);
}
//不使用的時候可以取消注冊
void unRegister({String registerName}){
if(null ==registerName){
registerName =defName;
}
_registerMap[registerName].close();
_registerMap.remove(registerName);
}
//針對當前注冊的流進行通訊预鬓,如果流不存在就不發(fā)消息巧骚,防止出現(xiàn)取消注冊以后報錯的情況,當然我們也可以在不存在的情況拋一個異常
void post(msg,{String registerName}){
if(null ==registerName){
registerName =defName;
}
if(_registerMap.containsKey(registerName)){
_registerMap[registerName].add(msg);
}
}
}
接下來是main.dart文件的代碼:
import 'event_bus.dart';
void main() {
EventBus.getDefault().register(onListener);
//發(fā)送消息
EventBus.getDefault().post('你猜猜我發(fā)了什么格二?');
//取消注冊
EventBus.getDefault().unRegister();
}
void onListener(msg){
print('接受來的消息為:'+msg);//最終輸出為:接受來的消息為:你猜猜我發(fā)了什么劈彪?
}
好了,至此顶猜,flutter入門之dart相關(guān)常見的內(nèi)容我們學(xué)習的差不多了沧奴,接下來就是開始flutter之旅了(由于flutter禁止了反射,所以針對dart中的反射等操作驶兜,這里我就不去進行深入討論扼仲,有原生開發(fā)java功底的可以自己查資料看下,和java的反射很像)