成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Flutter之SchedulerBinding簡析

BlackMass / 2660人閱讀

摘要:但是接下來并不是討論單線程如何方便開發(fā),而是要深入的調(diào)度器,看一下是如何安排任務(wù),調(diào)度工作??偨Y(jié)在大部分情況下,其實(shí)并不用擔(dān)心會(huì)像游戲一樣瘋狂消耗電量,消耗電量表現(xiàn)應(yīng)該跟原生沒有多大差別。

開始

在原生開發(fā)中(例如Android)都會(huì)強(qiáng)調(diào)不能阻塞主線程,但是開發(fā)中經(jīng)常會(huì)遇到發(fā)送請(qǐng)求或者操作數(shù)據(jù)庫等,這些操作都會(huì)阻塞主線程,幾乎唯一辦法就是用多線程處理這些工作;而在Flutter中就像跟在前端一樣,Dart也是單線程IO異步,剛才所說的這些操作既不會(huì)阻塞主線程也不會(huì)打斷你的代碼邏輯,所以在Flutter上開發(fā)有相當(dāng)高的效率。
但是接下來并不是討論單線程IO如何方便開發(fā),而是要深入Flutter的Scheduler(調(diào)度器),看一下Flutter是如何安排任務(wù),調(diào)度工作。

調(diào)度階段

在Flutter中有幾個(gè)調(diào)度階段:

transientCallbacks
主要處理動(dòng)畫計(jì)算,動(dòng)畫狀態(tài)的更新

midFrameMicrotasks
處理transientCallbacks階段觸發(fā)的Microtasks,啥是Microtasks?傳送門

persistentCallbacks
主要處理build/layout/paint

postFrameCallbacks
主要在下一幀之前,做一些清理工作或者準(zhǔn)備工作

idle
不產(chǎn)生Frame的空閑期,可以處理Tasks(由SchedulerBinding.scheduleTask觸發(fā)),microtasks(由scheduleMicrotask觸發(fā)),定時(shí)器的回調(diào),響應(yīng)事件處理(例如:用戶的輸入)

分析

這個(gè)幾個(gè)階段是如何定義出來的尼?
在SchedulerBinding實(shí)例化的時(shí)候:

void initInstances() {
    super.initInstances();
    _instance = this;
    ui.window.onBeginFrame = handleBeginFrame;
    ui.window.onDrawFrame = handleDrawFrame;
  }

可以看到底層暴露了兩個(gè)階段beginFrame和drawFrame,它們都是由底層觸發(fā)的,一般跟屏幕的刷新速率一致,如果是60幀就是每16.7毫秒回調(diào)一次,而onDrawFrame回調(diào)是緊接著onBeginFrame回調(diào)的,因?yàn)閯偛潘岬紽lutter有一個(gè)midFrameMicrotasks調(diào)度階段然后結(jié)合Dart的消息循環(huán)機(jī)制,可以推斷底層在Event隊(duì)列中連續(xù)創(chuàng)建了兩個(gè)Event,暫且稱作:beginFrame事件和drawFrame事件。
在handleBeginFrame處理中:

void handleBeginFrame(Duration rawTimeStamp) {
   ...
    try {
      // TRANSIENT FRAME CALLBACKS
      Timeline.startSync("Animate", arguments: timelineWhitelistArguments);
      _schedulerPhase = SchedulerPhase.transientCallbacks;
      final Map callbacks = _transientCallbacks;
      _transientCallbacks = {};
      callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
        if (!_removedIds.contains(id))
          _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp, callbackEntry.debugStack);
      });
      _removedIds.clear();
    } finally {
      _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
    }
  }

很簡單遍歷_transientCallbacks列表,然后回調(diào),最后就轉(zhuǎn)入midFrameMicrotasks階段;而把回調(diào)加入_transientCallbacks列表的方法,跟前端的requestAnimationFrame方法幾乎一樣,調(diào)用scheduleFrameCallback方法然后會(huì)返回一個(gè)id,你也可以使用cancelFrameCallbackWithId來取消這次回調(diào)。
接著進(jìn)入handleDrawFrame方法:

