Flutter 優(yōu)化 async 與 isolate

image

原本 Flutter 寫起來很開心潮针,任何耗時較長的工作赖淤,就放到一個

Future<T> someFunction(…) async {…}

中就好缸匪,不需要像 Android 里面還需要 rxJava 或是自己建 thread(線程) 來處理。
Ios中一樣是要用 thread(線程) 來處理
但是后來發(fā)現(xiàn)其實(shí)沒有那么單純。就像下面這篇文章的標(biāo)題一樣:剛接觸 Flutter 的人通常不會管到 asynchronous 的問題儡蔓,直到 UI 開始變得很卡的時候。

isolate 是啥

我們都知道 dart 是單線程異步編程模型 這一點(diǎn) 和js 很像

我們首先介紹一下 isolate 是什么東西 雖然 dart 是單線程異步模型 但是 dart 是支持(多線程)的在 dart 中 有一個 模塊 就是 isolate (隔離) 我們的 程序 就是 運(yùn)行 在 main isolate 中 而且 這個 isolate (隔離) 和我們 了解的其他語言的 線程 還不一樣 它是不共享內(nèi)存的 而且 在 flutter 中所有的 channel 和原生通信部分的 引用都是在主線程注冊的 也就是 main isolate 才能調(diào)用到 (需要注意 在 非 main isolate 中無法調(diào)用 channel 通信方法 ) isolate 和 isolate 之間的 通信 是通過 ReceivePort 這個類

那么有啥問題

在 Dart 中 async 和 Future 無法解決所有耗時的工作疼邀。Dart 雖然支持 非同步執(zhí)行,但其實(shí)如果是透過 async keyword 的話召锈,只是把工作丟到同一個 event loop 中旁振, 讓他暫時不會卡住目前的工作 , 等到真的輪到它執(zhí)行的時候 涨岁,如果它真的很耗時拐袜,那 main isolate 還是會 freeze(凍結(jié)) 住的 (為什么會凍結(jié)? 主線程負(fù)責(zé) UI的渲染 工作 但是 如果 密集型計(jì)算 很耗時 假如 這個計(jì)算 占用 1s的時間 你的UI就會卡住1s) 梢薪。Dart 主要的 task 都是在 main isolate 中完成的蹬铺,isolate 像是個 single thread 的 process。如果真的想要讓某些工作能夠同時執(zhí)行秉撇,不要卡住 main isolate 的話甜攀,就得要自己產(chǎn)生新的 isolate 來執(zhí)行。但 isolate 又不是那么好寫琐馆,必須由ReceivePort來傳輸參數(shù)(線程之間的交互通訊)规阀。下面有個小示例:

Isolate isolate;

void startRealAsyncTask() async {
  // need a ReceivePort to receive messages.
  ReceivePort receivePort= ReceivePort(); 
  isolate = await Isolate.spawn(heavyTask, receivePort.sendPort);
  receivePort.listen((data) {
    stdout.write('RECEIVE: ' + data + ', ');
  });
}

void heavyTask(SendPort sendPort) {
  // doing something very heavy here.
  String msg = "I'm done";  
  sendPort.send(msg);
}

void stop() {  
  if (isolate != null) {
      stdout.writeln('killing isolate');
      isolate.kill(priority: Isolate.immediate);
      isolate = null;        
  }  
}

void main() async {
  stdout.writeln('spawning isolate...');
  await startRealAsyncTask();
  stdout.writeln('press enter key to quit...');
  await stdin.first;
  stop();
  stdout.writeln('goodbye!');
  exit(0);
}

好在針對一般需要比較多時間執(zhí)行的工作,Dart 提供了一個比較容易使用的 compute() function瘦麸,幫助開發(fā)者包裝自建 isolate 的繁雜流暢谁撼。

以下是個簡單的范例。原先第一行的 processImage() 因?yàn)樾枰槍D片的每個 pixel 做處理滋饲,所以會很花時間厉碟,如果只是單純用 async 的話,在執(zhí)行的時候依然會在 main isolate 做屠缭,造成 畫面(UI刷新)反應(yīng)很不流暢箍鼓。將它改成用 compute() 來調(diào)用后,Dart 會幫忙產(chǎn)生新的 isolate 同步執(zhí)行勿她。如此一來畫面就不會再卡卡的了袄秩。

static Image processImage(Image srcImage) {
  return srcImage.replaceColor(Colors.white, Colors.transparent);
}

Image _getTransparentBackgroundImage(Image srcImage) async {
  Image resultImage = await compute(processImage, srcImage);
  return resultImae;
}

