background

I got in touch with the task flow operation of RxJava a long time ago. I feel that the complex business flow can be separated from one operator by another to form a coherent function, which is very satisfying to write. In fact, this is an application of the chain of responsibility model.

But RxJava is so powerful that I feel overqualified to just use it to handle these business flows.

I have also done application performance optimization for a period of time, including application cold start optimization of course. The concept of initiator was involved in the process. I also looked up some existing open source frameworks and used some of them, but I always felt that they were not very easy to use.

As a veteran of Android open source frameworks, I had an idea in my head: Why don’t I write my own framework for task flow execution? With this in mind, I took some of my spare time (without my girlfriend) and stroked out this XTask framework, feeling very nice about myself and sharing it with you.

Introduction to the

XTask is an extensible Android task execution framework.

You can freely define and combine tasks to achieve the functions you want, especially when dealing with complex business processes. You can flexibly add pre-tasks or adjust the execution sequence. For example, start and initialize the application.

The project address

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

Characteristics of the

  • Six thread types are supported to execute tasks.
  • Supports scheduling and control of execution threads for each task in the task chain.
  • Supports quick task creation and custom task creation.
  • Serial and parallel tasks are supported.
  • Supports data sharing between tasks.
  • Supports the execution of freely combined tasks.
  • Support task chain execution cancellation.
  • Cancels all task chains and the named task chain.
  • Support task chain call sequence record and query.
  • Support for custom thread pools for task execution.

Design idea

The framework body uses the chain of responsibility design pattern, supplemented by the Builder pattern, factory pattern, adapter pattern, composite pattern, facade pattern, and proxy pattern.

The structure

  • ITaskChainEngine: Task chain execution engine, responsible for the overall scheduling of task steps.

  • Task step ITaskStep: Responsible for specific task logic processing.

  • Data storage warehouse IDataStore: A warehouse for storing data. It is mainly used to store data in task parameters.

  • Task parameter ITaskParam: Records the path of the task and manages the parameters generated by the task.

  • Task execution result ITaskResult: Stores the task execution result and generated data.

  • Task group IGroupTaskStep: responsible for the overall scheduling of sub-task steps.

Click to see the FRAMEWORK UML design diagram

Log in


The integration guide

Add Gradle dependencies

1. Add build.gradle repositories in the project root directory:

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

2. Add to dependencies:

dependencies { ... / / XTask implementation 'com. Making. Xuexiangjys: XTask: XTask - core: 1.0.0'}Copy the code

Method of use

XTask serves as a unified API entry point for all common methods.

Open debug Mode

You can enable the debug mode to locate faults and debug the framework.

XTask.debug(true);
Copy the code

XTask API introduction

The method name describe
debug Sets whether to enable debugging
setLogger Custom log printing
setIsLogTaskRunThreadName Sets whether to print the name of the thread where the task is executed. The default value is false
getTaskChain Gets the task chain execution engine
getTask Get simplified tasks
getTaskBuilder Gets the builder of the simplified task
getConcurrentGroupTask Gets parallel task groups
getSerialGroupTask Gets the serial task group
cancelTaskChain Cancel the execution of the specified task chain
postToMain Execute tasks to the main thread
submit Perform common asynchronous tasks
emergentSubmit Perform an emergency asynchronous task
backgroundSubmit Run background asynchronous tasks
ioSubmit Execute asynchronous tasks that consume I/O time
groupSubmit Execute group asynchronous tasks

How do YOU perform a chain of tasks

Here’s a complete example:

Final TaskChainEngine engine = xtask.gettAskChain (); Engine.settaskparam (TaskParam.get("chainName", engine.getName()))); TaskParam taskParam = TaskParam.get("param1", 100) .put("param2", true); // 3. Create multiple tasks, GetTask (new TaskCommand() {@override public void run() {ITaskParam param = 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. Set the task chain execution callback (optional) 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. Task chain execution (must)}).start();Copy the code

1. Create a task chain. (Required)

TaskChainEngine engine = XTask.getTaskChain();
Copy the code

2. Set initialization parameters for the task chain. (Optional)

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

3. Create multiple tasks and add them to the task chain. (Required)