void handleDrawFrame() {
    Timeline.finishSync(); // end the "Animate" phase
    try {
      // PERSISTENT FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.persistentCallbacks;
      for (FrameCallback callback in _persistentCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp);

      // POST-FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.postFrameCallbacks;
      final List localPostFrameCallbacks =
          new List.from(_postFrameCallbacks);
      _postFrameCallbacks.clear();
      for (FrameCallback callback in localPostFrameCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp);
    } finally {
      _schedulerPhase = SchedulerPhase.idle;
      Timeline.finishSync(); // end the Frame
      _currentFrameTimeStamp = null;
    }
    // All frame-related callbacks have been executed. Run lower-priority tasks.
    _runTasks();
  }

直接進(jìn)入persistentCallbacks階段,drawFrame方法會(huì)在這里回調(diào)(build/layout/paint),然后在布局繪制完成后緊接著就進(jìn)入postFrameCallbacks階段,在這個(gè)階段我們基本可以拿到最新的布局信息了,就像Vue的$nextTick方法一樣,最后就是idle階段,這里的默認(rèn)處理就有點(diǎn)意思了。
直接來到_runTask方法:

void _runTasks() {
    if (_taskQueue.isEmpty || locked)
      return;
    final _TaskEntry entry = _taskQueue.first;
    if (schedulingStrategy(priority: entry.priority, scheduler: this)) {
      try {
        (_taskQueue.removeFirst().task)();
      } finally {
        if (_taskQueue.isNotEmpty)
          _ensureEventLoopCallback();
      }
    } else {
      scheduleFrame();
    }
  }

剛才也提到可以使用SchedulerBinding.scheduleTask加入一個(gè)task,但是task執(zhí)行前想要執(zhí)行首先要判斷優(yōu)先級(jí),默認(rèn)的判斷是這樣的:

bool defaultSchedulingStrategy({ int priority, SchedulerBinding scheduler }) {
  if (scheduler.transientCallbackCount > 0)
    return priority >= Priority.animation.value;
  return true;
}

也就是transientCallback存在,而且task的優(yōu)先級(jí)不大于animation的優(yōu)先級(jí),那么task就不會(huì)執(zhí)行了。其實(shí)目標(biāo)應(yīng)該是為了保證動(dòng)畫足夠流暢,因?yàn)閠ransientCallback一般都是處理動(dòng)畫的,如果存在transientCallback一般就是當(dāng)前有正在播放的動(dòng)畫,所以_runTasks方法會(huì)立馬進(jìn)行第二幀的調(diào)度,動(dòng)畫得以流暢進(jìn)行。
大部分時(shí)候,等動(dòng)畫播放完再處理一些耗時(shí)的操作其實(shí)也并不是問題,問題是如果存在循環(huán)播放的動(dòng)畫就有點(diǎn)尷尬了,這樣task就會(huì)永遠(yuǎn)都沒機(jī)會(huì)執(zhí)行,這是一個(gè)值得注意的地方,要么就是修改默認(rèn)的調(diào)度策略,要么把安排第二次播放動(dòng)畫的代碼放到addPostFrameCallback里面并使用scheduleMicrotask觸發(fā),這樣的話在處理完一個(gè)Task之后,又可以觸發(fā)第二次動(dòng)畫,把影響降到最低。

在schedulingStrategy方法之后,就是_ensureEventLoopCallback:

void _ensureEventLoopCallback() {
    assert(!locked);
    if (_hasRequestedAnEventLoopCallback)
      return;
    Timer.run(handleEventLoopCallback);
    _hasRequestedAnEventLoopCallback = true;
  }

主要驅(qū)動(dòng)事件循環(huán),其實(shí)在scheduleTask方法里面也會(huì)調(diào)用這個(gè)方法,保證task隊(duì)列里面的task都可以得到處理:

 void scheduleTask(VoidCallback task, Priority priority) {
    final bool isFirstTask = _taskQueue.isEmpty;
    _taskQueue.add(new _TaskEntry(task, priority.value));
    if (isFirstTask && !locked)
      _ensureEventLoopCallback();
  }

