关于Android启动优化的总结分享


关于Android启动优化的总结分享

一、Android应用的启动过程

1.Android 冷启动的过程

开局一张图,能仔细看完666
冷启动流程
图片来源:AOSP Android 8.0 冷启动流程分析(二) 作者:Tenderness4

具体启动过程就不累述了,主要总结为4个部分:
1. Android Launcher app 处理点击桌面ICON的启动,注入的方式请求AMS启动对应的APP;
2. 系统Binder服务进程AMS/ATMS,负责处理要启动的应用主页MainActivity的Intent、user id、Token、ActivityResult、Launcher页面的onPause等等配置项的设置;
3. 由Zygote进程fork出目标进程,并执行attach操作关联app的Application类及其生命周期;(65535分包、App.onCreate就是在这里处理的);
4. 启动MainActivity并执行生命周期;

思考:从点icon到MainActivity的完全绘制有哪些过程影响:
a. 1、2不受第三方应用控制,所以耗时是由Google工程师和厂商桌面开发工程师保证启动准确和快速;
b. 3中受到apk大小、方法数多少、SDK初始化的复杂度、SDK个数影响;
c. 4中受到Activity 声明周期中的 onCreate、onStart方法执行过程中是否有耗时操作影响,最终在onResume方法执行时MainActivity才真正意义跑起来。


2.Android 热启动的过程

热启动,从后台唤起应用比冷启动少两个过程,一个是Zygote.fork Process过程,直接使用已有的进程,不需要创建和分配资源;一个是Application attach onCreate过程。


3. 命令查看启动耗时——黑盒耗时

adb shell am start -S -W "full package name/launch activity full path"

使用上述命令可以简单的看出COLD LAUNCH(冷启动)的启动耗时;用于分析耗时优化效果变化。

e.g.
在这里插入图片描述
TotalTime:包含AMS启动、进程fork、Application创建、Activity创建显示可交互的时间,单位ms;
WaitTime:TotalTime + 上一个页面(Launcher页)的onPause时间

一般关注TotalTime即可


二、Android冷启动慢的多种因素

1.Android 应用在启动过程中做了过多加载、SDK初始化

一般地,第三方SDK的初始化,文档都要求在Application创建时,执行初始化,或者有些第三方SDK使用ContentProvicer.onCreate来实例化,即在AndroidManifest.xml文件中配置<application/>下注册<provider/>标签。

因为ContentProvider的创建时机在attachApplication之后,Application.onCreate之前,所以在ContentProvider.onCreate中执行初始化可以是SDK在整个Application的生命周期可用。


2.Android 应用方法数超过65535 分包引入问题(分版本)

分包问题耗时主要是在Android 5.0一下版本显著,5.0以上版本虚拟机本身支持多包,所以无需专门安装分包(MultiDex.install)。

5.0以下版本,使用的Dalvik虚拟机,是一种JIT(Just In Time)即时编译机制,即在应用首次启动时,执行编译任务,本身也会拖慢应用启动速度。加之不支持多包,如果需要多包,就需要使用反射的方式,将出主包以外的其他包加载到包队列中。这就是为什么需要使用MultiDex的原因,兼容SDK 20及以下的版本。minSdkVersion配置为小于20就会提示65535。

  • 5.0及以上版本使用的是ART虚拟机(Android Runtime) 默认支持多包,使用AOT(Ahead Of Time)机制编译,会在安装应用时,执行dex2oat将dex预编译为ELF,启动时可以不用重新编译。并且支持多包

因此在引入多包时需要特殊处理。


3.禁用了WindowPreview

在点击应用icon到Activity显示并可交互,经过了进程创建、Application关联、Application创建、SDK初始化、Activity创建几个过程,但用户最先看到的不是Activity,而是Start Window。

在请求AMS创建Activity的过程中,startActivityLocked方法执行时,Activity的窗口还未显示出来,由AMS决定是否请求WMS显示启动窗口(Start Window),启动窗口的创建由PhoneWindowManager策略来创建,WMS来显示,直到Activity的窗口被创建完毕显示时,由WMS取消Start Window的显示。

在创建过程中:

