日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Flutter?隊列任務的實現_Android

作者:BlackDream ? 更新時間: 2022-08-06 編程語言

前言

在電商的應用中,最常見的就是在首頁或完成某事件之后,彈出一堆的活動/廣告。假如重疊彈出,很丑,給用戶的體驗也不好,所以一般都會依次依條件的彈出。

下面講講我是怎么實現一個方便的隊列任務管理。

隊列

任務隊列,那當然要有個隊列。這個隊列的任務內容應該是返回FutureFunction,因為我需要得到他處理完成的結果,比如等待彈窗關閉時return,才表示這個任務被完成。

typedef TaskEventFunction = Future Function();

class TaskQueue {
    List<TaskEventFunction> _actionQueue = [];
}

添加任務進隊列

然后是加入隊列的方法add

void add(TaskEventFunction task) {
  _actionQueue.add(task);
}

然后想到,隊列是不是最好去重?或者可選去重。

那一個Function如何去重呢,我們可以使用它的hashCode,同一個FunctionhashCode相同。

taskQueue.add(testFunction);

Future testFunction() async {
  await Future.delayed(const Duration(milliseconds: 1000));
  return Future.value(true);
}

即我們可以按上面這樣定義,同一個類同一個實例下同一個Function,就是相同的hashCode。若是以下這種寫法則hashCode不一樣,每次add都相當于一個新的Function

taskQueue.add(() async {
  await Future.delayed(const Duration(milliseconds: 1000));
  return Future.value(true);
});

假如不需要去重,可以用第二種。也可以主動指定taskId來選擇哪些需要去重(即使內容不一樣),哪些不需要。

修改一下add,增加taskId。同時hashCode應該作為主要的key,修改一下隊列類型。 最終如下:

/// 任務編號隊列
List<String> _actionQueue = [];

/// 任務隊列
Map<String, TaskEventFunction> _actionMap = {};

/// 指定taskId 為 -1 則不用去重
String add(TaskEventFunction task, {
  String? taskId,
}) {
  String? id = taskId;
  id ??= task.hashCode.toString();
  bool isContains = false;
  if (taskId != '-1') {
    isContains = _actionQueue.contains(id);
  } else {
    id = task.hashCode.toString();
  }
  if (!isContains) {
    _actionQueue.add(id);
    _actionMap[id] = task;
  }
  return id;
}

-1時則不去重,也把最終的taskId返回。

移除隊列指定任務

有添加任務的方法,那也需有移除任務的方法

/// 這里需注意,add的時taskId為-1,那就直接把task傳入,add時有返回,可以對應
bool remove(TaskEventFunction task, {String? taskId}) {
  String? id = taskId;
  id ??= task.hashCode.toString();
  if (_actionQueue.contains(id)) {
    _actionMap.remove(id);
    return _actionQueue.remove(id);
  }
  return false;
}

taskId/hashCode為準。

判斷是否包含對應任務

使用者可以自己判斷是否已包含對應的任務

/// 是否隊列中包含該任務
/// [task] 與 [taskId] 應該只傳一個
bool containers({TaskEventFunction? task, String? taskId}) {
  assert(task != null || taskId != null);
  String? id = taskId;
  id ??= task.hashCode.toString();
  return _actionQueue.contains(taskId);
}

執行隊列任務

任務隊列的進出基本成型,開始處理任務。很簡單,取出任務,然后執行任務,最后移除任務

void startLoop() async {
  if (dealing || _actionQueue.isEmpty) {
    return;
  }
  dealing = true;
  String taskId = _actionQueue.first;
  TaskEventFunction? callback = _actionMap[taskId];
  if (callback == null) {
    _actionQueue.remove(taskId);
    return;
  }

  try {
    await callback();
  } catch (e) {
    log('_actionQueue 出錯 $e');
  } finally {
    _actionQueue.remove(taskId);
    _actionMap.remove(taskId);
    dealing = false;
    if (_actionQueue.isNotEmpty) {
      startLoop();
    }
  }
}

這里加了個dealing,表示任務正在處理中的狀態,正在處理的話,不允許執行下一個任務。

在執行完成(或失敗)后,自動觸發下一個任務。add中也可以加入startLoop,使添加任務后自動啟動任務循環。

任務條件

基本的任務隊列已經完成。不過每個任務其實一般都會有個條件,確認符合當前場景才執行。比如說的確是新人且在首頁,才顯示新人優惠彈窗。

條件可以是整個任務隊列統一的條件,也可以是某個任務特定的條件。

typedef TaskConditionFunction = FutureOr<bool> Function();

這里類型用FutureOr<bool>,有可能需要等待一下才能確認條件。任務對應的條件,也根據taskId來存儲。

/// 任務條件
Map<String, TaskConditionFunction> _actionCondition = {};

/// 總條件
TaskConditionFunction? _condition;

添加任務時加入條件

TaskEventFunction add(TaskEventFunction task, {
  String? taskId,
  TaskConditionFunction? condition,
}) {
    ...
    
    if (condition != null && !_actionCondition.containsKey(id)) {
      _actionCondition[id] = condition;
    }

}

設置總條件,或補充條件

/// 設置允許執行條件
void setAcceptConditions(TaskConditionFunction condition,
    {String? taskId}) {
  if (taskId == null) {
    _condition = condition;
  } else {
    _actionCondition[taskId] = condition;
  }
}

執行任務前判斷條件是否滿足

    bool canNext = await _nextConditions(taskId);
    if (canNext) {
      try {
        await callback();
      } catch (e) {
        log('_actionQueue 出錯 $e');
      } finally {
        _actionQueue.remove(taskId);
        _actionMap.remove(taskId);
        dealing = false;
        if (_actionQueue.isNotEmpty) {
          startLoop();
        }
      }
    } else {
      // 不滿足條件一般后續的也不會執行
      dealing = false;
    }
Future<bool> _nextConditions([String? id]) async {
  String taskId = id ?? _actionQueue.first;
  bool canNext = true;
  var taskCondition = _actionCondition[taskId];
  if (_condition != null) {
    canNext = await _condition!.call();
  }
  if (canNext && taskCondition != null) {
    canNext = await taskCondition();
  }

  return Future.value(canNext);
}

原則上應該既滿足總條件也滿足任務條件。

使用和總結

實例化TaskQueue

TaskQueue taskQueue = TaskQueue();

添加任務并開始任務

taskQueue.add(testFunction, condition: () async {
  await Future.delayed(const Duration(milliseconds: 200));
  return Future.value(true);
});
taskQueue.startLoop();

Future testFunction() async {
  return showDialog(...);
}

注意設置條件要返回bool。

還可以加入類似延時等操作,跟條件一樣配置即可。太久的操作不要像上面一樣寫在condition中,可能執行完之后又不滿足了,根據具體情況考慮。

一般來說類似彈窗的returnawait showDialog就可以等待彈窗頁面結束,再進行下一個。

跨頁面還是當前頁面的控制,只需要考慮是全局實例TaskQueue還是頁面內實例TaskQueue

原文鏈接:https://juejin.cn/post/7108642127373926436

欄目分類
最近更新