Android性能优化篇之程序启动时间性能优化

image

引言

1. Android性能优化篇之内存优化--内存泄漏

2.Android性能优化篇之内存优化--内存优化分析工具

3.Android性能优化篇之UI渲染性能优化

4.Android性能优化篇之计算性能优化

5.Android性能优化篇之电量优化(1)——电量消耗分析

6.Android性能优化篇之电量优化(2)

7.Android性能优化篇之网络优化

8.Android性能优化篇之Bitmap优化

9.Android性能优化篇之图片压缩优化

10.Android性能优化篇之多线程并发优化

11.Android性能优化篇之数据传输效率优化

12.Android性能优化篇之程序启动时间性能优化

13.Android性能优化篇之安装包性能优化

14.Android性能优化篇之服务优化

介绍

程序的启动速度是用户体验中很重要的一部分。启动时间越短,用户会耐心的使用你们公司的应用,如果时间过长,用户很有可能会切换到其他应用去了。我们从下面几个问题来开始分析和解决程序启动慢的问题。

  • APP应用有几种启动方式?
  • 启动方式有那些不同?
  • 启动时间消耗到哪里去了?
  • 有哪些工具可以查看启动过程中时间的消耗?
  • 针对启动过慢的优化建议有哪些?
1.APP应用有几种启动方式?

启动分为两种方式:

(1).冷启动

应用从桌面上启动,且后台没有进程的缓存,这是系统就需要新创建一个进程并且分配资源。

(2).热启动

app在后台有进程缓存

2.启动方式有那些不同?

对于冷启动来说,多了一个创建进程,这块也是我们优化的重点。热启动不需要重新分配进程,也不会Application了,直接走的就是app的入口Activity,这样就速度快很多.

3.启动时间消耗到哪里去了?

当用户点击桌面图标开始,系统会立即为这个APP创建独立的专属进程,然后显示启动窗口,直到APP在自己的进程里面完成了程序的创建以及主线程完成了Activity的初始化显示操作,再然后系统进程就会把启动窗口替换成APP的显示窗口。
我们来看下启动的流程描述:

创建APP进程-->显示启动窗口(displsy start window)-->Application从构造方法开始--->attachBaseContext()--->onCreate()
Activity构造方法--->onCreate()--->设置显示界面布局,设置主题、背景等等属性
--->[onStart()--->onResume()--->显示里面的view(测量、布局、绘制,显示到界面上)](窗口替换)
image2.png

流程中很多步骤是系统控制的,我们能够控制的和关注的点有以下几个:

(1).Application的onCreate,一般应用中通用主件和初始化都放在这里(耗时主因)
(2).Activity的onCreate,UI布局和渲染(耗时主因)
(3).显示启动窗口到窗口替换之间,会出现白屏的过度画面,体验太差(视主题而定,必须优化)

上面分析出来启动耗时的主因和启动白屏的点,这些都是我们需要解决的问题。在给出解决方案前,我们先来看下关于启动性能的评估工具有哪些?任何更好的定位问题的所在。

4.有哪些工具可以查看启动过程中时间的消耗?
(1).display time

从Android KitKat版本开始,Logcat中会输出从程序启动到某个Activity显示到画面上所花费的时间。这个方法比较适合测量程序的启动时间。


image1.png
(2).Traceview

TraceView 在[Android性能优化篇之内存优化--内存优化分析工具]中已经 讲过,这里就不分析了,我们来看下使用代码生成trace文件。

    Debug.startMethodTracing("perform");
    Debug.stopMethodTracing();
image3.png
  • 在下方的方法区点击"Real Time/Call", 按照方法每次调用耗时降序排.
  • 耗时超过500ms都是值得注意的.
(3).adb shell

使用命令行来启动app,同时进行时间测量。单位:毫秒

adb shell am start -W [PackageName]/[PackageName.MainActivity]
adb shell am start -W com.skcodestack.performanceoptimization/com.skcodestack.performanceoptimization.speed.SplashActivity

ThisTime: 165 指当前指定的MainActivity的启动时间
TotalTime: 165 整个应用的启动时间,Application+Activity的使用的时间。
WaitTime: 175 包括系统的影响时间---比较上面大。

5.针对启动过慢的优化建议有哪些?
(1).Application的onCreate 中优化

在Application初始化的地方做太多繁重的事情是可能导致严重启动性能问题,Application里面的初始化操作不结束,其他任意的程序操作都无法进行。

    @Override
    public void onCreate() {
        super.onCreate();
        CrashReport.initCrashReport(getApplicationContext(), BUGLY_APP_ID, false);
        SharedPreferences sharedPreferences =  getCacheSharedPreferences();
        //皮肤管理初始化
        SkinManager.getInstance().init(this);
        //初始化
        ToastUtils.init(this);
        //数据库管理
        DaoManager.init(this);
    }
Application中初始化优化的方案有两种:
  • 不需要立刻初始化的,延迟加载
  • 需要初始化的,开启线程初始化
延迟加载

