XTask 一个拓展性极强的Android任务执行框架

2022-04-18 13:37:34 浏览数 (1)

背景

很早之前接触了RxJava的任务流操作,觉得这种将复杂业务流通过一个个操作符拆解开来,形成一条条条理清晰的function, 让人写起来直呼过瘾.其实这就是责任链模式的一种应用.

但是RxJava的功能实在是太强大了, 如果仅仅是使用它来处理这些业务流我觉得还是有些大材小用了.

之前也做过一段时间的应用性能优化, 其中当然就包括应用冷启动优化, 中间有涉及过启动器的概念, 当时也查阅了一些现有的开源框架, 也使用过其中一些, 但是总觉得并不是很好用, 用起来不是很顺手.

作为一名资深Android开源框架卷王, 当时我脑海里就萌发一种想法, 为啥我不自己写一个任务流执行的框架呢?想到这, 我就抽出了我的一部分业余时间(女朋友都不陪了), 撸出了这个XTask框架, 自我感觉非常nice, 在这分享给大家.

简介

XTask是一个拓展性极强的Android任务执行框架。

可自由定义和组合任务来实现你想要的功能,尤其适用于处理复杂的业务流程,可灵活添加前置任务或者调整执行顺序。例如:应用的启动初始化流程。

项目地址

  • github: https://github.com/xuexiangjys/XTask
  • gitee: https://gitee.com/xuexiangjys/XTask

特征

  • 支持6种线程类型方式执行任务。
  • 支持任务链中各任务的执行线程调度和控制。
  • 支持快捷任务创建,同时支持自定义任务。
  • 支持串行和并行等组任务。
  • 支持任务间数据共享。
  • 支持自由组合任务执行。
  • 支持任务链执行取消。
  • 支持取消所有任务链和指定名称的任务链。
  • 支持任务链调用顺序记录和查询。
  • 支持自定义任务执行的线程池。

设计思想

框架主体使用责任链的设计模式,辅以建造者模式、工厂模式、适配器模式、组合模式、外观模式以及代理模式来实现。

组成结构

  • 任务链ITaskChainEngine:任务链执行引擎,负责统筹调度各任务步骤。
  • 任务步骤ITaskStep:负责具体任务逻辑处理。
  • 数据存储仓库IDataStore:存放数据的仓库,主要用于保存任务参数中的数据。
  • 任务参数ITaskParam:负责任务路径记录以及任务产生的参数管理。
  • 任务执行结果ITaskResult:存放任务最终执行的结果以及产生的数据。
  • 任务组IGroupTaskStep:负责统筹调度各子任务步骤。

点击查看框架UML设计图

日志一览

task_log.png

task_log2.png


集成指南

添加Gradle依赖

1.先在项目根目录的 build.gradlerepositories 添加:

代码语言:javascript复制
allprojects {
     repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

2.然后在dependencies添加:

代码语言:javascript复制
dependencies {
  ...
  // XTask
  implementation 'com.github.xuexiangjys.XTask:xtask-core:1.0.2'
}

使用方法

XTask作为对外统一的API入口,所有常用的方法都能从中找到。

打开调试模式

当需要定位问题,需要进行调试时,可打开调试模式,这样便可开启框架的日志。

代码语言:javascript复制
XTask.debug(true);

XTask的API介绍

方法名

描述

debug

设置是否打开调试

setLogger

自定义日志打印

setIsLogTaskRunThreadName

设置是否打印任务执行所在的线程名,默认false

getTaskChain

获取任务链执行引擎

getTask

获取简化的任务

getTaskBuilder

获取简化任务的构建者

getConcurrentGroupTask

获取并行任务组

getSerialGroupTask

获取串行任务组

cancelTaskChain

取消指定任务链执行

postToMain

执行任务到主线程

submit

执行普通异步任务

emergentSubmit

执行紧急异步任务

backgroundSubmit

执行后台异步任务

ioSubmit

执行io耗时的异步任务

groupSubmit

执行分组异步任务

如何执行一条任务链

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

代码语言:javascript复制
// 1.创建一条任务链(必须)
final TaskChainEngine engine = XTask.getTaskChain();
// 2.设置任务链的初始化参数(可选)
engine.setTaskParam(TaskParam.get("chainName", engine.getName()));
TaskParam taskParam = TaskParam.get("param1", 100)
        .put("param2", true);
// 3.创建多个任务,并向任务链中添加(必须)
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.设置任务链执行回调(可选)
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.任务链执行(必须)
}).start();

1.创建一条任务链.(必须)

代码语言:javascript复制
TaskChainEngine engine = XTask.getTaskChain();

2.设置任务链的初始化参数.(可选)

代码语言:javascript复制
engine.setTaskParam(TaskParam.get("chainName", engine.getName()));

3.创建多个任务,并向任务链中添加.(必须)

代码语言:javascript复制
// 设置任务初始化参数
TaskParam taskParam = TaskParam.get("param1", 100)
        .put("param2", true);
XTaskStep taskStep = XTask.getTask(new TaskCommand() {
    @Override
    public void run() {
        // ...执行任务
    }
}, taskParam);
engine.addTask(taskStep)
        .addTask(XTask.getTask(new TaskCommand() {
            @Override
            public void run() {
                // ...执行任务
            }
        }));

