Preface
Last Saturday, I ate the wrong food, suddenly dizzy, fever, vomiting suddenly in the middle of the night, diarrhea, the whole person was tossed enough. I went to the hospital for an examination and said it was caused by xx enteritis and eating the wrong food. I lay at home and rested for four days.
Today, it finally came over. Update the last article in the Android startup optimization directed acyclic graph series. Recently, articles in this area will not be updated for the time being. The series of articles are summarized as follows:
Android startup optimization (1)-directed acyclic graph
Android startup optimization (two)-the principle of topological sorting and problem-solving ideas
Android startup optimization (3)-AnchorTask instructions
Android startup optimization (4)-teach you how to implement AnchorTask
Release Notes
- Pre-configured version prior to 0.1.0 dependent tasks, by
AnchorTask getDependsTaskList
the way, he is throughclassName
to findAnchorTask
and cohesion in the current AnchorTask, from a global point of view, this approach is not very intuitive, 1.0.0 gave up ways, Ali referenceAlpha
way throughaddTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)
- 1.0.0 version adds Project class, and increase
OnProjectExecuteListener
listening - New version 1.0.0
OnGetMonitorRecordCallback
monitor, facilitate the statistics of each task takes
Description
Android startup optimization, you may think of asynchronous loading for the first time. Put time-consuming tasks on the sub-thread to load, and then enter the home page after all loading tasks are loaded.
The multi-threaded asynchronous loading scheme is indeed ok. But what if you encounter a dependency relationship. For example, task 2 depends on task 1. How to solve it at this time?
Suppose we have such task dependency
How do we use it
val project =
AnchorProject.Builder().setContext(context).setLogLevel(LogUtils.LogLevel.DEBUG)
.setAnchorTaskCreator(ApplicationAnchorTaskCreator())
.addTask(TASK_NAME_ZERO)
.addTask(TASK_NAME_ONE)
.addTask(TASK_NAME_TWO)
.addTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)
.addTask(TASK_NAME_FOUR).afterTask(TASK_NAME_ONE, TASK_NAME_TWO)
.addTask(TASK_NAME_FIVE).afterTask(TASK_NAME_THREE, TASK_NAME_FOUR)
.build()
project.start().await()
复制代码
class ApplicationAnchorTaskCreator : IAnchorTaskCreator {
override fun createTask(taskName: String): AnchorTask? {
when (taskName) {
TASK_NAME_ZERO -> {
return AnchorTaskZero()
}
TASK_NAME_ONE -> {
return AnchorTaskOne()
}
TASK_NAME_TWO -> {
return AnchorTaskTwo()
}
TASK_NAME_THREE -> {
return AnchorTaskThree()
}
TASK_NAME_FOUR -> {
return AnchorTaskFour()
}
TASK_NAME_FIVE -> {
return AnchorTaskFive()
}
}
return null
}
}
复制代码
When the demo runs, you can see the expected effect.
Basic use
Step 1: Configure remote dependencies in moulde build.gradle
implementation 'com.xj.android:anchortask:1.0.0'
复制代码
The latest version number can be found here lastedt version
Step two: custom AnchorTaskZero
, inheritance AnchorTask
, and specify taskName
, attention taskName
must be unique, because we will be in accordance with taskName
the corresponding find AnchorTask
the appropriate override method
class AnchorTaskZero() : AnchorTask(TASK_NAME_ZERO) {
override fun isRunOnMainThread(): Boolean {
return false
}
override fun run() {
val start = System.currentTimeMillis()
try {
Thread.sleep(300)
} catch (e: Exception) {
}
LogUtils.i(
TAG, "AnchorTaskOne: " + (System.currentTimeMillis() - start)
)
}
}
复制代码
If task three depends on task two and task one, you can write like this
addTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)
复制代码
Finally, the project.start()
method starts, if necessary block waiting, call await () method
AnchorProject.Builder().setContext(context).setLogLevel(LogUtils.LogLevel.DEBUG)
.setAnchorTaskCreator(ApplicationAnchorTaskCreator())
.addTask(TASK_NAME_ZERO)
.addTask(TASK_NAME_ONE)
.addTask(TASK_NAME_TWO)
.addTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)
.addTask(TASK_NAME_FOUR).afterTask(TASK_NAME_ONE, TASK_NAME_TWO)
.addTask(TASK_NAME_FIVE).afterTask(TASK_NAME_THREE, TASK_NAME_FOUR)
.build()
project.start().await()
复制代码
Monitor task callback
project.addListener(object : OnProjectExecuteListener {
// project 开始执行的时候
override fun onProjectStart() {
com.xj.anchortask.LogUtils.i(MyApplication.TAG, "onProjectStart ")
}
// project 执行一个 task 完成的时候
override fun onTaskFinish(taskName: String) {
com.xj.anchortask.LogUtils.i(
MyApplication.TAG,
"onTaskFinish, taskName is $taskName"
)
}
// project 执行完成的时候
override fun onProjectFinish() {
com.xj.anchortask.LogUtils.i(MyApplication.TAG, "onProjectFinish ")
}
})
复制代码
Add time-consuming callback for each task execution
project.onGetMonitorRecordCallback = object : OnGetMonitorRecordCallback {
// 所有 task 执行完毕会调用这个方法,Map 存储了 task 的执行时间, key 是 taskName,value 是时间,单位毫秒
override fun onGetTaskExecuteRecord(result: Map<String?, Long?>?) {
onGetMonitorRecordCallback?.onGetTaskExecuteRecord(result)
}
// 所有 task 执行完毕会调用这个方法,costTime 执行时间
override fun onGetProjectExecuteTime(costTime: Long) {
onGetMonitorRecordCallback?.onGetProjectExecuteTime(costTime)
}
}
复制代码
AnchorProject introduction
AnchorTaskDispatcher start
The method must be called in the main thread, and the sub-thread calls will throw an exception.await
Block the current thread, wait for all tasks to be executed, and then go down automatically, the await method carries a parameter, timeOutMillion represents the timeout waiting timeawait()
Method must be called after the start method- Add a task by
AnchorProject.Builder().addTask
adding a typical configuration mode - Set the thread pool for execution, you can pass
AnchorProject.Builder().setThreadPoolExecutor(TaskExecutorManager.instance.cpuThreadPoolExecutor)
Introduction to AnchorTask
AnchorTask implements the IAnchorTask interface, there are several main methods
isRunOnMainThread(): Boolean
Indicates whether to run on the main thread, the default value is falsepriority(): Int
The method indicates the priority level of the thread, the default value is Process.THREAD_PRIORITY_FOREGROUNDneedWait()
Means that when we callAnchorTaskDispatcher await
upon, whether to wait, return true, expressed the need to wait for the end to change the task execution,AnchorTaskDispatcher await
the method can continue down the implementation.fun run()
Method, which means when the task is executed
interface IAnchorTask : IAnchorCallBack {
/**
* 是否在主线程执行
*/
fun isRunOnMainThread(): Boolean
/**
* 任务优先级别
*/
@IntRange(
from = Process.THREAD_PRIORITY_FOREGROUND.toLong(),
to = Process.THREAD_PRIORITY_LOWEST.toLong()
)
fun priority(): Int
/**
* 调用 await 方法,是否需要等待改任务执行完成
* true 不需要
* false 需要
*/
fun needWait(): Boolean
/**
* 任务被执行的时候回调
*/
fun run()
}
复制代码
class AnchorTaskOne : AnchorTask() {
override fun isRunOnMainThread(): Boolean {
return false
}
override fun run() {
val start = System.currentTimeMillis()
try {
Thread.sleep(300)
} catch (e: Exception) {
}
LogUtils.i(
TAG, "AnchorTaskOne: " + (System.currentTimeMillis() - start)
)
}
}
复制代码
The callback of the monitoring task
val anchorTask = AnchorTaskTwo()
anchorTask.addCallback(object : IAnchorCallBack {
override fun onAdd() {
com.xj.anchortask.LogUtils.i(TAG, "onAdd: $anchorTask")
}
override fun onStart() {
com.xj.anchortask.LogUtils.i(TAG, "onStart:$anchorTask ")
}
override fun onFinish() {
com.xj.anchortask.LogUtils.i(TAG, "onFinish:$anchorTask ")
}
})
复制代码
to sum up
The principle of AnchorTask is not complicated, and the essence is the combination of directed acyclic graph and multithreaded knowledge.
- Construct a directed acyclic graph according to BFS, and get its topological sort
- In the process of multi-threaded execution, we ensure the sequential execution relationship through the task's sub-task relationship and CounDownLatch
- If the predecessor task has not been executed, wait, if the execution is completed, go down
- Perform task
- Notify the subtask that the current task is completed, and the corresponding counter (in degrees) should be reduced by one.
More android development welcome to join our Android advanced learning circle