XTask 一個拓展性極強的Android任務(wù)執(zhí)行框架

背景

很早之前接觸了RxJava的任務(wù)流操作,覺得這種將復(fù)雜業(yè)務(wù)流通過一個個操作符拆解開來,形成一條條條理清晰的function, 讓人寫起來直呼過癮.其實這就是責(zé)任鏈模式的一種應(yīng)用.

但是RxJava的功能實在是太強大了, 如果僅僅是使用它來處理這些業(yè)務(wù)流我覺得還是有些大材小用了.

之前也做過一段時間的應(yīng)用性能優(yōu)化, 其中當(dāng)然就包括應(yīng)用冷啟動優(yōu)化, 中間有涉及過啟動器的概念, 當(dāng)時也查閱了一些現(xiàn)有的開源框架, 也使用過其中一些, 但是總覺得并不是很好用, 用起來不是很順手.

作為一名資深A(yù)ndroid開源框架卷王, 當(dāng)時我腦海里就萌發(fā)一種想法, 為啥我不自己寫一個任務(wù)流執(zhí)行的框架呢?想到這, 我就抽出了我的一部分業(yè)余時間(女朋友都不陪了), 擼出了這個XTask框架, 自我感覺非常nice, 在這分享給大家.

簡介

XTask是一個拓展性極強的Android任務(wù)執(zhí)行框架糖赔。

可自由定義和組合任務(wù)來實現(xiàn)你想要的功能,尤其適用于處理復(fù)雜的業(yè)務(wù)流程,可靈活添加前置任務(wù)或者調(diào)整執(zhí)行順序分井。例如:應(yīng)用的啟動初始化流程订歪。

項目地址


特征

  • 支持6種線程類型方式執(zhí)行任務(wù)刊殉。
  • 支持任務(wù)鏈中各任務(wù)的執(zhí)行線程調(diào)度和控制涮坐。
  • 支持快捷任務(wù)創(chuàng)建吓笙,同時支持自定義任務(wù)。
  • 支持串行和并行等組任務(wù)崎岂。
  • 支持任務(wù)間數(shù)據(jù)共享捆毫。
  • 支持自由組合任務(wù)執(zhí)行。
  • 支持任務(wù)鏈執(zhí)行取消冲甘。
  • 支持取消所有任務(wù)鏈和指定名稱的任務(wù)鏈绩卤。
  • 支持任務(wù)鏈調(diào)用順序記錄和查詢。
  • 支持自定義任務(wù)執(zhí)行的線程池江醇。

設(shè)計思想

框架主體使用責(zé)任鏈的設(shè)計模式濒憋,輔以建造者模式、工廠模式嫁审、適配器模式跋炕、組合模式、外觀模式以及代理模式來實現(xiàn)律适。

組成結(jié)構(gòu)

  • 任務(wù)鏈ITaskChainEngine:任務(wù)鏈執(zhí)行引擎辐烂,負責(zé)統(tǒng)籌調(diào)度各任務(wù)步驟。

  • 任務(wù)步驟ITaskStep:負責(zé)具體任務(wù)邏輯處理捂贿。

  • 數(shù)據(jù)存儲倉庫IDataStore:存放數(shù)據(jù)的倉庫纠修,主要用于保存任務(wù)參數(shù)中的數(shù)據(jù)。

  • 任務(wù)參數(shù)ITaskParam:負責(zé)任務(wù)路徑記錄以及任務(wù)產(chǎn)生的參數(shù)管理厂僧。

  • 任務(wù)執(zhí)行結(jié)果ITaskResult:存放任務(wù)最終執(zhí)行的結(jié)果以及產(chǎn)生的數(shù)據(jù)扣草。

  • 任務(wù)組IGroupTaskStep:負責(zé)統(tǒng)籌調(diào)度各子任務(wù)步驟。

點擊查看框架UML設(shè)計圖

日志一覽

task_log.png
task_log2.png

集成指南

添加Gradle依賴

1.先在項目根目錄的 build.gradlerepositories 添加:

allprojects {
     repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

2.然后在dependencies添加:

dependencies {
  ...
  // XTask
  implementation 'com.github.xuexiangjys.XTask:xtask-core:1.0.2'
}

使用方法

XTask作為對外統(tǒng)一的API入口颜屠,所有常用的方法都能從中找到辰妙。

打開調(diào)試模式

當(dāng)需要定位問題,需要進行調(diào)試時甫窟,可打開調(diào)試模式密浑,這樣便可開啟框架的日志。

XTask.debug(true);

XTask的API介紹

方法名 描述
debug 設(shè)置是否打開調(diào)試
setLogger 自定義日志打印
setIsLogTaskRunThreadName 設(shè)置是否打印任務(wù)執(zhí)行所在的線程名粗井,默認false
getTaskChain 獲取任務(wù)鏈執(zhí)行引擎
getTask 獲取簡化的任務(wù)
getTaskBuilder 獲取簡化任務(wù)的構(gòu)建者
getConcurrentGroupTask 獲取并行任務(wù)組
getSerialGroupTask 獲取串行任務(wù)組
cancelTaskChain 取消指定任務(wù)鏈執(zhí)行
postToMain 執(zhí)行任務(wù)到主線程
submit 執(zhí)行普通異步任務(wù)
emergentSubmit 執(zhí)行緊急異步任務(wù)
backgroundSubmit 執(zhí)行后臺異步任務(wù)
ioSubmit 執(zhí)行io耗時的異步任務(wù)
groupSubmit 執(zhí)行分組異步任務(wù)

如何執(zhí)行一條任務(wù)鏈

下面是一整個完整的例子:

// 1.創(chuàng)建一條任務(wù)鏈(必須)
final TaskChainEngine engine = XTask.getTaskChain();
// 2.設(shè)置任務(wù)鏈的初始化參數(shù)(可選)
engine.setTaskParam(TaskParam.get("chainName", engine.getName()));
TaskParam taskParam = TaskParam.get("param1", 100)
        .put("param2", true);
// 3.創(chuàng)建多個任務(wù)尔破,并向任務(wù)鏈中添加(必須)
XTaskStep taskStep = XTask.getTask(new TaskCommand() {
    @Override
    public void run() {
        ITaskParam param = getTaskParam();
        Log.e(TAG, getName() + "  start, param1:" + param.get("param1") + ", chainName:" + param.get("chainName"));
        param.put("param1", 200);
        param.put("param3", "this is param3!");
    }
}, taskParam);
engine.addTask(taskStep)
        .addTask(XTask.getTask(new TaskCommand() {
            @Override
            public void run() {
                ITaskParam param = getTaskParam();
                Log.e(TAG, getName() + "  start, param1:" + param.get("param1") + ", param3:" + param.get("param3"));
                param.put("param2", false);
            }
        }));
// 4.設(shè)置任務(wù)鏈執(zhí)行回調(diào)(可選)
ICanceller canceller = engine.setTaskChainCallback(new TaskChainCallbackAdapter() {
    @Override
    public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
        Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName());
        Map<String, Object> data = result.getDataStore().getData();
        for (Map.Entry<String, Object> entry : data.entrySet()) {
            Log.e(TAG, "key:" + entry.getKey() + ", value:" + entry.getValue());
        }
    }
// 5.任務(wù)鏈執(zhí)行(必須)
}).start();

1.創(chuàng)建一條任務(wù)鏈.(必須)

TaskChainEngine engine = XTask.getTaskChain();

2.設(shè)置任務(wù)鏈的初始化參數(shù).(可選)

engine.setTaskParam(TaskParam.get("chainName", engine.getName()));

3.創(chuàng)建多個任務(wù),并向任務(wù)鏈中添加.(必須)

// 設(shè)置任務(wù)初始化參數(shù)
TaskParam taskParam = TaskParam.get("param1", 100)
        .put("param2", true);
XTaskStep taskStep = XTask.getTask(new TaskCommand() {
    @Override
    public void run() {
        // ...執(zhí)行任務(wù)
    }
}, taskParam);
engine.addTask(taskStep)
        .addTask(XTask.getTask(new TaskCommand() {
            @Override
            public void run() {
                // ...執(zhí)行任務(wù)
            }
        }));

