Skip to content

Latest commit

 

History

History
250 lines (170 loc) · 10.2 KB

App应用启动分析与优化.md

File metadata and controls

250 lines (170 loc) · 10.2 KB

源码分析相关面试题

Activity相关面试题

与XMPP相关面试题

与性能优化相关面试题

与登录相关面试题

与开发相关面试题

与人事相关面试题

本文配套视频

Android性能优化之App应用启动分析与优化

App启动方式

通常来说, 一个App启动也会分如下二中不同的状态:

.1)冷启动

当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动。冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。

2.)热启动

当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动。热启动因为会从已有的进程中来启动,所以热启动就不会走Application这步了,而是直接走MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个MainActivity就行了,而不必创建和初始化Application,因为一个应用从新进程的创建到进程的销毁,Application只会初始化一次。

启动时间的测量

关于Activity启动时间的定义

对于Activity来说,启动时,首先执行的是onCreate()、onStart()、onResume()这些生命周期函数,但即使这些生命周期方法回调结束了,应用也不算已经完全启动,还需要等View树全部构建完毕,一般认为,setContentView中的View全部显示结束了,算作是应用完全启动了。

Display Time

从API19之后,Android在系统Log中增加了Display的Log信息,通过过滤ActivityManager以及Display这两个关键字,可以找到系统中的这个Log:

$ adb logcat | grep “ActivityManager”
ActivityManager: Displayed com.example.launcher/.LauncherActivity: +999ms

抓到的Log如图所示:

img

那么这个时间,实际上是Activity启动,到Layout全部显示的过程,但是要注意,这里并不包括数据的加载,因为很多App在加载时会使用懒加载模式,即数据拉取后,再刷新默认的UI。

基于上面的启动流程我们尽量做到如下几点

  1. Application的创建过程中尽量少的进行耗时操作

  2. 首屏Activity的渲染

当前冷启动效果:

img

Application

Application是程序的主入口,特别是很多第三方SDK都会需要在Application的onCreate里面做很多初始化操作,一般来说我们可以将这些初始化放在一个单独的线程中处理, 为了方便今后管理,

优化的方法,无非是通过以下几个方面:

1)异步初始化 2)后台任务 3)界面预加载

异步初始化

这个很简单,就是让App在onCreate里面尽可能的少做事情,而利用手机的多核特性,尽可能的利用多线程,例如一些第三方框架的初始化,如果能放线程,就尽量的放入线程中,最简单的,你可以直接new Thread(),当然,你也可以通过公共的线程池来进行异步的初始化工作,这个是最能够压缩启动时间的方式

后台任务

因为这个App一般会集成很多三方SDK等服务, 所以Application的onCreate有很多第三方平台的初始化工作…

Application代码如下:

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        LitePal.initialize(this.getApplicationContext());
    }
}

使用IntentService不同于Service, 它是工作在后台线程的.

IntentService代码如下:

public class InitializeService extends IntentService {
    private static final String ACTION_INIT_WHEN_APP_CREATE = "com.maweiqi";
    public InitializeService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_INIT_WHEN_APP_CREATE.equals(action)) {
                performInit();
            }
        }
    }
    private void performInit() {
        LitePal.initialize(this.getApplicationContext());
    }
    public static void start(Context context) {
        Intent intent = new Intent(context, InitializeService.class);
        intent.setAction(ACTION_INIT_WHEN_APP_CREATE);
        context.startService(intent);
    }
}

MyApp的onCreate改成:

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        InitializeService.start(this);
    }
}

最终的效果

img

界面预加载

当系统加载一个Activity的时候,onCreate()是一个耗时过程,那么在这个过程中,系统为了让用户能有一个比较好的体验,实际上会先绘制一些初始界面,类似于PlaceHolder。

系统首先会读取当前Activity的Theme,然后根据Theme中的配置来绘制,当Activity加载完毕后,才会替换为真正的界面。所以,Google官方提供的解决方案,就是通过android:windowBackground属性,来进行加载前的配置,同时,这里不仅可以配置颜色,还能配置图片,例如,我们可以使用一个layer-list来作为android:windowBackground要显示的图:

在drawable文件夹下面 , 做一个logo_splash的背景: start_window.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 底层白色 -->
    <item android:drawable="@color/white" />

    <!-- 顶层Logo居中 -->
    <item>
        <bitmap
            android:gravity="center"
            android:src="@drawable/ic_github" />
    </item>
</layer-list>

在style里面弄一个主题:

<style name="StartStyle" parent="AppTheme">
        <item name="android:windowBackground">@drawable/start_window</item>
    </style>

在清单文件里面给Activity指定需要预加载的Style:

<activity android:name=".MainActivity" android:theme="@style/StartStyle">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

activity代码如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }
}

首屏Activity的渲染

Android系统每隔16ms会发出VSYNC信号重绘我们的界面(Activity). 为什么是16ms, 因为Android设定的刷新率是60FPS(Frame Per Second), 也就是每秒60帧的刷新率, 约合16ms刷新一次.就像是这样的:

img

这就意味着, 我们需要在16ms内完成下一次要刷新的界面的相关运算, 以便界面刷新更新. 然而, 如果我们无法在16ms内完成此次运算会怎样呢?

例如, 假设我们更新屏幕的背景图片, 需要24ms来做这次运算. 当系统在第一个16ms时刷新界面, 然而我们的运算还没有结束, 无法绘出图片. 当系统隔16ms再发一次VSYNC信息重绘界面时, 用户才会看到更新后的图片. 也就是说用户是32ms后看到了这次刷新(注意, 并不是24ms). 这就是传说中的丢帧(dropped frame):

img

丢帧给用户的感觉就是卡顿, 而且如果运算过于复杂, 丢帧会更多, 导致界面常常处于停滞状态, 卡到爆.

  • 欢迎关注微信公众号,长期推荐技术文章和技术视频

微信公众号名称:Android干货程序员

img