前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >5分钟教你打造一个秒开的 Android App

5分钟教你打造一个秒开的 Android App

原创
作者头像
MatthewF
修改2017-07-04 14:09:27
4.1K3
修改2017-07-04 14:09:27
举报
文章被收录于专栏:MatthewMatthew

背景

近日在开发过程中,发现每次点击 app 从桌面启动都有一个在桌面明显的等待时间,机型越低端的越明显,冷启动优化看来已经势在必行,所以怒而一顿研究再解决之。话不多说先上优化前后效果图:

买家秀:

[image.gif]
[image.gif]

淘宝秀:

[image.gif]
[image.gif]

Android App 启动流程:

俗话说要想优化好,流程不可少!关于 android app 启动的流程图如下:

[1499137609999_5619_1499137610023.jpg]
[1499137609999_5619_1499137610023.jpg]

总结一下一个完成的冷启动 app 过程应该是经过:

Zygote Fork Proccess -> Application:attachBaseContext() -> Application:onCreate() -> MainActiviity:onCreate() 

这里主要是把相应的生命周期回调写出来。 因为一般大家的业务代码都是在这些函数回调中调用。

App 冷启动相关优化点

1. 生命周期内减少耗时操作

  • Application:attachBaseContext():
    [1499137706095_1299_1499137705819.png]
    [1499137706095_1299_1499137705819.png]
       这个方法中一般雷区主要都在这句代码上,因为在 Android 5.0(API 21) 以下会存在 65535 方法数分包的问题,当 dex 过大时会导致 Application 启动慢, ANR 或者 ClassNotFound 等异常,关于分包解决和优化方案网上一堆就不在做赘述了。
  • Application:onCreate()

这个方法是需要重点优化的,因为大家的第三方插件初始化一般都会放在这里,在 Application 初始化做繁重的东西会严重阻塞 app 启动(DiskIO,网络请求等)。以下是我们第三方插件初始化的耗时:

debug:

[1499137836623_8171_1499137836492.png]
[1499137836623_8171_1499137836492.png]

release:

[1499137850665_9931_1499137850560.png]
[1499137850665_9931_1499137850560.png]

这里把 debug 和 release 下的时间都贴出来,是希望大家不要重蹈覆辙。。。  因为 debug 模式下和 release 模式下一个是 1000ms 左右, 一个是 300ms 左右,误差很大,请大家一定要在 release 下测试数据!!!!

针对于解决第三方插件初始化耗时方案一般是:

1.SDK 分优先级加载,非必要 SDK 由懒加载实现。

2.可以多线程初始化的 sdk 由多线程方式来进行初始化。

  • MainActiviity:onCreate() 同上一样,尽量不要在此布局做一些耗时的操作或者呈现一些过于复杂的布局。在具体分析自己的 app 时发现 onCreate 中有这样一行代码:
    [1499137907181_7848_1499137906840.png]
    [1499137907181_7848_1499137906840.png]
    其作用是希望用户在打开时,一定能看见 Splash 的画面,主动延迟了 1s 加载。这里其实有更好的解决办法处理,则是把跳转 MainPage 的方法放在 onResume 中而不是放在 onCreate 中。因为 Android 系统中 onResume 一定是处于可见可交互的状态,用户一定能看见 SplashActivity 再去跳转,由系统生命周期决定,而不是固定的等待 1s,此处优化后启动速度又提升了 500ms。所以建议大家以后还是遵循生命周期去做一些事情,尽量别进行人为延迟阻塞。2. 避免冷启动App 启动方式一般有 3 种:
  •  ColdStart ——冷启动:

此种方式最为耗时,一般是因为进程被干掉,系统需要重新 fork 进程进行一系列初始化。

  • WarmStart ——暖启动比 ColdStart 稍快,因为 app 的所有 Activities 还常驻在内存中,并没有被杀掉,所做的只是把 app 从后台提到前台来展示,并不需要重走初始化一系列行为,减少了对象初始化、布局加载等工作。但其行为表现与冷启动一致,是会 displays a blank screen 直到 App 渲染 activity。这个 blank screen 后面会解释。
  • LukeWarm Start——热启动

启动方式最快,类似于返回键退出应用又立即进入的那种行为。

优化方案:

既然冷启动那么慢,我们就在非用户主动 kill 进程或系统通知 kill 进程的其他情况下不再主动退出进程。那答案很简单了,就是在位于 Activity 栈底 activity 中 Hook 其返回键行为,保证用户点击返回键后不再退出 app。在我们 App 里位于我们栈底的一定是我们的 MainActivity,因为一系统行为都是由其向下衍生的。所以只需加入以下几句话:

[1499137997386_7440_1499137997106.png]
[1499137997386_7440_1499137997106.png]

moveTaskToBack:作用是不再 Finish 到此 Activity,仅仅是把它放到后台隐藏。类似于用户主动触发系统 Home 键的效果。在同是点击返回键优化前后的对比如下:

 优化前: 

