Android 启动优化(五)- AnchorTask 1.0.0 版本正式发布了

微信公众号:程序员徐公(stormjun94),本文首发我的微信公众号,有兴趣的可以点此扫码关注

前言

上周六,吃错了东西,突然头晕,发烧,半夜突然呕吐,拉肚子,整个人被折腾得够呛的。到医院检查,说是 xx 肠炎,吃错东西导致的,整整躺在家休息四天。

今天,终于缓过来了。更新一下 Android 启动优化有向无环图系列的最后一篇文章。最近一段时间,暂时不会更新这方面的文章了。系列文章汇总如下:

Android 启动优化(一) - 有向无环图

Android 启动优化(二) - 拓扑排序的原理以及解题思路

Android 启动优化(三) - AnchorTask 使用说明

Android 启动优化(四)- 手把手教你实现 AnchorTask

更新说明

  1. 之前的 0.1.0 版本 配置前置依赖任务,是通过 AnchorTask getDependsTaskList 的方式,他是通过 className 找到 AnchorTask,并且内聚在当前的 AnchorTask 中,从全局的角度看 ,这种方式不太直观,1.0.0 放弃了这种方式,参考阿里 Alpha 的方式,通过 addTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)
  2. 1.0.0 版本新增了 Project 类,并增加 OnProjectExecuteListener 监听
  3. 1.0.0 版本新增 OnGetMonitorRecordCallback 监听,方便统计各个任务的耗时

说明

Android 启动优化,大家第一时间可能会想到异步加载。将耗时任务放到子线程加载,等到所有加载任务加载完成之后,再进入首页。

多线程异步加载方案确实是 ok 的。但如果遇到前后依赖的关系呢。比如任务2 依赖于任务 1,这时候要怎么解决呢。

假设我们有这样的任务依赖

我们要怎么使用它呢

        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
    }

}

Demo 跑起来,可以看到预期的效果。

基本使用

第一步:在 moulde build.gradle 配置远程依赖

implementation 'com.xj.android:anchortask:1.0.0'

最新的版本号可以看这里 lastedt version

第二步:自定义 AnchorTaskZero,继承 AnchorTask,并指定 taskName,注意 taskName 必须是唯一的,因为我们会根据 taskName 找到相应的 AnchorTask 重写相应的方法

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)
        )
    }
}

如果任务 三 依赖任务 二,任务 一,可以这样写

addTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)

最后,通过 project.start() 方法启动, 如果需要阻塞等待,调用 await() 方法

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()

监听任务回调

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 ")
            }

        })

添加每个任务执行耗时回调

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 介绍

  1. AnchorTaskDispatcher start 方法必须在主线程调用,子线程调用会抛出异常。
  2. await 阻塞当前线程,等待所有任务执行完毕之后,会自动往下走,await 方法携带一个参数,timeOutMillion 表示超时等待的时间
  3. await() 方法必须在 start 方法之后调用
  4. 添加任务是通过 AnchorProject.Builder().addTask 添加的,典型的构造模式
  5. 设置执行的线程池,可以通过 AnchorProject.Builder().setThreadPoolExecutor(TaskExecutorManager.instance.cpuThreadPoolExecutor)

AnchorTask 介绍

AnchorTask 实现了 IAnchorTask 接口,主要有几个方法

  • isRunOnMainThread(): Boolean表示是否在主线程运行,默认值是 false
  • priority(): Int 方法 表示线程的优先级别,默认值是 Process.THREAD_PRIORITY_FOREGROUND
  • needWait() 表示当我们调用 AnchorTaskDispatcher await 时,是否需要等待,return true,表示需要等待改任务执行结束,AnchorTaskDispatcher await 方法才能继续往下执行。
  • fun run() 方法,表示任务执行的时候
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)
        )
    }

}

监听任务的回调

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 ")
            }

        })

总结

AnchorTask 的原理不复杂,本质是有向无环图与多线程知识的结合。

  1. 根据 BFS 构建出有向无环图,并得到它的拓扑排序
  2. 在多线程执行过程中,我们是通过任务的子任务关系和 CounDownLatch 确保先后执行关系的
    1. 前置任务没有执行完毕的话,等待,执行完毕的话,往下走
    2. 执行任务
    3. 通知子任务,当前任务执行完毕了,相应的计数器(入度数)要减一。

AnchorTask

想看 1.0.0 版本的具体实现,可以看这篇文章。 AnchorTask 1.0.0 原理说明

如果你觉得对你有所帮助,可以关注我的微信公众号 程序员徐公

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员徐师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值