public class PhoneWindowManager implements WindowManagerPolicy {
	...
	public View addStartingWindow(IBinder appToken, String packageName,  
                                  int theme, CharSequence nonLocalizedLabel,  
                                  int labelRes, int icon) {
    	...
    	if (win.getWindowStyle().getBoolean(  
                    com.android.internal.R.styleable.Window_windowDisablePreview, false)) {  
                return null;  
        }  
    	...
    }
}

会根据Android项目中设置的主题Theme,来决定是否创建Start Window;
如果windowDisablePreview属性设置为true,即窗口预览失效,将不会显示StartWindow,在启动较慢的app点击图片后不会有闪屏页,而是一直等待主页显示!


4.JSBundle的初始化和数据传递耗时

JSBundle的初始化耗时是Android原生应用不具备的。
在创建ReactActivity的过程中,需要:

// ReactActivityDelegate.java
protected void loadApp(String appKey) {
    mReactDelegate.loadApp(appKey);
    getPlainActivity().setContentView(mReactDelegate.getReactRootView());
}

// ReactDelegate.java
public void loadApp(String appKey) {
    ...
    mReactRootView = createRootView();
    mReactRootView.startReactApplication(
        getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);
}
Android AppReact Native Android App
inflate view treecreate react root view
-使用JS Bundle 加载和render
将view tree通过setContentView设置到Activity同Android App

React Native Android App的页面创建过程:
a. 通过 mReactRootView = createRootView(); 创建一个根视图,该视图便是React Native应用的最顶部视图;
b. 通过mReactRootView.startReactApplication方法,加载并渲染JS Bundle,此过程是比较耗时的;
c. 通过setContentView(mReactRootView);将根视图绑定到Activity界面上。

startReactApplication方法需要去loadJS ,通过URL方式去下载JS Bundle的资源和数据,底层使用的是Okhttp3,类似本地服务器客户端的访问过程。所以比较耗时。


5.Lottie动效读写文件I/O耗时

动销使用Lottie的json动画效果,需要执行android/assets/目录下文件的读取,json file I/O耗时约300ms


6.其他耗时操作阻塞主线程更新

在主线程中执行文件I/O、网络远程数据获取、复杂逻辑计算、数据量庞大的GPU渲染、频繁invalidate等都会引起阻塞、慢、卡效果,严重时甚至会导致应用ANR、内存抖动和Crash。


三、Android 冷启动的甄别手段和优化方向

1.甄别手段TraceView ——白盒耗时

在需要查看耗时的地方绑桩:

Debug.startMethodTracing()
...
Debug.stopMethodTracing()

在monitor工具中查看各个方法的执行时间


2.优化方向

2.1 对SDK初始化进行懒加载、按需加载

  1. 使用主线程的Handler的idleHandler来加载SDK的初始化;

  2. 一些使用较少的业务需求,使用轻量级的SDK或者自造轮子;

  3. 去除重复的SDK;

  4. 同时支持ContentProvider初始化和手动invoke初始化的情况下,选择手动invoke懒加载。


2.2 对低版本的分包进行多进程分包

对比版本进行进程分包加载:
在App创建上下文时,启动设置process标签的Activity来加载MultiDex的安装,等待安装完成后再从main process安装,此时是直接读缓存,就比较快了。
而这一动作处可以在闪屏页加静图Logo+Loading动画(可选),在Splash页显示广告的期间,操作完成,实现了低版本的启动优化。

2.3 使用WindowPreview进行Splash静图显示,多进程Activity显示加载动画

高版本无需分包优化,SDK >= 21 Android 5.0以上的及其默认支持多包。则可:

  1. 在Logo+ Loading动画页(采用原生页面和原生动效),预加载广告资源;
  2. 在广告页倒计时预加载JS Bundle的页面资源。
  3. 在无广告的情况下,Logo + Loading动画页,预加载JS Bundle的页面资源。

2.4 在启动的过程、UI都采用原生手段,后续业务功能使用RN注入的组件,并在原生Activity启动显示之后,执行JSBundle的预处理;

2.5 I/O读写操作等耗时项采用工作线程处理,并在要求不高的场景下,使用原生动画

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值