這里可以得知Flutter并不是都在以每16.7毫秒產(chǎn)生一幀來布局繪制界面,當(dāng)沒有動(dòng)畫,或者我們不調(diào)起setState方法,又或者說不調(diào)起ScheduleBinding.scheduleFrame有關(guān)聯(lián)的方法,F(xiàn)lutter并不會(huì)進(jìn)行布局繪制和刷新界面,這樣的情況下就不能靠onBeginFrame和onDrawFrame來驅(qū)動(dòng)處理task,只能靠dart自身的事件循環(huán),這也是_ensureEventLoopCallback方法存在的必要性。

總結(jié)

在大部分情況下,其實(shí)并不用擔(dān)心Flutter會(huì)像游戲一樣瘋狂消耗電量,消耗電量表現(xiàn)應(yīng)該跟原生沒有多大差別。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/89633.html

相關(guān)文章

  • Flutter樣式和布局控件簡析(一)

    摘要:但是好像反其道而行之,樣式糅合在結(jié)構(gòu)里面,這樣究竟有啥意思尼首先應(yīng)該是一個(gè)性能的考慮,瀏覽器解析其實(shí)也是一個(gè)性能消耗點(diǎn),沒有解析自然也可以加快頁面的顯示。 開始 搞前端的同學(xué)可能都習(xí)慣了CSS局部的思維,過去也出現(xiàn)過一些跟布局或者樣式相關(guān)的標(biāo)簽,例如:big, center, font, s, strike, tt, u;但是目前也被CSS所代替,已經(jīng)不推薦使用。但是在Flutter里...

    BoYang 評(píng)論0 收藏0
  • Flutter樣式和布局控件簡析(一)

    摘要:但是好像反其道而行之,樣式糅合在結(jié)構(gòu)里面,這樣究竟有啥意思尼首先應(yīng)該是一個(gè)性能的考慮,瀏覽器解析其實(shí)也是一個(gè)性能消耗點(diǎn),沒有解析自然也可以加快頁面的顯示。 開始 搞前端的同學(xué)可能都習(xí)慣了CSS局部的思維,過去也出現(xiàn)過一些跟布局或者樣式相關(guān)的標(biāo)簽,例如:big, center, font, s, strike, tt, u;但是目前也被CSS所代替,已經(jīng)不推薦使用。但是在Flutter里...

    wangxinarhat 評(píng)論0 收藏0
  • Flutter樣式和布局控件簡析(二)

    摘要:開始繼續(xù)接著分析相關(guān)的樣式和布局控件,但是這次內(nèi)容難度感覺比較高,怕有分析不到位的地方,所以這次僅僅當(dāng)做一個(gè)參考,大家最好可以自己閱讀一下代碼,應(yīng)該會(huì)有更深的體會(huì)。關(guān)于屬性,指前一個(gè)組件的布局區(qū)域和繪制區(qū)域重疊了。 開始 繼續(xù)接著分析Flutter相關(guān)的樣式和布局控件,但是這次內(nèi)容難度感覺比較高,怕有分析不到位的地方,所以這次僅僅當(dāng)做一個(gè)參考,大家最好可以自己閱讀一下代碼,應(yīng)該會(huì)有更深...

    yck 評(píng)論0 收藏0
  • Flutter樣式和布局控件簡析(二)

    摘要:開始繼續(xù)接著分析相關(guān)的樣式和布局控件,但是這次內(nèi)容難度感覺比較高,怕有分析不到位的地方,所以這次僅僅當(dāng)做一個(gè)參考,大家最好可以自己閱讀一下代碼,應(yīng)該會(huì)有更深的體會(huì)。關(guān)于屬性,指前一個(gè)組件的布局區(qū)域和繪制區(qū)域重疊了。 開始 繼續(xù)接著分析Flutter相關(guān)的樣式和布局控件,但是這次內(nèi)容難度感覺比較高,怕有分析不到位的地方,所以這次僅僅當(dāng)做一個(gè)參考,大家最好可以自己閱讀一下代碼,應(yīng)該會(huì)有更深...

    leanxi 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<