今天跟大家聊一聊,一个android app启动过程,有哪些方法可以加快启动速度。
先来说一说有哪些因素可能会延缓启动速度:
1.UI线程IO操作(数据库、网络、SharePreference、文件读写、磁盘操作、文件夹创建等)
UI线程是Android中负责渲染视图的线程,也是Android四大组建生命周期回调的主要执行线程,还是用户交互事件的主要响应线程,所以UI线程任务繁重。
IO操作容易阻塞,且容易受出现不稳定状况,存在各种潜在威胁。
2.静态类中的静态变量初始化
一个类中的任意方法,哪怕是getClass()被调用,都会导致这个类中的静态变量被初始化,如果这些静态变量的初始化有实例赋值且实例构造中会诱发各种耗时操作,也会对当前线程造成很大影响。
代码事例:
public class ServiceFacade {
public static A a = new A();
public static B b = new B();
public static A getA(){
return a;
}
...
}
3.反射
反射也是一个典型的耗时操作,包括类的反射实例化、field和方法的反射遍历、注解的反射等等,其中,field和方法的反射遍历、注解的反射最为耗时,我们所有的很多框架中都有使用类似的反射,尤其是注解反射,如Retrofit、OrmLite、Butternife等等。当然,他们的有些版本已经将运行时反射注解改成了编译器aapt生成静态代码了。
以Retrofit为例,虽然网络请求在线程中异步执行,但是动态代理的生成仍然是在当前线程中通过反射实现的,所以使用此类框架时要注意其对性能的影响。
4.不必要的业务逻辑
这点不必多说,要让APK快速启动,怎容得这种劳民伤财的事情耽搁启动大业,砍!
5.大量反复的重复查询
万万不得已,有些UI线程中还有查询数据库的操作,或者一时改不成异步,但是也需要优化查询方式。
6.其它线程和进程的资源抢占
BaseFragmentActivity的OnCreate()中bind了新的service进程,导致大量的资源抢占,对启动性能造成了比较大的影响。
分析完了可能造成延缓的因素,再来说一说优化的方案:
启动速度优化是一个系统工程,涉及的面很广,从业务逻辑到UI渲染,从线程调度到算法优化,从性能分析到性能监控。不仅要求开发者在开发的时候有敏感的性能意识,还要求具有统顾全局的高度。用一句话概括,启动速度优化更像是一门统筹学的实践。
1.明确启动的优化目标:让用户以最快的速度看到首屏,且不影响正常的业务逻辑。此外,以什么事件作为启动完成的标志,也是需要明确的。
分析手段:先在启动的关键路径上打点,实践表明,Log是最准确的有效的打点方式(对性能影响最小,对线程时序影响最小),MathodTrace对线程执行性能影响太大,且不容易在启动过程中使用。
关键点:四大组件的生命周期,Application的onCreate(),Activiy的onCreate()、onStart()、onResume()、onWindowFocusChanged()
2.提前后台加载
对于启动中需要的数据准备、初始化等操作,可以利用多线程的形式,在后台线程多提前加载,尤其是和IO相关的操作。
如:数据库查询、网络数据请求、配置信息读取、资源加载等;
等到主线程运行到对应节点时,便可以直接“饭来张口、衣来伸手”了。
3.延迟加载+懒加载 (“懒加载”的原则是不能“懒”!)
a. 巧用单例
单例模式是懒加载天生的伴侣,哪里用到哪里最先加载初始化,类似的思路可以应用在很多模块的初始化上。但是也有需要注意的地方!
采用懒汉模式的单例,尽量不要在类中写公有静态方法。
b. 构造方法中尽量少处理逻辑
单例模式的构造方法中,尽量至保留必要的基础逻辑,不要为了嫌麻烦把很多不必要的初始化操作放在构造方法中,因为后面不一定要用到。
c. 能懒加载的就懒加载,不要吝啬判断逻辑
遵循一个原则“用到再加载”。这一点,就要求在对应的逻辑上做很多类似单例的判断,所以说,懒加载不能懒。
4.前后台多线程多任务(统筹学)
a. 弄清依赖关系(集中方法)
逆推法:先总结确认启动目标的“最小集”,反向逆推构建启动流程
删减法:从头至尾,以质疑的眼光剔除不必要的逻辑(先错杀,杀不掉再救回)
b. 统筹分配任务
规划几条关键路径,弄清关键路径之间的关键交点和节点
合理利用CPU资源,不宜开辟太多线程(同步异步逻辑不好处理,线程过多也降低效率)
根据打点摸清楚各个线程任务的开始结束点,合理统筹
c. UI线程为关键路径,此路径上的绊脚石全部踢开
UI线程上,有些必须走的生命周期回调必须尽量提前,几乎是越往前越好,因为只有这些回调都走完了,我们目标的启动的回调才会调出来。
d. 后台多线程:
IO阻塞任务(如数据库查询、网络请求等),这种以IO为主要资源占用的任务,适合放在后台线程处理,并且可以一开始就抛出去,以达到时间的最大化利用;
含有大量CPU计算工作的任务,如果不是启动必须的,适合抛到首屏加载完成后执行;
如果是启动必须的,也可以通过后台线程来计算处理,理论上,多核CPU的性能可以在对应线程数下发挥到最优,当然,这个需要实验数据证明;
5.抓住关键节点:onWindowFocusChanged()
onWindowFocusChanged可以粗略地作为Activity显示的标志节点,此外还有一些节点也可以作为参考,如:IdleHandler等。
但是实验表明IdleHandler由于某些原因不太可靠,故以保险起见弃用;
onWindowFocusChanged并不是只执行一次,故需要加入“直走一次”的逻辑。
6.优化视图渲染
a. 减少层级
b. 布局文件优化(LinearLayout\RelaitveLayout\FrameLayout)
c. 巧用ViewStub
d. 减少大资源加载,优化大资源视图实现
e. 异步回调加载视图
7.优化数据库查询操作
a. 能合并成一次的操作就合并成一次
b. 至查询条目数量的,就至调用queryCount, 只关心某个字段的,就只查询某个字段(大多数情况还没必要优化到这么细致)
8.巧用内存缓存
a. 重复获取的数据可以使用缓存(数据库、网络等)
b. 代价就是需要有额外的缓存数据同步机制,保证从缓存中获取和从数据库中获取的结果一样;
好了,以上就是这次整理的全部内容,欢迎大家补充,纠正,谢谢。