【注意】对于任务执行完成,需要注意以下两点:

  • 如果任务执行成功,就调用notifyTaskSucceed,任务执行失败,就调用notifyTaskFailed。这里任务无论成功还是失败,只要执行完成都需要调用notifyTaskXXX通知任务链该任务完成,否则任务将无法正常执行。
  • TaskCommandSimpleTaskStep默认提供了自动通知执行结果的功能,但是AbstractTaskStep没有提供,需要手动通知。

4.设置任务链执行回调.(可选)

调用setTaskChainCallback设置任务链执行回调。

代码语言:javascript复制
engine.setTaskChainCallback(new TaskChainCallbackAdapter() {

    @Override
    public boolean isCallBackOnMainThread() {
        // 回调是否返回主线程, 默认是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.任务链执行.(必须)

调用start执行任务链。

代码语言:javascript复制
ICanceller canceller = engine.start();

任务创建

创建任务有两种方式:

  • 通过XTask.getTask构建
  • 继承SimpleTaskStep/AbstractTaskStep实现任务的自定义

通过XTask创建

通过XTask.getTask, 传入对应的属性进行构建

属性名

描述

name

任务步骤名称

command

任务执行内容

threadType

线程执行类型

taskParam

任务参数

taskHandler

任务处理者

isAutoNotify

是否自动通知任务执行结果

代码语言:javascript复制
XTaskStep taskStep = XTask.getTask(new TaskCommand() {
    @Override
    public void run() {
        // todo
    }
}, ThreadType.ASYNC, taskParam);

通过继承创建

通过继承SimpleTaskStep或者AbstractTaskStep实现具体功能。

代码语言:javascript复制
public class StepATask extends SimpleTaskStep {

    @Override
    public void doTask() throws Exception {
        // todo
        // 不需要手动通知任务链任务完成
    }
}

public class StepBTask extends AbstractTaskStep {

    @Override
    public void doTask() throws Exception {
        // todo
        // 需手动通知任务链任务完成
        notifyTaskSucceed(TaskResult.succeed());
    }

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

任务执行原则

每一个任务都是依托于任务链进行流程控制。任何任务都需要遵循以下原则:

  • 任何任务无论失败还是成功,都需要调用notifyTaskSucceed或者notifyTaskFailed去通知任务链任务的完成情况。TaskCommandSimpleTaskStep默认提供了自动通知执行结果的功能。
  • 一旦任务链中某个任务执行失败,整个链路都停止工作。

任务类型

任务执行说明

TaskCommand

自动通知执行结果。如需手动通知,只需设置isAutoNotify为false即可

SimpleTaskStep

自动通知执行结果。如需手动通知,只需重写isAutoNotify方法为false即可

AbstractTaskStep

需手动通知执行结果

TaskCommand手动通知执行结果

在通过XTask.getTask传入TaskCommand构建Task的时候,设置isAutoNotify为false即可手动通知执行结果。

代码语言:javascript复制
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, "任务执行失败!");
            } else {
                notifyTaskSucceed(TaskResult.succeed());
            }
        }
    }, false)); // 设置手动通知执行结果
}
engine.start();

SimpleTaskStep手动通知执行结果

重写SimpleTaskStepisAutoNotify方法为false即可手动通知执行结果。

代码语言:javascript复制
public class StepATask extends SimpleTaskStep {

    @Override
    public void doTask() throws Exception {
        // todo
        // 手动通知任务链任务完成
        notifyTaskSucceed();
    }

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

参数传递

  • 任何TaskStep我们都可以通过getTaskParam获取任务参数和任务执行结果ITaskParam
  • 上一个TaskStep保存处理过的任务参数会自动带入到下一个TaskStep中去,因此最后一个TaskStep拥有之前所有任务的参数数据。
代码语言:javascript复制
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);
    }
})

线程控制

设置任务的threadType类型,即可完成对任务运行线程的控制。目前支持6种线程处理方式。

类型

描述

线程池构成

MAIN

主线程(UI线程)

/

ASYNC

异步线程(开子线程,普通线程池)

核心线程数和最大线程为CPU数,0s keepTime,LinkedBlockingQueue(128),线程优先级5

ASYNC_IO

异步线程(开子线程,io线程池)

核心线程数和最大线程为(2*CPU数 1),30s keepTime,LinkedBlockingQueue(128),线程优先级5

ASYNC_EMERGENT

异步线程(开子线程,紧急线程池)

核心线程数为2,最大线程为∞,60s keepTime,SynchronousQueue(不阻塞),线程优先级10

ASYNC_BACKGROUND

异步线程(开子线程,优先级较低线程池)

核心线程数和最大线程为2,0s keepTime,LinkedBlockingQueue(128),线程优先级1

SYNC

同步线程(直接执行)

/

代码语言:javascript复制
// 1.构造时传入线程
XTaskStep taskStep = XTask.getTask(new SimpleTaskCommand(1000), ThreadType.ASYNC_EMERGENT);
// 2.设置线程的方法
taskStep.setThreadType(ThreadType.ASYNC_IO);

任务组

目前共有串行任务组(SerialGroupTaskStep)和并行任务组(ConcurrentGroupTaskStep)

串行任务组

串行任务组是按顺序依次执行,和任务链的处理方式类似。使用XTask.getSerialGroupTask获取。

代码语言:javascript复制
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);

并行任务组

并行任务组是组内所有任务同时执行,待所有任务都完成后才视为任务组完成。使用XTask.getConcurrentGroupTask获取。

代码语言:javascript复制
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,一枚热爱学习,爱好编程,致力于Android架构研究以及开源项目经验分享的技术up主。

0 人点赞