启动优化

2020-12-25 10:03:59 浏览数 (1)

前言

之前有读者说到关于Android性能优化,性能优化包括很多方面,比如:

启动优化、布局优化、内存优化、卡顿优化、网络优化、数据库优化、内存泄漏优化、包体积优化等等。

后面我们会依次聊聊,今天就从启动优化聊起

冷启动、温启动、热启动

首先了解下启动的这三个概念,也是面试常被问到的:

  • 冷启动。冷启动指的是该应用程序在此之前没有被创建,发生在应用程序首次启动或者自上次被终止后的再次启动。简单的说就是app进程还没有,需要创建app的进程并启动app。

比如开机后,点击屏幕的app图标启动应用。

冷启动的过程主要分为两步:

1)系统任务。加载并启动应用程序;显示应用程序的空白启动窗口;创建APP进程

2)APP进程任务。启动主线程;创建Activity;加载布局;屏幕布局;绘制屏幕

其实这不就是APP的启动流程嘛?所以冷启动是会完整走完一个启动流程的,从系统到进程。

  • 温启动。温启动指的是App进程存在,但Activity可能因为内存不足被回收,这时候启动App不需要重新创建进程,只需要执行APP进程中的一些任务,比如创建Activity。

比如返回Home后,又继续使用其他的APP,时间久了或者打开的应用多了,之前应用的Activity有可能被回收了,但是进程还在。

所以温启动过程相当于执行了冷启动的第二过程,也就是APP进程任务,需要重新启动线程,Activity等。

  • 热启动。热启动就是指App进程存在,并且Activity对象仍然存在内存中没有被回收。

比如app被切到后台,再次启动app的过程。

所以热启动的开销最少,这个过程只会把Activity从后台展示到前台,无需初始化,布局绘制等工作。

优化点

三种启动方式中,冷启动经历的时间最长,也是走完了最完整的启动流程,所以我们再次分析下冷启动的启动流程,看看有哪些可以优化的点:

  • Launcher startActivity
  • AMS startActivity
  • Zygote fork 进程
  • ActivityThread main()
  • ActivityThread attach
  • handleBindApplication
  • attachBaseContext
  • Application attach
  • installContentProviders
  • Application onCreate
  • Looper.loop
  • Activity onCreate,onResume

纵观整个流程,其实我们能动的地方不多,无非就是Application的attach,onCreate方法,Activity的onCreate,onResume方法,这些方法也就是我们的优化点。

优化方案

1)消除启动时的白屏/黑屏

App启动的时候会有一个白屏/黑屏时间,我们可以通过设置windowBackground属性来给启动的Activity提供一个drawable,这样就给用户一个快递启动的假象了。

代码语言:javascript复制
<activity ...
android:theme="@style/MyAppTheme" />

<style name="MyAppTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@drawable/logo</item> 
</style>

2)第三方库懒加载/异步加载

ApplicationonCreate方法中,总会有很多初始化操作,比如友盟,数据库,网络请求库,广告SDK等等。

对此,我们能做的有哪些呢?

  • 异步加载。有些库不需要在主线程进行初始化,那么我们就可以在子线程中进行初始化,进行异步加载。
  • 延迟加载。有些库不必要一开始就初始化,我们可以按需初始化,将一些库放到用它的时候再初始化,或者放到启动页去进行初始化。

所以需要我们对这些初始化操作进行分析,哪些需要在主线程进行,哪些可以延迟加载,哪些初始化任务有先后关系等等。这里涉及到一个启动器的概念,启动器的用处就是可以充分利用CPU多核,自动梳理任务顺序。有空的朋友可以去了解下。

这里还需要注意一点就是线程的使用:

  • 即不要频繁创建线程,线程的频繁创建是耗性能的,所以需要用到线程池去执行异步任务。

3)预创建Activity

Java中的对象第一次创建的时候,java虚拟机首先检查类对应的Class对象是否已经加载。如果没有加载,jvm会根据类名查找.class文件,将其Class对象载入。同一个类第二次new的时候就不需要加载类对象,而是直接实例化,创建时间就缩短了。

今日头条中就有这种做法,先创建一个Activity的实例。

4)预加载数据

在我们的启动页或者主页可以将一些要用到的数据保存到内存或者数据库,那么其他页面要用到这些数据的时候就可以直接使用并显示了。

5)Multidex预加载优化

由于65536方法限制,所以一般class文件要生成多个dex文件,Android5.0以下,ClassLoader加载类的时候只会从class.dex(主dex)里加载,所以要执行MultiDex.install(context)方法才能正常读取所有的dex类。

而这个install方法就是耗时大户,会解压apk,遍历dex文件,压缩dex、将dex文件通过反射转换成DexFile对象、反射替换数组。

这里需要的方案就是今日头条方案:

  • 在Application的attachBaseContext方法里,启动另一个进程的LoadDexActivity去异步执行MultiDex逻辑,显示Loading。
  • 然后主进程Application进入while循环,不断检测MultiDex操作是否完成 ,MultiDex执行完之后主进程Application继续执行ContentProvider初始化和Application onCreate方法,也就是主进程正常的逻辑。

所以重点就是单开进程去执行MultiDex逻辑,这样就不影响APP的启动了。

当然,这仅仅针对5.0以下加载Multidex情况,5.0以上默认使用ART加载类,安装时候就已经转换dex文件为oat文件了,所以无需优化Multidex情况了。

6)Webview启动优化

如果我们的主页涉及到Webview,那我们还要处理WebView的优化。因为Webview的创建很耗时,所以我们采取以下方案进行Webview的优化:

  • 预先创建WebView,提前将其内核初始化。
  • 使用WebView缓存池,从缓存池中拿到Webview实例。
  • 本地提供静态页面资源。

7)避免布局嵌套

如果启动页和主页的布局比较复杂,也会影响我们的启动时间,所以注意我们的布局,多用merge,include,constraintlayout等,特别是多层嵌套问题。

总结

最后再和大家回顾下今天说到的启动优化方案:

  • 消除启动时的白屏/黑屏。windowBackground。
  • 第三方库懒加载/异步加载。线程池,启动器。
  • 预创建Activity。对象预创建。
  • 预加载数据。
  • Multidex预加载优化。5.0以下多dex情况。
  • Webview启动优化。预创建,缓存池,静态资源。
  • 避免布局嵌套。多层嵌套。

为了方便记忆,我再整理成以下三类,分别是Application、Activity、UI

  • Application 三方库,Multidex。
  • Activity 预创建类,预加载数据。
  • UI方面 windowBackground,布局嵌套,webview。

参考

https://juejin.cn/post/6844903641032163336 https://juejin.cn/post/6844903958113157128

感谢大家的阅读,有一起学习的小伙伴可以关注下公众号—码上积木❤️ 每日三问知识点/面试题,积少成多。

0 人点赞