国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Flutter之SchedulerBinding簡析

BlackMass / 2647人閱讀

摘要:但是接下來并不是討論單線程如何方便開發,而是要深入的調度器,看一下是如何安排任務,調度工作。總結在大部分情況下,其實并不用擔心會像游戲一樣瘋狂消耗電量,消耗電量表現應該跟原生沒有多大差別。

開始

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

調度階段

在Flutter中有幾個調度階段:

transientCallbacks
主要處理動畫計算,動畫狀態的更新

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

persistentCallbacks
主要處理build/layout/paint

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

idle
不產生Frame的空閑期,可以處理Tasks(由SchedulerBinding.scheduleTask觸發),microtasks(由scheduleMicrotask觸發),定時器的回調,響應事件處理(例如:用戶的輸入)

分析

這個幾個階段是如何定義出來的尼?
在SchedulerBinding實例化的時候:

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

可以看到底層暴露了兩個階段beginFrame和drawFrame,它們都是由底層觸發的,一般跟屏幕的刷新速率一致,如果是60幀就是每16.7毫秒回調一次,而onDrawFrame回調是緊接著onBeginFrame回調的,因為剛才所提到Flutter有一個midFrameMicrotasks調度階段然后結合Dart的消息循環機制,可以推斷底層在Event隊列中連續創建了兩個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列表,然后回調,最后就轉入midFrameMicrotasks階段;而把回調加入_transientCallbacks列表的方法,跟前端的requestAnimationFrame方法幾乎一樣,調用scheduleFrameCallback方法然后會返回一個id,你也可以使用cancelFrameCallbackWithId來取消這次回調。
接著進入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();
  }

直接進入persistentCallbacks階段,drawFrame方法會在這里回調(build/layout/paint),然后在布局繪制完成后緊接著就進入postFrameCallbacks階段,在這個階段我們基本可以拿到最新的布局信息了,就像Vue的$nextTick方法一樣,最后就是idle階段,這里的默認處理就有點意思了。
直接來到_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加入一個task,但是task執行前想要執行首先要判斷優先級,默認的判斷是這樣的:

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

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

在schedulingStrategy方法之后,就是_ensureEventLoopCallback:

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

主要驅動事件循環,其實在scheduleTask方法里面也會調用這個方法,保證task隊列里面的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毫秒產生一幀來布局繪制界面,當沒有動畫,或者我們不調起setState方法,又或者說不調起ScheduleBinding.scheduleFrame有關聯的方法,Flutter并不會進行布局繪制和刷新界面,這樣的情況下就不能靠onBeginFrame和onDrawFrame來驅動處理task,只能靠dart自身的事件循環,這也是_ensureEventLoopCallback方法存在的必要性。

總結

在大部分情況下,其實并不用擔心Flutter會像游戲一樣瘋狂消耗電量,消耗電量表現應該跟原生沒有多大差別。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89633.html

相關文章

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

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

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

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

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

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

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

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

    leanxi 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<