启动优化三问—字节跳动真题

2020-10-29 16:40:44 浏览数 (1)

之前大家应该看过我写的启动流程分析了吧,那篇文章里我说过分析源码的目的一直都不是为了学知识而学,而是理解了这些基础,我们才能更好的解决问题。所以今天就来看看通过分析app启动流程,我们该怎么具体进行启动优化。

  • App启动流程中我们能进行优化的地方有哪些?
  • 具体有哪些优化方法?
  • 分析启动耗时的方法

App启动流程中我们能进行优化的地方有哪些?

  • Application的attach方法,MultiDexApplication会在方法里面会去执行MultiDex逻辑。
  • Application的onCreate方法,大量三方库的初始化都在这里进行,
  • Activity的onCreate、onStart、onResume方法。

具体有哪些优化方法?

  • 障眼法之闪屏页

为了消除启动时的白屏/黑屏,可以通过设置android:windowBackground,让人感觉一点击icon就启动完毕了的感觉。

代码语言:javascript复制
        <activity android:name=".ui.activity.启动activity"
            android:theme="@style/MyAppTheme"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

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

  • 预创建Activity

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

  • 第三方库懒加载

很多第三方开源库都说在Application中进行初始化,所以可以把一些不是需要启动就初始化的三方库的初始化放到后面,按需初始化,这样就能让Application变得更轻。

  • WebView启动优化

webview第一次启动会非常耗时,具体优化方法可以看我之前的文章,关于webview的优化。

  • 线程优化

线程是程序运行的基本单位,线程的频繁创建是耗性能的,所以大家应该都会用线程池。单个cpu情况下,即使是开多个线程,同时也只有一个线程可以工作,所以线程池的大小要根据cpu个数来确定。

  • MultiDex 优化

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

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

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

1、在Application的attachBaseContext方法里,启动另一个进程的LoadDexActivity去异步执行MultiDex逻辑,显示Loading。

2、然后主进程Application进入while循环,不断检测MultiDex操作是否完成 3、MultiDex执行完之后主进程Application继续走,ContentProvider初始化和Application onCreate方法,也就是执行主进程正常的逻辑。

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

分析启动耗时的方法

  • Systrace 函数插桩

也就是通过在方法的入口和出口加入统计代码,从而统计方法耗时

代码语言:javascript复制
class Trace{
    public static void i(String tag){
        android.os.Trace.beginSection(tag);
    }

    public static void o(){
        android.os.Trace.endSection();
    }
}


void test(){
    Trace.i("test");
    System.out.println("doSomething");
    Trace.o();
}

* BlockCanary

BlockCanary 可以监听主线程耗时的方法,就是在主线程消息循环打出日志的地入手, 当一个消息操作时间超过阀值后, 记录系统各种资源的状态, 并展示出来。所以我们将阈值设置低一点,这样的话如果一个方法执行时间超过200毫秒,获取堆栈信息。

而记录时间的方法我们之前也说过,就是通过looper()方法中循环去从MessageQueue中去取msg的时候,在dispatchMessage方法前后会有logging日志打印,所以只需要自定义一个Printer,重写println(String x)方法即可实现耗时统计了。

参考文章

https://juejin.im/post/6844903958113157128


Android开发者们,快来关注公| 众 |号【码上积木】,每天三问面试题,并详细剖析,助你成为offer收割机。 相信积累的力量。

0 人点赞