当我们想要使用时,在进行初始化

    static SharedPreferences mSp = null;
    public static synchronized  SharedPreferences getAppSharedPreFerences(Context context){
        if(mSp == null){
            mSp = context.getApplicationContext().getSharedPreferences(NAME,MODE_PRIVATE);
        }
        return mSp;
    }
开启线程初始化

我们这里使用IntentService

    public class InitIntentService extends IntentService {
    // TODO: Rename actions, choose action names that describe tasks that this
    // IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
    private static final String ACTION_FOO = "com.skcodestack.performanceoptimization.speed.action.FOO";
    public InitIntentService() {
        super("InitIntentService");
    }
    /**
     * Starts this service to perform action Foo with the given parameters. If
     * the service is already performing a task this action will be queued.
     *
     * @see IntentService
     */
    // TODO: Customize helper method
    public static void startActionFoo(Context context) {
        Intent intent = new Intent(context, InitIntentService.class);
        intent.setAction(ACTION_FOO);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_FOO.equals(action)) {
                handleActionFoo();
            } 
        }
    }

    /**
     * Handle action Foo in the provided background thread with the provided
     * parameters.
     */
    private void handleActionFoo() {
        //初始化 app 
        ....        
    }
}

    @Override
    public void onCreate() {
        super.onCreate();
        InitIntentService.startActionFoo(this);
    }
(2).Activity的onCreate中优化

提升Activity的创建速度是优化APP启动速度的首要关注目标。从桌面点击APP图标启动应用开始,程序会显示一个启动窗口等待Activity的创建加载完毕再进行显示。在Activity的创建加载过程中,会执行很多的操作,例如设置页面的主题,初始化页面的布局,加载图片,获取网络数据,读写Preference等等。任何一个环节出现性能问题都可能导致画面不能及时显示,影响了程序的启动速度。

有几条优化建议:
  • 优化布局耗时:一个布局层级越深,里面包含需要加载的元素越多,就会耗费更多的初始化时间。关于布局性能的优化,在渲染优化的文章中已经讲过。
  • 异步延迟加载:一开始只初始化最需要的布局,异步加载图片,非立即需要的组件可以做延迟加载。
  • 预加载:在前一个显示时预加载下一个显示页面中需要用到的数据(常常用到Spalsh调整主页面时使用)
(3).闪屏优化

在应用启动时,屏幕会显示一个空白的窗口(颜色基于主题), 直至activity渲染完毕.

第一种,自定义一张图片,把这张图片通过设置主题的方式显示为启动闪屏,代码执行到主页面的onCreate的时候设置为程序正常的主题。
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/white"></item>
    <item>
        <bitmap android:src="@drawable/timg"
            android:gravity="center"/>
    </item>
</layer-list>

<style name="SplashTheme" parent="AppTheme">
    <!-- Customize your theme here. -->
    <item name="android:windowBackground">@drawable/splash_background</item>
</style>

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setTheme(R.style.SplashTheme);  
        startActivity(new Intent(this,SpeedMainActivity.class));
        finish();
    }
第二种,就是上面提到的预加载数据+延时加载布局,我们将Splash页面和主页面合并成一个页面,先显示Splash(尽量简单),然后等异步加载的主页数据完成(设定欢迎界面的友好时间),就显示主页面。

下面我们一起来看下代码:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_splash_and_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ViewStub
        android:id="@+id/content_viewstub"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/activity_speed_main"
        />
    <FrameLayout
        android:id="@+id/frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>
</RelativeLayout>

MainActivity:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash_and_main);

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        final SplashFragment splashFragment = new SplashFragment();
        FragmentTransaction fragmentTransaction = transaction.replace(R.id.frame, splashFragment);
        fragmentTransaction.commit();

        final ViewStub viewStub = (ViewStub) findViewById(R.id.content_viewstub);
        //当窗体加载完毕时,执行
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                //加载布局
                viewStub.inflate();
                //开始延迟加载
                mHandler.postDelayed(new DelayRunnable(SplashAndMainActivity.this,splashFragment),2000);
            }
        });
    }
    /**
     * 延迟操作
     */
    private   static  class DelayRunnable implements Runnable{
        private WeakReference<Context> mContextRef;
        private WeakReference<SplashFragment> mFragmentRef;

        public DelayRunnable(Context context,SplashFragment fragment) {
            this.mContextRef = new WeakReference<Context>(context);
            this.mFragmentRef = new WeakReference<SplashFragment>(fragment);
        }

        @Override
        public void run() {
            if(mContextRef != null){
                FragmentActivity activity = (FragmentActivity) mContextRef.get();
                if(mFragmentRef != null && activity != null && mFragmentRef.get() != null){
                    SplashFragment fragment = mFragmentRef.get();
                    FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
                    transaction.remove(fragment);
                    transaction.commit();
                }   
            }
        }
    }
下面我们主要讲解预加载的实现:
总结:Application的onCreate中不要做耗时操作,首屏Activity尽量简化。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容