【注意】對于任務(wù)執(zhí)行完成浇衬,需要注意以下兩點:

  • 如果任務(wù)執(zhí)行成功懒构,就調(diào)用notifyTaskSucceed,任務(wù)執(zhí)行失敗耘擂,就調(diào)用notifyTaskFailed胆剧。這里任務(wù)無論成功還是失敗,只要執(zhí)行完成都需要調(diào)用notifyTaskXXX通知任務(wù)鏈該任務(wù)完成醉冤,否則任務(wù)將無法正常執(zhí)行赞赖。
  • TaskCommandSimpleTaskStep默認提供了自動通知執(zhí)行結(jié)果的功能滚朵,但是AbstractTaskStep沒有提供冤灾,需要手動通知前域。

4.設(shè)置任務(wù)鏈執(zhí)行回調(diào).(可選)

調(diào)用setTaskChainCallback設(shè)置任務(wù)鏈執(zhí)行回調(diào)。

engine.setTaskChainCallback(new TaskChainCallbackAdapter() {

    @Override
    public boolean isCallBackOnMainThread() {
        // 回調(diào)是否返回主線程, 默認是true
        return false;
    }
    @Override
    public void onTaskChainStart(@NonNull ITaskChainEngine engine) {
        Log.e(TAG, "task chain start");
    }
    @Override
    public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
        Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName());
    }
    @Override
    public void onTaskChainError(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
        Log.e(TAG, "task chain error");
    }
})

5.任務(wù)鏈執(zhí)行.(必須)

調(diào)用start執(zhí)行任務(wù)鏈韵吨。

ICanceller canceller = engine.start();

任務(wù)創(chuàng)建

創(chuàng)建任務(wù)有兩種方式:

  • 通過XTask.getTask構(gòu)建
  • 繼承SimpleTaskStep/AbstractTaskStep實現(xiàn)任務(wù)的自定義

通過XTask創(chuàng)建

通過XTask.getTask, 傳入對應(yīng)的屬性進行構(gòu)建

屬性名 描述
name 任務(wù)步驟名稱
command 任務(wù)執(zhí)行內(nèi)容
threadType 線程執(zhí)行類型
taskParam 任務(wù)參數(shù)
taskHandler 任務(wù)處理者
isAutoNotify 是否自動通知任務(wù)執(zhí)行結(jié)果
XTaskStep taskStep = XTask.getTask(new TaskCommand() {
    @Override
    public void run() {
        // todo
    }
}, ThreadType.ASYNC, taskParam);

通過繼承創(chuàng)建

通過繼承SimpleTaskStep或者AbstractTaskStep實現(xiàn)具體功能匿垄。

public class StepATask extends SimpleTaskStep {

    @Override
    public void doTask() throws Exception {
        // todo
        // 不需要手動通知任務(wù)鏈任務(wù)完成
    }
}

public class StepBTask extends AbstractTaskStep {

    @Override
    public void doTask() throws Exception {
        // todo
        // 需手動通知任務(wù)鏈任務(wù)完成
        notifyTaskSucceed(TaskResult.succeed());
    }

    @Override
    public String getName() {
        return "StepATask";
    }
}

任務(wù)執(zhí)行原則

每一個任務(wù)都是依托于任務(wù)鏈進行流程控制。任何任務(wù)都需要遵循以下原則:

  • 任何任務(wù)無論失敗還是成功归粉,都需要調(diào)用notifyTaskSucceed或者notifyTaskFailed去通知任務(wù)鏈任務(wù)的完成情況椿疗。TaskCommandSimpleTaskStep默認提供了自動通知執(zhí)行結(jié)果的功能。
  • 一旦任務(wù)鏈中某個任務(wù)執(zhí)行失敗糠悼,整個鏈路都停止工作届榄。
