Android性能优化之启动优化方式详解
日期:2024-03-11 13:09 | 人气:
经过近十年的发展,Android技术优化日新月异,如今Android10.0已经发布,Android系统的表现已经非常流畅,在体验上完全可以媲美iOS。在各大厂商手里,改改源代码、定制系统让Android原生系统变得鱼龙混杂,然后到了不同层次的开发工程师手里,因为技术水平的参差不齐,即使很多手机性能非常高,打开应用时还是会出现卡顿现象。
?APP的性能优化已经成为开发者应该具备的综合素质,也是开发者完成高质量应用作品的保障。本篇文章将详细介绍Android性能优化之启动优化。
在文章开始前,小编有对性能优化方面的知识模块做一个梳理并将其整理成了PDF格式,希望能够帮助到各位正在学习中的朋友。
网上流行一种说法,就是8秒定律,意思是说,如果用户在打开一个页面,在8秒的时间内还没有打开,那么用户大概的会放弃掉,意味着一个用户的流失。从这里就可以看出,启动优化的重要性了。
应用程序启动有三种状态,每种状态都会影响应用程序对用户可见所需的时间,分别是:
-
冷启动
-
热启动
-
温启动
APP冷启动的过程是:ActivityManagerProxy 通过IPC来调用AMS(ActivityManagerService),AMS通过IPC启动一个APP进程,ApplicationThread通过反射来创建Application并且绑定,最后通过ActivityThread来控制activity的生命周期,在相关页面的生命周期中通过ViewRootImpl来对view的实现。从而完成应用的启动。
热启动的速度是最快的,它就是进程从后台切换到前台的一个过程。
温启动只会重新走一遍页面的生命周期,但是对于进程,application不会重新在创建。
上针对启动优化,基本只是优化冷启动就可以了。但是从冷启动的启动流程中很多都是系统做的,我们没有办法操控。我们能做的,就是application的生命周期和activity的生命周期这部分,启动优化往往就是从这两块入手。
使用adb 命令方式(线下使用方便)
adb shell am start -W 包名/包名+类名
?ThisTime:最后一个activity的启动耗时
TotalTime:所有activity的启动耗时
WaitTime:AMS启动activity的总耗时
这里由于我直接进入到主界面,中间并没有SplashActivity,所有ThisTime 和 TotalTime的时间是一样的
优势:在线下使用方便,适合于跑线下的产品,和获取竞品的时间,然后比对
缺点:不能带到线上,获取的时间,只能说是一个大概时间,不是很严谨。
手动打点方式
?通过System.currentTimeMillis()来打时间戳
缺点:很明显,对代码侵入性非常的大,如果说我想要打出每一个任务花费的时间,那么代码看起来就很恶心了
AOP Aspect Oriented Programming 面向切面编程
AOP:通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。它的核心思想就是将应用程序中的业务逻辑处理部分同对其提供通用服务部分即“横切关注点”进行分离。
OOP:引入封装,继承,多态等概念来建立一种对象层次结构,它允许开发者定义纵向的关系,但并不适合横向的关系。 可以说AOP是OOP的一种补充和完善。
aspectj的使用
AspectJ是一个面向切面编程的框架,是对java的扩展且兼容java,AspectJ定义了AOP语法,它有一个专门的编译器来生成遵守java字节编码规范的Class文件。
在项目的根目录的build.gradle添加依赖:
dependencies { classpath ‘com.android.tools.build:gradle:3.5.2’ classpath ‘com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0’ // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files }
在app下的build.gradle添加依赖
apply plugin: ‘android-aspectjx’
在dependencies中添加
implementation ‘org.aspectj:aspectjrt:1.9.4’
然后创建一个类
这样我们运行的时候,就会直接在logcat中打印出application中的onCreate方法中所有调用方法的耗时情况了
2020-07-10 14:22:27.151 1619-1619/? E/MyApplicationAspectj: taskOne cost 150 2020-07-10 14:22:29.203 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskTwo cost 2052 2020-07-10 14:22:29.554 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskThrid cost 351 2020-07-10 14:22:30.556 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskFour cost 1001
这样我们几乎没有碰Application中的任何代码,也就够得出各个方法的耗时,几乎对代码无侵入。
traceview
TraceView是Android SDK中内置的一个工具,他可以加载trace文件,以图形化的形式展示相应代码的执行时间,次数及调用栈,便于我们分析。
运行项目就可以我们的SD卡中找到对应的trace文件了,如果是Android studio可以直接在右下角找到 DeviceFileExporer –>sdcard –> Android — > data –>files —>自己项目的包名
然后双击即可查看文件了
优点:使用简单,图形形式展示所执行的时间,调用栈等。
缺点:会影响到我们优化的方向,由于是图形化展示,也是比较占用CPU资源的,所以得到的时间往往是比实际的要大。
上面介绍了多了几个获取任务执行时间的方式和工具,那么当我们知道某个方法耗时了,我们该怎么处理呢?
现在application的onCreate方法中有几个任务,各个耗时是不一样的。可能很多同学就会说了,异步处理啊,开线程,放到线程池中,或者创建一个IntentService来执行。那么我们就要考虑几个问题了
第一:异步处理,如果在某个页面需要用到某个SDK,但是又没有初始化完成呢?
第二:假如说taskTwo需要taskOne的某个返回值呢?怎么保证taskOne在taskTwo之前执行完毕呢?
第三:开线程,开多少个线程呢?多了会造成资源浪费,少了资源又没有合理的利用。
我个人觉得针对于启动优化,在Application中的onCreate()进行初始化任务操作,我们首先需要对这些任务进行一个优先级划分,针对于那些优先级高的任务,我们可以优先进行处理,对于那些优先级较低的,我们可以适当的延迟进行加载。
其次有很多同学喜欢把那些优先级较低的任务进行延迟加载,比如new Handler().postDelayed(),这种我觉得是非常不可取的,假如说放在postDelayed中的任务耗时2s,延迟1s进行处理,那么在执行2s任务的过程中,有用户进行操作,那岂不是很卡吗,很明显,这是指标不治本的。
启动器的思想
针对上面说的几个痛点,怎么在处理上面的几个痛点,又能保证代码的可维护性呢?换句话说就是一个新人不需要理解整个过程,直接就可以开干呢?那么,启动器来了。
启动器核心思想:充分利用CPU多核,自动梳理任务顺序
启动器的原理
1、任务全部封装成Task对象,传入到集合中。
2、根据所有的任务依赖关系,形成一个有向无环图,然后通过拓扑排序排列出任务的执行流程
3、通过CountDownLatch来控制某一个任务是否执行完毕才进行下一步。
4、线程池创建核心线程的数量,由手机的核数量决定的。
启动器使用方式
?启动器核心代码
进行任务排序
执行任务代码
基本核心代码就是上面这几个。
对延迟任务进行分批初始化
使用IdleHandler特性,进行空闲执行 (适合优先级不是很高,不急于初始化的第三方SDK)
IdleHandler:IdleHandler 可以用来提升性能,主要用在我们希望能够在当前线程消息队列空闲时做些事情(譬如 UI 线程在显示完成后,如果线程空闲我们就可以提前准备其他内容)的情况下,不过最好不要做耗时操作。简单来说就是,looper对象有空的时候就会执行IdleHandler中的任务。
前面说过,在application的任务进行优先级划分,那么如果优先级低的任务,我们是不是可以不再application的onCreate中进行初始化呢?是否可以放到activity中进行呢?如果可以在activity中,那么在activity那个阶段适合呢?其实在onCreate(),onResume()都是可以的。当然我们也可以使用view中的getViewTreeObserver().addOnPreDrawListener进行监听,这个事件是视图将要绘制的时候会回调该方法。我们可以在回调的方法中将要初始化的SDK放大IdleHandler中。
提前加载SP 可以放到multidex之前加载,利用此阶段的CPU
SharedPreferences,以键值对的形式进行数据的保存的,会一次性加载到内存中,所以我们可以考虑那个阶段的CPU相对来说比较空闲。如可以放到multidex之前加载,充分利用此阶段的CPU进行
以上就是详解Android性能优化之启动优化的详细内容,Android性能优化之启动优化的资料,点击下面链接或私信小编获取。
App性能优化学习手册:?docs.qq.com/doc/DWGRIR1hVWkFoZWVK