[image.gif]
[image.gif]
       

优化后: 

[image.gif]
[image.gif]

若图 1 中时间久后进程回收后优化效果会更为明显。

3.WindowBackGround——脱下秒开的最后一层薄丝袜

经过上面一顿操作后,我发现然并卵!!!启动速度是提升了,但是 App 一点击还是会在桌面停顿一下。哇呀~很难受~细细思考了一下,一个 APP 启动无论如何都是会新 Fork 进程,难道就是这个问题导致其在桌面上停顿一会儿?那其他 app 又是怎么做到秒开的呢?在 AndroidDeveloper 的 Launch-Time Performance有这么一句话:

[1499138150883_8287_1499138150693.png]
[1499138150883_8287_1499138150693.png]

其实在创建 App 进程时,android 系统会为你立即显示一个 background window,然后再去创建 app 进程,当 app 完成 first draw 时,会立即由你的 MainActivity(即默认启动的 Activity)替换掉它。这里的 background window 就是上文 WarmStart 中提到的 blank screen。谜底到此解开所谓的秒开原来就是视觉欺骗。。。所以说有人给你说他只是仅仅是优化生命周期内初始化代码达到秒开都是扯淡。但不得不承认这样用户体验大大的提升了,一点击 launcher 就渲染好一个背景图片,给用户一种已经启动的感觉,前面做的一系列优化,不过为了让用户少看一会儿系统给渲染的 black window。

那为什么我们的 APP 会出现在系统桌面上停留一会,而不是渲染背景图呢?原来在项目创建时,系统会为 launcherActivity 默认了一个 LightTheme,这样就会导致 App 点击启动后会白屏一段时间然后展示自己的 Activity,为了解决白屏的问题把 theme 主题换成透明的就像下面

[1499138180734_1423_1499138180634.png]
[1499138180734_1423_1499138180634.png]

但其实这样虽然解决了白屏的问题,但是就会出现上文所说的,点击后停留在桌面一会儿,直至 MainActivity 渲染出来。这是大部分 App 的做法,但并不是最佳解决方案。

WindowBackground 最佳解决方案:

应该由 windowBackground 此属性作为你的品牌推广页或者 logo 页,如果你的 SplashActivity 完全不需要做任何初始化,只是希望有个闪屏页,完全可以由 windowBackground 来满足。

1.设置自定义带 windowBackground 的 Theme

[1499138280733_3313_1499138280435.png]
[1499138280733_3313_1499138280435.png]

前两行代码是设置 Theme 不透明并且默认渲染的背景图是我们必看影视闪屏页的图片。

  • windowBackground:关键,主要设置你想要的背景图或者是动态自绘的 drawable 皆行,这个视图会在你冷启动时渲染给用户过渡看。
  • windowFullscreen:全屏展示,免得顶部状态栏显现颜色不一致过于脱节和突兀

2. 为你的 launcher Activity 设置你的启动 Theme

[1499138288458_1657_1499138288209.png]
[1499138288458_1657_1499138288209.png]

3. 在 Launch Activity 启动后再把主题设置回自己的

AppTheme

[1499138295039_2835_1499138294762.png]
[1499138295039_2835_1499138294762.png]

此时你的 App 就能完成秒开了!

推荐测试工具

最终我们 App 从 launcher 点击到第一个 Activity 渲染完成总共需要时间是 888ms,很吉利!

[1499138411092_7793_1499138410764.png]
[1499138411092_7793_1499138410764.png]

Time to full Display

上图的那行日志是由系统打印出来的,意思就是渲染 Acitivity 所需要的时间,如果是第一个 Activity 启动时长也是算在内的。 在 Android4.4(API19)以上才会有此 Log 打出,在 verbose 级别搜索 Displayed 即可。不仅可以看自己的还可以看到其他 APP 的启动时长。

[1499138422577_4959_1499138422429.png]
[1499138422577_4959_1499138422429.png]

附带一张小米 4 上业界各大 App 的冷启动时长~仅供参考~

Hugo

执行时间打印神器,集成至项目后,只需在想要测试的地方加上注解即可:

[1499138437426_4220_1499138437073.png]
[1499138437426_4220_1499138437073.png]

可以是 class 级别,也可以是函数级别。如果注解是 Appication 类级别,还会把各大生命周期回调函数执行时间打印出来及其方便!GitHub 传送门

效果如下:

[1499138500137_4198_1499138499989.png]
[1499138500137_4198_1499138499989.png]

Stetho

这是非死不可出的调试神器,如果开发 android 这个没用过的话,你一定会觉得相见恨晚!GitHub 传送门

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • Android App 启动流程:
  • App 冷启动相关优化点
    • 1. 生命周期内减少耗时操作
      • 3.WindowBackGround——脱下秒开的最后一层薄丝袜
        • WindowBackground 最佳解决方案:
        • 推荐测试工具
        • Time to full Display
        • Hugo
        • Stetho
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档