TaskParam TaskParam = taskparam. get("param1", 100). Put ("param2", true); XTaskStep taskStep = XTask.getTask(new TaskCommand() { @Override public void run() { // ... Execute task}}, taskParam); engine.addTask(taskStep) .addTask(XTask.getTask(new TaskCommand() { @Override public void run() { // ... Perform tasks}}));Copy the code

【 Attention 】 To complete the task, pay attention to the following two points:

  • If the task succeeds, it is callednotifyTaskSucceedIf the task fails to execute, thenotifyTaskFailed. Whether the task succeeds or fails, it needs to be invoked as long as the execution is completenotifyTaskXXXNotify the task chain that the task is complete, otherwise the task will not execute normally.
  • TaskCommandandSimpleTaskStepThe default function provides automatic notification of execution results. However, AbstractTaskStep does not provide this function and requires manual notification.

4. Set the task chain to perform the callback. (Optional)

Call setTaskChainCallback to set the task chain to perform the callback.

engine.setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public boolean isCallBackOnMainThread() { // Whether the callback returns the main thread. The default is 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"); }})Copy the code

5. Task chain execution (required)

Call start to execute the task chain.

ICanceller canceller = engine.start();
Copy the code

Task creation

There are two ways to create a task:

  • Build with xtask.gettask
  • inheritanceSimpleTaskStep/AbstractTaskStepImplement task customization

Created by XTask

With xtask.gettask, the corresponding attributes are passed in to build

The property name describe
name Task Step Name
command Task Execution Content
threadType Thread execution type
taskParam The task parameters
taskHandler tasker
isAutoNotify Whether to automatically notify task execution results
XTaskStep taskStep = XTask.getTask(new TaskCommand() {
    @Override
    public void run() {
        // todo
    }
}, ThreadType.ASYNC, taskParam);
Copy the code

Create by inheritance

Implement specific functions by inheriting SimpleTaskStep or AbstractTaskStep.

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"; }}Copy the code

Principle of Task Execution

Each task relies on the task chain for process control. Any task should follow the following principles:

  • Any task needs to be invoked whether it fails or succeedsnotifyTaskSucceedornotifyTaskFailedTo notify the task chain of the completion of a task.TaskCommandandSimpleTaskStepAutomatic notification of execution results is provided by default.
  • If a task fails, the entire link stops working.
Task type Task Execution Instructions
TaskCommand Automatic notification of execution results. If manual notification is required, you only need to set itisAutoNotifyTo false can
SimpleTaskStep Automatic notification of execution results. If manual notification is required, simply overrideisAutoNotifyThe method is false
AbstractTaskStep The result must be manually notified

TaskCommand manually notifies the execution result

When building a Task by passing in TaskCommand through xtask.gettask, set isAutoNotify to false to manually notify the result of execution.

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, "Task execution failed!" ); } else { notifyTaskSucceed(TaskResult.succeed()); } } }, false)); } engine.start();Copy the code

SimpleTaskStep Indicates the execution result manually

Override the isAutoNotify method of SimpleTaskStep to false to manually notify the result of execution.

Public class StepATask extends SimpleTaskStep {@override public void doTask() throws Exception {// todo // Manually notifying the task chain that the task is complete  notifyTaskSucceed(); } @Override protected boolean isAutoNotify() { return false; }}Copy the code

Parameter passing

  • We can pass any TaskStepgetTaskParamObtain task parameters and execution resultsITaskParam.
  • The parameters saved by the previous TaskStep are automatically carried over to the next TaskStep, so that the last TaskStep has the parameters of all previous tasks.
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); }})Copy the code

The thread of control

To control the running thread of a task, set the threadType type of the task. Currently, six thread processing modes are supported.

type describe Thread pool composition
MAIN Main thread (UI thread) /
ASYNC Asynchronous threads (open child threads, normal thread pool) The number of core threads and maximum threads are the number of cpus, 0s keepTime, LinkedBlockingQueue (128), and thread priority 5
ASYNC_IO Asynchronous threads (open child threads, IO thread pool) The number of core threads and maximum threads are (2* NUMBER of cpus +1), 30s keepTime, LinkedBlockingQueue (128), and thread priority 5
ASYNC_EMERGENT Asynchronous thread (open child thread, emergency thread pool) 60 seconds keepTime, SynchronousQueue (non-blocking), priority 10
ASYNC_BACKGROUND Asynchronous thread (open child thread, lower priority thread pool) The number of core threads and maximum threads are 2,0 s keepTime, LinkedBlockingQueue (128), and thread priority 1
SYNC Synchronous thread (direct execution) /
XTaskStep taskStep = xtask. getTask(new SimpleTaskCommand(1000), ThreadType.ASYNC_EMERGENT); TaskStep. SetThreadType (ThreadType.ASYNC_IO);Copy the code

Task group

There are currently serial task groups (SerialGroupTaskStep) and parallel task groups (ConcurrentGroupTaskStep)

Serial task group

Serial task groups are executed sequentially, similar to the way task chains are handled. Run xtask.getSerialGroupTask to obtain the value.

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);
Copy the code

Parallel task group

In a parallel task group, all tasks in the group are executed at the same time. The task group is considered complete only after all tasks are completed. Using XTask getConcurrentGroupTask access.

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);
Copy the code

The last

If you think this project is helpful to you, you can click star to bookmark or share it to let more people know about this project!

I am Xuexiangjys, a technical leader who loves learning and programming, and is committed to Android architecture research and experience sharing of open source projects. For more information, welcome to wechat search public number: [My Android open Source journey]