你的 APP 为何启动那么慢?
本文首发于微信公众号「玉刚说」
原文链接:你的 APP 为何启动那么慢?
App启动方式
冷启动(Cold start)
冷启动是指APP在手机启动后第一次运行,或者APP进程被kill掉后在再次启动。
可见冷启动的必要条件是该APP进程不存在,这就意味着系统需要创建进程,APP需要初始化。在这三种启动方式中,冷启动耗时最长,对于冷启动的优化也是最具挑战的。因此本文重点谈论的是对冷启动相关的优化。
温启动(Warm start)
App进程存在,当时Activity可能因为内存不足被回收。这时候启动App不需要重新创建进程,但是Activity的onCrate还是需要重新执行的。场景类似打开淘宝逛了一圈然后切到微信去聊天去了,过了半小时再次回到淘宝。这时候淘宝的进程存在,但是Activity可能被回收,这时候只需要重新加载Activity即可。
热启动(Hot start)
App进程存在,并且Activity对象仍然存在内存中没有被回收。可以重复避免对象初始化,布局解析绘制。
场景就类似你打开微信聊了一会天这时候出去看了下日历 在打开微信 微信这时候启动就属于热启动。
在最近任务给App加锁和启动方式有什么关系
某些厂商为了用户体验提供了给APP上锁的功能,目的就是让用户自己做主是上锁的APP不被杀,启动的时候不会处于冷启动方式,但是加锁也不是万能的,Low memory killer在内存极度吃紧的情况下也会杀死加锁APP,在此启动时也将以冷启动方式运行。
AI和启动方式有什么关系
AI在进程管理方面可谓是大有可为。MIUI10发布了进程AI唤醒功能,使APP启动速度远超友商。这其中的道理简单说就是学习用户的使用习惯,提前将App进程创建好,当用户打开APP时不会出现冷启动。比如你是微信重度用户你发现用了MIUI10就再也见不到微信启动页面的那个地球了,这就是AI唤醒的功劳。
从点击APP图标到主页显示出现需要经过的步骤
这里我们来讨论冷启动的过程,进程启动原则上有四种途径,也就是通过其他进程对该APP的四大组件的调用来实现。
这里我们重点讨论用户点击桌面后的APP启动,通过startActivity方式的启动。
调用startActivity,该方法经过层层调用,最终会调用ActivityStackSupervisor.java中的startSpecificActivityLocked,当activity所属进程还没启动的情况下,则需要创建相应的进程.
void startSpecificActivityLocked(...) { ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); if (app != null && app.thread != null) { ...//进程已创建 return } //创建进程 mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true);}复制代码
最终进程由 Zygote Fork进程:
进程启动后系统还有一个工作就是:进程启动后立即显示应用程序的空白启动窗口。
一旦系统创建应用程序进程,应用程序进程就会负责下一阶段。这些阶段是:
1.创建应用程序对象
2.启动主线程
3.创建主要Activity
4.绘制视图(View)
5.布局屏幕
6.执行初始化绘制
而一旦App进程完成了第一次绘制,系统进程就会用Main Activity替换已经展示的Background Window,此时用户就可以使用App了。
这里很明显有两个优化点:
1.Application OnCrate()优化
当APP启动时,空白的启动窗口将保留在屏幕上,直到系统首次完成绘制应用程序。此时,系统进程会交换应用程序的启动窗口,允许用户开始与应用程序进行交互。如果应用程序中重载了Application.onCreate(),系统会调用onCreate()方法。之后,应用程序会生成主线程(也称为UI线程),并通过创建MainActivity来执行任务。
2.Activity onCreate()优化
onCreate()方法对加载时间的影响最大,因为它以最高的开销执行工作:加载并绘制视图,以及初始化Activity运行所需的对象。
启动速度优化
如何对启动时间进行量化?
1.目前为止见过最最牛逼的是使用机械手和高速相机测试,手机开机后使用机械手点击应用桌面图标,高速相机记录启动过程,后续通过程序分析视频,从机械手点击图标到Activity显示出来使用了多少时间。这种方式是最直观和精确的,但是成本也很高。
2.通过shell 命令
adb shell am start -W [packageName]/[packageName.MainActivity]复制代码
执行成功后将返回三个测量到的时间:
ThisTime:一般和TotalTime时间一样,除非在应用启动时开了一个透明的Activity预先处理一些事再显示出主Activity,这样将比TotalTime小。
TotalTime:应用的启动时间,包括创建进程+Application初始化+Activity初始化到界面显示。
WaitTime:一般比TotalTime大点,包括系统影响的耗时。
3.可以通过在代码中增加log来计算启动时间
4.使用systrace
Application OnCrate()优化
1.第三方SDK初始化的处理
Application是程序的主入口,很多三方SDK示例程序中都要求自己在Application OnCreate时做初始化操作。这就是增加Application OnCreate时间的主要元凶,所以需要尽量避免在Application onCreate时同步做初始化操作。比较好的解决方案就是对三方SDK就行懒加载,不在Application OnCreate()时初始化,在真正用到的时候再去加载。
下面实例对比下ImageLoader在采用懒加载后启动速度优化。
一般我们在使用imageLoader时都会在Application onCreate()时在主线程加载:
public class MyApplication extends Application {
@Override public void onCreate() { super.onCreate(); ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(this); ImageLoader.getInstance().init(config.build()); }}复制代码
此时使用adb shell am start -W [packageName]/[packageName.MainActivity]检测应用启动时间,每次执行命令时需要杀死进程。
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.luozhanwei.myapplication/.MainActivity }Status: okActivity: com.luozhanwei.myapplication/.MainActivityThisTime: 423TotalTime: 423WaitTime: 441复制代码
Total time在423ms之间,下面是封装了一个懒加载ImageLoader的工具类示例:
public class ImageUtil {
private static boolean sInit;
private synchronized static void ensureInit() { if (sInit) { return; } ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(SecurityCoreApplication.getInstance()); .... // Initialize ImageLoader with configuration. ImageLoader.getInstance().init(config.build()); sInit = true; }
public static void display(String uri, ImageView imageView, boolean cacheOnDisk) { imageView.setImageResource(R.drawable.icon_app_default); ensureInit(); ImageLoader loader = ImageLoader.getInstance(); if (cacheOnDisk) { loader.displayImage(uri, imageView); } else { loader.displayImage(uri, imageView, OPTIONS_NO_CACHE_DISK); } }复制代码
使用这种方案后的启动时间:
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.luozhanwei.myapplication/.MainActivity }Status: okActivity: com.luozhanwei.myapplication/.MainActivityThisTime: 389TotalTime: 389WaitTime: 405复制代码
看到TotalTime比之前减少了34ms(给出的数据为10次检测平均值)。
所以Application OnCreate 避免在主线程做大量耗时操作,例如和IO相关的逻辑,这样都会影响到应用启动速度。如果必须要做需要放到子线程中。
Activity onCreate()优化
减少LaunchActivity的View层级,减少View测量绘制时间。
避免主线程做耗时操作
用户体验优化
消除启动时的白屏/黑屏
为什么启动时会出现短暂黑屏或白屏的现象?当用户点击你的app那一刻到系统调用Activity.onCreate()之间的这个时间段内,WindowManager会先加载app主题样式中的windowBackground做为app的预览元素,然后再真正去加载activity的layout布局。
很显然,如果你的application或activity启动的过程太慢,导致系统的BackgroundWindow没有及时被替换,就会出现启动时白屏或黑屏的情况(取决于你的主题是Dark还是Light)。
解决方案
1.甩锅给系统
使用透明主题:
<item name="android:windowIsTranslucent">true</item>复制代码
Activity.onCreate()之前App不做显示,这样用户误以为是手机慢了,这种瞒天过海的方案大家还是不要用了。
<resources>
<!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="android:windowIsTranslucent">true</item> </style>
</resources>复制代码
效果如下:
2.主题替换
我们在style中自定义一个样式Lancher,在其中放一张背景图片,或是广告图片之类的
<style name="AppTheme.Launcher"> <item name="android:windowBackground">@drawable/bg</item> </style>复制代码
把这个样式设置给启动的Activity
<activity android:name=".activity.SplashActivity" android:screenOrientation="portrait" android:theme="@style/AppTheme.Launcher" >复制代码
然后在Activity的onCreate方法,把Activity设置回原来的主题
@Override protected void onCreate(Bundle savedInstanceState) { //替换为原来的主题,在onCreate之前调用 setTheme(R.style.AppTheme); super.onCreate(savedInstanceState); }复制代码
这样在启动时就通过给用户看一张图片或是广告来防止黑白屏的尴尬。
你的 APP 为何启动那么慢?相关推荐
- Android性能优化之App应用启动分析与优化
前言: 昨晚新版本终于发布了,但是还是记得有测试反馈app启动好长时间也没进入app主页,所以今天准备加个班总结一下App启动那些事! app的启动方式: 1.)冷启动 当启动应用时,后台没 ...
- c++builder启动了怎么停止_App 竟然是这样跑起来的 —— Android App/Activity 启动流程分析...
在我的上一篇文章: AJie:按下电源键后竟然发生了这一幕 -- Android 系统启动流程分析zhuanlan.zhihu.com 我们分析了系统在开机以后的一系列行为,其中最后一阶段 AMS( ...
- 安卓App的启动过程、安卓布局分类及布局和页面的关系
Android App 启动流程: 当你想要启动一个app时,首先得点击该app桌面图标.那点击图标时到底发生了什么呢?先看个理论知识 .Launcher: launcher其实就是一个app,从功能 ...
- 外媒:微信小程序顺应“APP中启动APP”的行业潮流
BI中文站 11月30日报道 上周,中国网络巨头腾讯的高级副总裁张小龙对外披露了一些照片,显示聊天工具微信开始整合"小程序".这一功能可以让微信的用户在无需下载软件的基础上,使用各 ...
- springboot启动自动关闭_App自动化测试用例格式和App的启动与关闭
[一]基本的用例格式 这里使用unittest测试框架来编写用例. 注:unittest可以提供用例的组织和管理.预期结果验证.批量化运行.产生测试报告等功能. 新建一个calculator的pyth ...
- android反射开启通知_作为Android开发者 你真的知道app从启动到主页显示的过程吗?...
前言 之前我跟大家说过,在一个夜黑风高的晚上,我的男同事突然给我发了一条微信,我点开来看,他竟然问我Android从按下开机键到启动到底发生了什么?此刻我的内心如下图: 然后就在昨天晚上,我又收到了他 ...
- 录音机 在launcher中显示_「 腾讯 微信事业部 社招二面」——一个APP从启动到主页面显示经历了哪些过程?...
8月份的时候离职,至今已经2个月了.突击复习下来结果算是有个交代了吧,有赞(深圳) .招商已经过了三面,等待HR面,腾讯过了二面,面试时状态还行,不知会不会有三面,许愿一个三面和HR面吧!(本人比较想 ...
- APP首次启动提示开启定位服务
最近在项目中有用到位置,以前都是在进入需要位置信息的地方才去让用户打开定位,或者是在用户安装APP的时候就让用户给APP赋予读取位置的权限,但是现在想要用户在安装完App第一次启动的时候才去询问用户是 ...
- APP的启动流程梳理
前言 2022年的第一篇分享文章,整整一周,利用晚上下班时间梳理了桌面点击APP到打开的流程,可以说看似简简单单的一个操作,里边的过程相当复杂,各种进程间通信等等,下面我们一起看看整个流程是怎么样的. ...
最新文章
- 为什么 JSP 还没有被淘汰?
- 使用深度学习和OpenCV的早期火灾检测系统
- mysql 协议的ResultsetRow包及解析
- 服务器报错:“/usr/local/var/run/nginx.pid”failed
- 防火墙认证的类型——Vecloud
- CLOUD配置审批流发消息
- 电压kV为什么k要小写,原因你知道吗?
- javascript原型_JavaScript的原型:古怪,但这是它的工作原理
- 命令不识别_互助问答138期:GMM命令代码中如何识别年份国家及异方差检验问题...
- Javascript鸡汤
- 使用SoapUI测试Web Service
- 一步一步实现KNN分类算法
- python123程序设计_Python语言程序设计
- 如何下载矢量电子地图(OpenStreetMap)
- 使用机器学习和数据挖掘算法进行数据处理
- java 调用图灵接口_图灵机器人(问答机器人)API调用示例
- 解决gitlab-runner一直处于等待中
- python抓取google搜索结果
- 在线音乐播放项目——BY音乐
- 有趣好玩的html页面,有趣的HTML5网站集合 - 小俊学习网
热门文章
- python自动化测试看什么书-Python接口自动化测试
- python电脑版微信-python如何实现远程控制电脑(结合微信)
- python对象编程例子-python编程进阶之类和对象用法实例分析
- python银行系统-python简单的银行系统
- python推荐系统-RecQ-Python推荐系统框架
- python零基础怎么学-零基础如何入门Python
- 学python最好的方式-Python 学习怎样开始比较好?
- Warning: Permanently added the RSA host key for IP address '192.30.253.113' to the list of known hos
- LeetCode House Robber II(动态规划)
- spoj450 Enormous Input Test