(五)flutter入門之dart中的并發(fā)編程救军、異步和事件驅(qū)動詳解

上篇博客介紹了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交互.png

從上圖中我們可以看到兩個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í)行流程圖如下:

isolate中的消息機制.png

從上圖中我們可以很清晰的看出來兩個隊列之間的微妙的執(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的反射很像)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抄淑,一起剝皮案震驚了整個濱河市屠凶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肆资,老刑警劉巖矗愧,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異郑原,居然都是意外死亡唉韭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門犯犁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來属愤,“玉大人,你說我怎么就攤上這事酸役∽≈睿” “怎么了驾胆?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贱呐。 經(jīng)常有香客問我丧诺,道長,這世上最難降的妖魔是什么奄薇? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任驳阎,我火速辦了婚禮,結(jié)果婚禮上馁蒂,老公的妹妹穿的比我還像新娘呵晚。我一直安慰自己,他們只是感情好远搪,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布劣纲。 她就那樣靜靜地躺著,像睡著了一般谁鳍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上劫瞳,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天倘潜,我揣著相機與錄音,去河邊找鬼志于。 笑死涮因,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的伺绽。 我是一名探鬼主播养泡,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奈应!你這毒婦竟也來了澜掩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤杖挣,失蹤者是張志新(化名)和其女友劉穎肩榕,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惩妇,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡株汉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了歌殃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乔妈。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖氓皱,靈堂內(nèi)的尸體忽然破棺而出路召,到底是詐尸還是另有隱情贮懈,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布优训,位于F島的核電站朵你,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏揣非。R本人自食惡果不足惜抡医,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望早敬。 院中可真熱鬧忌傻,春花似錦、人聲如沸搞监。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琐驴。三九已至俘种,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绝淡,已是汗流浹背宙刘。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牢酵,地道東北人悬包。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像馍乙,于是被迫代替她去往敵國和親布近。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 以下內(nèi)容從官網(wǎng)得到:https://webdev.dartlang.org/articles/performanc...
    None_Ling閱讀 37,034評論 7 34
  • Mixin 備注:子類沒有重寫超類A方法的前提下丝格,如果2個或多個超類擁有相同簽名的A方法撑瞧,那么子類會以繼承的最后一...
    小流星雨閱讀 1,342評論 0 2
  • 《The Event Loop and Dart》譯文 原文:https://webdev.dartlang.or...
    無玄閱讀 867評論 0 2
  • 前言 我們所熟悉的前端開發(fā)框架大都是事件驅(qū)動的。事件驅(qū)動意味著你的程序中必然存在事件循環(huán)和事件隊列铁追。事件循環(huán)會不停...
    HowHardCanItBe閱讀 15,173評論 6 29
  • “月朗星稀”琅束,這是很多星月菩提愛好者所極力追求的品質(zhì)扭屁。月孔明朗、方正涩禀,星眼稀疏料滥、細小,這是星月菩提子品相優(yōu)異的體現(xiàn)...
    碎星星_e665閱讀 5,114評論 0 0