將現(xiàn)有的 async function 改成 compute()調(diào)用,只是幾行 code 的事逢并,就可以解決在同一個 event loop 中 執(zhí)行影響其他 task 導(dǎo)致 不流暢 卡頓的問題之剧,但如同上面的例子所示, compute 呼叫的 function 必須是要是top-level function (全局函數(shù))或是 static (靜態(tài)方法)才行砍聊,而且 調(diào)用的方法本身 是 不支持 async function 的 已經(jīng) 大佬 提供 實(shí)現(xiàn)方案 但是 在 目前flutter的版本 中 并沒有合并 代碼 當(dāng)前 是 1.7版本 ** 實(shí)現(xiàn)的 pull地址 **

請求合并的 核心 代碼 如下

image.png

測試用例


image.png

由于還沒有合并到當(dāng)前flutter 版本中 不建議直接修改flutter 源碼雖然(可以 直接改) 所以我自己對 isolate ReceivePort 做了 簡單的封裝

實(shí)現(xiàn)線程管理器

import 'dart:isolate';
typedef LikeCallback = void Function(Object value);

class  ThreadManagement  {
  //entryPoint 必須是靜態(tài)方法
  static Future<Map>  runtask (void entryPoint(SendPort message), LikeCallback(Object value),{Object parameter})async{
    final response = ReceivePort();
    Isolate  d =  await Isolate.spawn(entryPoint, response.sendPort);
    // 調(diào)用sendReceive自定義方法
    if(parameter!=null){
       SendPort sendPort = await response.first;
       ReceivePort receivePort = ReceivePort();
       sendPort.send([parameter, receivePort.sendPort]);
       receivePort.listen((value){
         receivePort.close();
         d.kill();
         LikeCallback(value);
       });
       return {
         'isolate': d,
         "receivePort":receivePort,
       };
    }else{
      response.listen((value){
        response.close();
        d.kill();
        LikeCallback(value);
      });
      return {
        'isolate': d,
        "receivePort":response,
      };
    }
  }
}

編寫 任務(wù)函數(shù)(僅做參考)

 static void getbannerthread(SendPort port) async {
    var c  =  await HttpManager.isolationnetFetch(
        "http://118.25.61.120/api/v2/banner"
        ,null,
        null, new Options(method: 'GET'));
    port.send(c);
  }

port.send(c); 是回調(diào)計(jì)算結(jié)果

調(diào)用任務(wù)

 ThreadManagement.runtask(API.getbannerthread, (value){
      if(value != null){
             //業(yè)務(wù)邏輯
      }
    });

帶參數(shù)任務(wù)

  static getVideolisttask(SendPort port) async {
    ReceivePort receivePort =ReceivePort();
    port.send(receivePort.sendPort);
    // 監(jiān)聽外界調(diào)用
    await for (var msg in receivePort) {
      Map requestURL =msg[0];
      SendPort callbackPort =msg[1];
      receivePort.close();
      
      
      var res = await HttpManager.isolationnetFetch(
          "http://xxxxxx?type="+requestURL["type"]+"&after="+requestURL["after"]
          ,null,
          null, new Options(method: 'GET'));


      callbackPort.send(res);
    }
  }

執(zhí)行帶參數(shù)的任務(wù)

 ThreadManagement.runtask(API.getVideolisttask, (value){
      if(value != null){
//業(yè)務(wù)邏輯
      }
    },parameter: {
      "type":"hot",
      "after":"1"
    });

轉(zhuǎn)載:姜姜和張張
原文地址
著作權(quán)歸作者所有背稼。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處玻蝌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蟹肘,一起剝皮案震驚了整個濱河市词疼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帘腹,老刑警劉巖贰盗,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異阳欲,居然都是意外死亡舵盈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門球化,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秽晚,“玉大人,你說我怎么就攤上這事筒愚「坝” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵巢掺,是天一觀的道長句伶。 經(jīng)常有香客問我,道長址遇,這世上最難降的妖魔是什么熄阻? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮倔约,結(jié)果婚禮上秃殉,老公的妹妹穿的比我還像新娘。我一直安慰自己浸剩,他們只是感情好钾军,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绢要,像睡著了一般吏恭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上重罪,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天樱哼,我揣著相機(jī)與錄音,去河邊找鬼剿配。 笑死搅幅,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的呼胚。 我是一名探鬼主播茄唐,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蝇更!你這毒婦竟也來了沪编?” 一聲冷哼從身側(cè)響起呼盆,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚁廓,沒想到半個月后访圃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡相嵌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年挽荠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片平绩。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖漠另,靈堂內(nèi)的尸體忽然破棺而出捏雌,到底是詐尸還是另有隱情,我是刑警寧澤笆搓,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布性湿,位于F島的核電站,受9級特大地震影響满败,放射性物質(zhì)發(fā)生泄漏肤频。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一算墨、第九天 我趴在偏房一處隱蔽的房頂上張望宵荒。 院中可真熱鬧,春花似錦净嘀、人聲如沸报咳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暑刃。三九已至,卻和暖如春膜眠,著一層夾襖步出監(jiān)牢的瞬間岩臣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工宵膨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留架谎,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓柄驻,卻偏偏與公主長得像狐树,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鸿脓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355