任務(wù)類型 任務(wù)執(zhí)行說明
TaskCommand 自動通知執(zhí)行結(jié)果。如需手動通知倔喂,只需設(shè)置isAutoNotify為false即可
SimpleTaskStep 自動通知執(zhí)行結(jié)果铝条。如需手動通知,只需重寫isAutoNotify方法為false即可
AbstractTaskStep 需手動通知執(zhí)行結(jié)果

TaskCommand手動通知執(zhí)行結(jié)果

在通過XTask.getTask傳入TaskCommand構(gòu)建Task的時候席噩,設(shè)置isAutoNotify為false即可手動通知執(zhí)行結(jié)果班缰。

final TaskChainEngine engine = XTask.getTaskChain();
for (int i = 0; i < 5; i++) {
    int finalI = i;
    engine.addTask(XTask.getTask(new TaskCommand() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (finalI == 2) {
                notifyTaskFailed(404, "任務(wù)執(zhí)行失敗!");
            } else {
                notifyTaskSucceed(TaskResult.succeed());
            }
        }
    }, false)); // 設(shè)置手動通知執(zhí)行結(jié)果
}
engine.start();

SimpleTaskStep手動通知執(zhí)行結(jié)果

重寫SimpleTaskStepisAutoNotify方法為false即可手動通知執(zhí)行結(jié)果。

public class StepATask extends SimpleTaskStep {

    @Override
    public void doTask() throws Exception {
        // todo
        // 手動通知任務(wù)鏈任務(wù)完成
        notifyTaskSucceed();
    }

    @Override
    protected boolean isAutoNotify() {
        return false;
    }
}

參數(shù)傳遞

  • 任何TaskStep我們都可以通過getTaskParam獲取任務(wù)參數(shù)和任務(wù)執(zhí)行結(jié)果ITaskParam悼枢。
  • 上一個TaskStep保存處理過的任務(wù)參數(shù)會自動帶入到下一個TaskStep中去埠忘,因此最后一個TaskStep擁有之前所有任務(wù)的參數(shù)數(shù)據(jù)。
XTask.getTask(new TaskCommand() {
    @Override
    public void run() {
        ITaskParam param = getTaskParam();
        Log.e(TAG, getName() + "  start, param1:" + param.get("param1") + ", param3:" + param.get("param3"));
        param.put("param2", false);
    }
})

線程控制

設(shè)置任務(wù)的threadType類型馒索,即可完成對任務(wù)運行線程的控制莹妒。目前支持6種線程處理方式。

類型 描述 線程池構(gòu)成
MAIN 主線程(UI線程) /
ASYNC 異步線程(開子線程绰上,普通線程池) 核心線程數(shù)和最大線程為CPU數(shù)旨怠,0s keepTime,LinkedBlockingQueue(128)渔期,線程優(yōu)先級5
ASYNC_IO 異步線程(開子線程运吓,io線程池) 核心線程數(shù)和最大線程為(2*CPU數(shù)+1),30s keepTime疯趟,LinkedBlockingQueue(128)拘哨,線程優(yōu)先級5
ASYNC_EMERGENT 異步線程(開子線程,緊急線程池) 核心線程數(shù)為2信峻,最大線程為∞倦青,60s keepTime,SynchronousQueue(不阻塞)盹舞,線程優(yōu)先級10
ASYNC_BACKGROUND 異步線程(開子線程产镐,優(yōu)先級較低線程池) 核心線程數(shù)和最大線程為2隘庄,0s keepTime,LinkedBlockingQueue(128)癣亚,線程優(yōu)先級1
SYNC 同步線程(直接執(zhí)行) /
// 1.構(gòu)造時傳入線程
XTaskStep taskStep = XTask.getTask(new SimpleTaskCommand(1000), ThreadType.ASYNC_EMERGENT);
// 2.設(shè)置線程的方法
taskStep.setThreadType(ThreadType.ASYNC_IO);

任務(wù)組

目前共有串行任務(wù)組(SerialGroupTaskStep)和并行任務(wù)組(ConcurrentGroupTaskStep)

串行任務(wù)組

