美文网首页Android面试经验Android开发知识集性能优化
android面试题-App应用启动分析与优化

android面试题-App应用启动分析与优化

作者: 小怪兽打葫芦娃 | 来源:发表于2017-04-24 18:23 被阅读2917次

    Android程序员面试宝典

    自定义控件

    联网

    工具

    数据库

    源码分析相关面试题

    Activity相关面试题

    Service相关面试题

    与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如图所示:

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

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

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

    2. 首屏Activity的渲染

    当前冷启动效果:

    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);
        }
    }
    

    最终的效果

    界面预加载

    当系统加载一个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刷新一次.就像是这样的:

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

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

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

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

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

    相关文章

      网友评论

      • 8716d9135cf4:使用intentservice来初始化第三方的sdk,确实可以减少启动的时间,学习了
      • 5ea4beb37efa:前辈,我是小米工程师,想加您微信好友,我的微信是shaoxy1992,方便的话备注写下伟奇哈~ 嘿嘿
        小怪兽打葫芦娃:@邵翔宇 你直接扫我的微信号就行
      • 74025b8b0dd9:界面预加载也不能有效的避免丢帧吧,跟我们要加载的东西多少还有效率如何有关是这样吗楼主:blush:
        74025b8b0dd9:@open_android 好哒,受教了
        小怪兽打葫芦娃:@Yuyang0214 预加载的目的是给用户提供一个更好的用户体验,如果想避免丢帧,那么需要把布局尽量的简单化,具体的可以看前面几篇性能优化的文章。

      本文标题:android面试题-App应用启动分析与优化

      本文链接:https://www.haomeiwen.com/subject/digyzttx.html