Android 启动优化(五)- AnchorTask 1.0.0 版本正式发布了
今天,更新一下 Android 启动优化有向无环图系列的最后一篇文章。最近一段时间,暂时不会更新这方面的文章了。系列文章汇总如下:
Android 启动优化(一) - 有向无环图
Android 启动优化(二) - 拓扑排序的原理以及解题思路
Android 启动优化(三)- AnchorTask 开源了
Android 启动优化(四)- AnchorTask 是怎么实现的
更新说明
之前的 0.1.0 版本 配置前置依赖任务,是通过
AnchorTask getDependsTaskList
的方式,他是通过className
找到AnchorTask
,并且内聚在当前的 AnchorTask 中,从全局的角度看 ,这种方式不太直观,1.0.0 放弃了这种方式,参考阿里Alpha
的方式,通过addTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)
1.0.0 版本新增了 Project 类,并增加
OnProjectExecuteListener
监听1.0.0 版本新增
OnGetMonitorRecordCallback
监听,方便统计各个任务的耗时
说明
Android 启动优化,大家第一时间可能会想到异步加载。将耗时任务放到子线程加载,等到所有加载任务加载完成之后,再进入首页。
多线程异步加载方案确实是 ok 的。但如果遇到前后依赖的关系呢。比如任务2 依赖于任务 1,这时候要怎么解决呢。
假设我们有这样的任务依赖
我们要怎么使用它呢
1 val project =2 AnchorProject.Builder().setContext(context).setLogLevel(LogUtils.LogLevel.DEBUG)3 .setAnchorTaskCreator(ApplicationAnchorTaskCreator())4 .addTask(TASK_NAME_ZERO)5 .addTask(TASK_NAME_ONE)6 .addTask(TASK_NAME_TWO)7 .addTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)8 .addTask(TASK_NAME_FOUR).afterTask(TASK_NAME_ONE, TASK_NAME_TWO)9 .addTask(TASK_NAME_FIVE).afterTask(TASK_NAME_THREE, TASK_NAME_FOUR)
10 .build()
11 project.start().await()
1class ApplicationAnchorTaskCreator : IAnchorTaskCreator {2 override fun createTask(taskName: String): AnchorTask? {3 when (taskName) {4 TASK_NAME_ZERO -> {5 return AnchorTaskZero()6 }78 TASK_NAME_ONE -> {9 return AnchorTaskOne()
10 }
11 TASK_NAME_TWO -> {
12 return AnchorTaskTwo()
13 }
14 TASK_NAME_THREE -> {
15 return AnchorTaskThree()
16 }
17 TASK_NAME_FOUR -> {
18 return AnchorTaskFour()
19 }
20 TASK_NAME_FIVE -> {
21 return AnchorTaskFive()
22 }
23 }
24 return null
25 }
26
27}
Demo 跑起来,可以看到预期的效果。
基本使用
第一步:在 moulde build.gradle 配置远程依赖
1implementation 'com.xj.android:anchortask:1.0.0'
最新的版本号可以看这里 lastedt version
第二步:自定义 AnchorTaskZero
,继承 AnchorTask
,并指定 taskName
,注意 taskName
必须是唯一的,因为我们会根据 taskName
找到相应的 AnchorTask
重写相应的方法
1class AnchorTaskZero() : AnchorTask(TASK_NAME_ZERO) {2 override fun isRunOnMainThread(): Boolean {3 return false4 }56 override fun run() {7 val start = System.currentTimeMillis()8 try {9 Thread.sleep(300)
10 } catch (e: Exception) {
11 }
12 LogUtils.i(
13 TAG, "AnchorTaskOne: " + (System.currentTimeMillis() - start)
14 )
15 }
16}
如果任务 三 依赖任务 二,任务 一,可以这样写
1addTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)
最后,通过 project.start()
方法启动, 如果需要阻塞等待,调用 await() 方法
1AnchorProject.Builder().setContext(context).setLogLevel(LogUtils.LogLevel.DEBUG)2 .setAnchorTaskCreator(ApplicationAnchorTaskCreator())3 .addTask(TASK_NAME_ZERO)4 .addTask(TASK_NAME_ONE)5 .addTask(TASK_NAME_TWO)6 .addTask(TASK_NAME_THREE).afterTask(TASK_NAME_ZERO, TASK_NAME_ONE)7 .addTask(TASK_NAME_FOUR).afterTask(TASK_NAME_ONE, TASK_NAME_TWO)8 .addTask(TASK_NAME_FIVE).afterTask(TASK_NAME_THREE, TASK_NAME_FOUR)9 .build()
10project.start().await()
监听任务回调
1project.addListener(object : OnProjectExecuteListener {23 // project 开始执行的时候4 override fun onProjectStart() {5 com.xj.anchortask.LogUtils.i(MyApplication.TAG, "onProjectStart ")6 }78 // project 执行一个 task 完成的时候9 override fun onTaskFinish(taskName: String) {
10 com.xj.anchortask.LogUtils.i(
11 MyApplication.TAG,
12 "onTaskFinish, taskName is $taskName"
13 )
14 }
15
16 // project 执行完成的时候
17 override fun onProjectFinish() {
18 com.xj.anchortask.LogUtils.i(MyApplication.TAG, "onProjectFinish ")
19 }
20
21 })
添加每个任务执行耗时回调
1project.onGetMonitorRecordCallback = object : OnGetMonitorRecordCallback {23 // 所有 task 执行完毕会调用这个方法,Map 存储了 task 的执行时间, key 是 taskName,value 是时间,单位毫秒4 override fun onGetTaskExecuteRecord(result: Map<String?, Long?>?) {5 onGetMonitorRecordCallback?.onGetTaskExecuteRecord(result)6 }78 // 所有 task 执行完毕会调用这个方法,costTime 执行时间9 override fun onGetProjectExecuteTime(costTime: Long) {
10 onGetMonitorRecordCallback?.onGetProjectExecuteTime(costTime)
11 }
12
13 }
AnchorProject 介绍
AnchorTaskDispatcher start
方法必须在主线程调用,子线程调用会抛出异常。await
阻塞当前线程,等待所有任务执行完毕之后,会自动往下走,await 方法携带一个参数,timeOutMillion 表示超时等待的时间await()
方法必须在 start 方法之后调用添加任务是通过
AnchorProject.Builder().addTask
添加的,典型的构造模式设置执行的线程池,可以通过
AnchorProject.Builder().setThreadPoolExecutor(TaskExecutorManager.instance.cpuThreadPoolExecutor)
AnchorTask 介绍
AnchorTask 实现了 IAnchorTask 接口,主要有几个方法
isRunOnMainThread(): Boolean
表示是否在主线程运行,默认值是 falsepriority(): Int
方法 表示线程的优先级别,默认值是 Process.THREAD_PRIORITY_FOREGROUNDneedWait()
表示当我们调用AnchorTaskDispatcher await
时,是否需要等待,return true,表示需要等待改任务执行结束,AnchorTaskDispatcher await
方法才能继续往下执行。fun run()
方法,表示任务执行的时候
1interface IAnchorTask : IAnchorCallBack {23 /**4 * 是否在主线程执行5 */6 fun isRunOnMainThread(): Boolean78 /**9 * 任务优先级别
10 */
11 @IntRange(
12 from = Process.THREAD_PRIORITY_FOREGROUND.toLong(),
13 to = Process.THREAD_PRIORITY_LOWEST.toLong()
14 )
15 fun priority(): Int
16
17 /**
18 * 调用 await 方法,是否需要等待改任务执行完成
19 * true 不需要
20 * false 需要
21 */
22 fun needWait(): Boolean
23
24 /**
25 * 任务被执行的时候回调
26 */
27 fun run()
28
29}
1class AnchorTaskOne : AnchorTask() {2 override fun isRunOnMainThread(): Boolean {3 return false4 }56 override fun run() {7 val start = System.currentTimeMillis()8 try {9 Thread.sleep(300)
10 } catch (e: Exception) {
11 }
12 LogUtils.i(
13 TAG, "AnchorTaskOne: " + (System.currentTimeMillis() - start)
14 )
15 }
16
17}
监听任务的回调
1val anchorTask = AnchorTaskTwo()2 anchorTask.addCallback(object : IAnchorCallBack {3 override fun onAdd() {4 com.xj.anchortask.LogUtils.i(TAG, "onAdd: $anchorTask")5 }67 override fun onStart() {8 com.xj.anchortask.LogUtils.i(TAG, "onStart:$anchorTask ")9 }
10
11 override fun onFinish() {
12 com.xj.anchortask.LogUtils.i(TAG, "onFinish:$anchorTask ")
13 }
14
15 })
总结
AnchorTask 的原理不复杂,本质是有向无环图与多线程知识的结合。
根据 BFS 构建出有向无环图,并得到它的拓扑排序
在多线程执行过程中,我们是通过任务的子任务关系和 CounDownLatch 确保先后执行关系的
前置任务没有执行完毕的话,等待,执行完毕的话,往下走
执行任务
通知子任务,当前任务执行完毕了,相应的计数器(入度数)要减一。
AnchorTask:https://github.com/gdutxiaoxu/AnchorTask
想看 1.0.0 版本的具体实现,可以看这篇文章。AnchorTask 1.0.0 原理说明。
如果你觉得对你有所帮助,可以关注我的微信公众号 程序员徐公
Android 启动优化(一) - 有向无环图
Android 启动优化(二) - 拓扑排序的原理以及解题思路
Android 启动优化(三)- AnchorTask 开源了
Android 启动优化(四)- AnchorTask 是怎么实现的
Android 启动优化(五)- AnchorTask 1.0.0 版本正式发布了相关推荐
- 深入探索Android 启动优化(七) - JetPack App Startup 使用及源码浅析
本文首发我的微信公众号:徐公,想成为一名优秀的 Android 开发者,需要一份完备的 知识体系,在这里,让我们一起成长,变得更好~. 前言 前一阵子,写了几篇 Android 启动优化的文章,主要是 ...
- 启动优化·基础论·浅析 Android 启动优化
" [小木箱成长营]启动优化系列文章(排期中): 启动优化 · 工具论 · 启动优化常见的六种工具 启动优化 · 方法论 · 这样做启动优化时长降低 70% 启动优化 · 实战论 · 手把手 ...
- Android 启动优化(一) - 有向无环图
前言 说到 Android 启动优化,大家第一时间可能会想到异步加载.将耗时任务放到子线程加载,等到所有加载任务加载完成之后,再进入首页. 多线程异步加载方案确实是 ok 的.但如果遇到前后依赖的关系 ...
- Android启动优化实战(有效降低APP启动时间)
1.概述 手机点击一个APP,用户希望应用能够及时响应并快速加载.启动时间过长的应用不能满足这个期望,并且可能会令用户失望.这种糟糕的体验可能会导致用户在 Play 商店针对您的应用给出很低的评分,甚 ...
- android布局优化方案,Android启动优化-布局优化
Android启动优化-布局优化 安卓应用开发发展到今天,已经成为一个非常成熟的技术方向,从目前的情况看,安卓开发还是一个热火朝天的发展,但高级人才却相对较少,如今移动互联网的开发者也逐渐开始注重插入 ...
- android 启动优化方案,Android 项目优化(五):应用启动优化
介绍了前面的优化的方案后,这里我们在针对应用的启动优化做一下讲解和说明. 一.App启动概述 一个应用App的启动速度能够影响用户的首次体验,启动速度较慢(感官上)的应用可能导致用户再次开启App的意 ...
- Android 启动优化(一)
声明:本篇文章已授权微信公众号 YYGeeker 独家发布. 前言 对于一个APP来说,启动秒开,切换顺畅的体验能给用户留下良好的第一印象,启动速度对于用户体验及提高用户留存的重要性不言而喻.那么我们 ...
- Android启动优化方案调研
/ 今日科技快讯 / 7月21日,国家互联网信息办公室依据<网络安全法><数据安全法><个人信息保护法><行政处罚法>等法律法规,对滴滴全球股份 ...
- Android 启动优化总结
前言 性能优化包括很多方面,比如:启动优化.布局优化.内存优化.卡顿优化.网络优化.数据库优化.内存泄漏优化.包体积优化等等. 冷启动.温启动.热启动 首先了解下启动的这三个概念,也是面试常被问到的: ...
最新文章
- 徘徊于win和ubuntu
- primefaces_PrimeFaces 5.0 DataTable列切换器
- DMN 1.1 XML:使用Drools 7.0从建模到自动化
- python怎么定义正方形函数_python – Matplotlib自定义图例以显示正方形而不是矩形...
- 年度总结 是不公平和智障的产物
- Codeforces Round #500 (Div. 2) C.Photo of The Sky
- VSCode运行Python教程
- 计算机二级数据模型三要素,2017年计算机二级公共基础知识重点讲解:数据模型...
- 移动web适配利器-rem
- J2ME开发模拟器KEmulator简介及使用
- JVM,卷走面试官(二)—— 有党性的前端编译
- 小程序路径与APPID获取
- 数据库防火墙:数据库防火墙商业化的前提条件
- uniCloud云开发平台简介及云函数数据库基础操作练习(新人首作,欢迎支持
- 第6章 项目整体管理
- 适兕:成为开源布道师
- Excel,根据一列的子集进行筛选
- Impala入门操作
- GitHub上README.md编写教程(基本语法)
- BGA焊点气泡的分布与原因