串行任務(wù)組是按順序依次執(zhí)行丑掺,和任務(wù)鏈的處理方式類似。使用XTask.getSerialGroupTask獲取述雾。

final TaskChainEngine engine = XTask.getTaskChain();
SerialGroupTaskStep group1 = XTask.getSerialGroupTask("group1");
for (int i = 0; i < 5; i++) {
    group1.addTask(XTask.getTask(new SimpleTaskCommand(500)));
}
SerialGroupTaskStep group2 = XTask.getSerialGroupTask("group2");
for (int i = 0; i < 5; i++) {
    group2.addTask(XTask.getTask(new SimpleTaskCommand(1000)));
}
ICanceller canceller = engine.addTask(group1)
        .addTask(group2)
        .setTaskChainCallback(new TaskChainCallbackAdapter() {
            @Override
            public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
                Log.e(TAG, "task chain completed, path:" + result.getPath());
            }
        })
        .start();
addCanceller(canceller);

并行任務(wù)組

并行任務(wù)組是組內(nèi)所有任務(wù)同時執(zhí)行街州,待所有任務(wù)都完成后才視為任務(wù)組完成。使用XTask.getConcurrentGroupTask獲取玻孟。

final TaskChainEngine engine = XTask.getTaskChain();
ConcurrentGroupTaskStep group1 = XTask.getConcurrentGroupTask("group1");
for (int i = 0; i < 5; i++) {
    group1.addTask(XTask.getTask(new SimpleTaskCommand(100 * (i + 1))));
}
ConcurrentGroupTaskStep group2 = XTask.getConcurrentGroupTask("group2");
for (int i = 0; i < 5; i++) {
    group2.addTask(XTask.getTask(new SimpleTaskCommand(200 * (i + 1))));
}
ICanceller canceller = engine.addTask(group1)
        .addTask(group2)
        .setTaskChainCallback(new TaskChainCallbackAdapter() {
            @Override
            public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
                Log.e(TAG, "task chain completed, path:" + result.getPath());
            }
        })
        .start();
addCanceller(canceller);

最后

如果你覺得這個項目對你有所幫助, 你可以點擊star進行收藏或者將其分享出去, 讓更多的人知道這個項目!

我是xuexiangjys唆缴,一枚熱愛學(xué)習(xí),愛好編程黍翎,致力于Android架構(gòu)研究以及開源項目經(jīng)驗分享的技術(shù)up主面徽。獲取更多資訊,歡迎微信搜索公眾號:【我的Android開源之旅】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匣掸,一起剝皮案震驚了整個濱河市趟紊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旺聚,老刑警劉巖织阳,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異砰粹,居然都是意外死亡唧躲,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門碱璃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弄痹,“玉大人,你說我怎么就攤上這事嵌器「卣妫” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵爽航,是天一觀的道長蚓让。 經(jīng)常有香客問我,道長讥珍,這世上最難降的妖魔是什么历极? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮衷佃,結(jié)果婚禮上趟卸,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好锄列,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布图云。 她就那樣靜靜地躺著,像睡著了一般邻邮。 火紅的嫁衣襯著肌膚如雪竣况。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天饶囚,我揣著相機與錄音帕翻,去河邊找鬼。 笑死萝风,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的紫岩。 我是一名探鬼主播规惰,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼泉蝌!你這毒婦竟也來了歇万?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤勋陪,失蹤者是張志新(化名)和其女友劉穎贪磺,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诅愚,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡寒锚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了违孝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刹前。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖雌桑,靈堂內(nèi)的尸體忽然破棺而出喇喉,到底是詐尸還是另有隱情,我是刑警寧澤校坑,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布拣技,位于F島的核電站,受9級特大地震影響耍目,放射性物質(zhì)發(fā)生泄漏膏斤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一制妄、第九天 我趴在偏房一處隱蔽的房頂上張望掸绞。 院中可真熱鬧,春花似錦、人聲如沸衔掸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敞映。三九已至较曼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間振愿,已是汗流浹背捷犹。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冕末,地道東北人萍歉。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像档桃,于是被迫代替她去往敵國和親枪孩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

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