Android如何更加有效的提升App的啓動速度

一 啓動App分爲兩種方式:

1 冷啓動:當直接從桌面上直接啓動,同時後臺進程中沒有該APP進程的緩存,這個時候系統就需要重新創建一個新的進程,並且分配各種資源。

2 熱啓動:該App在後臺有該進程的緩存(例:按back鍵、home鍵,應用雖然會退出,但是該應用的進程是依然會保留在後臺,可進入任務列表查看),這個時候啓動的進程就屬於熱啓動。熱啓動不需要重新分配進程,也不會執行Application,直接走的就是App要顯示的Activity中的代碼,這樣就速度快一些。

冷啓動會有哪些表現?

1 點擊桌面後短時間內沒有反應,沒有瞬間開打應用

2 點擊桌面圖標會顯示黑屏或者白屏,沒有及時呈現出頁面

二 應用的啓動流程

冷啓動流程:當點擊app的啓動圖標時,安卓系統會從Zygote進程中fork出一個新的進程分配給該應用,之後會依次創建和初始化Application類、創建MainActivity類、加載主題樣式Theme中的windowBackground等屬性設置給MainActivity、再inflate佈局、當onCreate/onStart/onResume方法都走完了後最後才進行contentView的measure/layout/draw顯示在界面上,所以直到這裏,應用的第一次啓動纔算完成,這時候我們看到的界面也就是所說的第一幀。

總結流程如下:啓動Application從構造方法開始 -->attachBaseContent() -->onCreate()之後啓動Activity-->啓動Acitivyt構造方法-->onCreate()->設置主題、背景等等屬性-->onStart()-->onResume()-->顯示裏面的View(測量、佈局、繪製,顯示到界面上)

三 如何減少應用的啓動時間的耗時?

1 不要在Application的構造方法、attachBaseContent()、onCreate()裏面進行初始化耗時操作。這是必然的。

2 應用的第一個顯示Activity、由於用戶只關心最後的顯示的這一幀,對我們的佈局的層次要求要減少,如果有自定義控件,注意測量、佈局、繪製的時間。不要在onCreate、onStart、onResume當中做耗時操作。

3 對於SharedPreference的初始化,因爲他初始化的時候是需要將數據全部讀取出來放到內存中。如果有大量的數據,是耗時的。如果啓動app就初始化,是消耗時間的。

優化:(1)可以儘可能減少SharedPreference文件數量 (2)像這樣的初始化最好放到線程裏面。(3)大數據放到Sqlite裏面

思考:

有很多開發者使用SplashActivity 作爲應用的第一頁,在這個頁面什麼都不幹,停留2秒鐘,就只顯示一張圖片

這樣App啓動的耗時主要是在:Application初始化,第一個顯示的界面Activity以及MainActivity的界面加載繪製時間。

但是要知道SplashActivity啓動之後,最終還是需要跳到MainActivity。MainActivity還是需要從頭開始加載佈局和數據。這樣當用戶關心的MainActivity還是不能很快的顯示給用戶。在SplashActivity顯示的2秒鐘,主要的MainActivity什麼都沒有做,我們要充分的利用每一段時間。即使有的讀者在這裏想到,在SplashActivity中加載數據,然後Intent將數據傳入到MainActivity中,但是MainActivity還是要繪製界面處理邏輯。

 

終極解決方案

可不可以再做一些更好的優化呢?

將MainActivity和SplashActivity和併爲一個Activity。

應用一進來就啓動MainActivity,將MainActivity作爲應用的第一個啓動的Activity,不再啓動SplashActivity,SplashActivity可以變成一個SplashFragment(SplashFragment成爲MainActivity中的一個Fragment),然後放一個FrameLayout幀佈局作爲MainActivity的根佈局,直接顯示SplashFragment界面。SplashFragment裏非常之簡單,就是實現一個圖片,啓動非常快。

當SplashFragment顯示完畢後再將它remove掉。在Splash顯示的2s友好時間內,MainActivity中已經進行網絡數據獲取,UI繪製,以及很多事情。這個時候再看到MainActivity就不必再去等待網絡數據返回了。

問題:SplashFragment和MainActivity中的內容佈局加載放到一起來做,這可能會影響應用的啓動時間。

解決:可以使用ViewStub延遲加載MainActivity中的View來達到減輕這個影響。

ViewStub的設計就是爲了防止MainActivity的啓動加載資源太耗時了,延遲進行加載,不影響啓動。但是ViewStub啓動加載也需要時間。但是我們可以等到SplashFragment顯示出來以後再講ViewStub渲染進來。

 

爲防止黑屏或者白屏,設置啓動頁面的theme。

<style name="LoadingThreme" parent="Threme.AppCompat.Light.NoActionBar">
    <item name="android:background">@drawable/loading_bg</item>
</style>

1 AndroidManifest.xml

<application
     <activity android:name=".MainActivity">
         <intent-filter>
             <action android:name="android.intent.action.MAIN" />

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

2 activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.startoptdemo.MainActivity">

   <!--MainActivity真正要顯示的內容,用ViewStub先暫時保留位置,並不渲染-->
    <ViewStub
        android:id="@+id/content_viewstub"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/activity_main_viewstub"/>

    <!--  放SplashFragment  -->
    <FrameLayout
        android:id="@+id/frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

 

activity_main_viewstub.xml就是MainActivity的一些佈局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:drawableTop="@mipmap/ic_launcher"
            android:gravity="center_horizontal"
            android:text="首頁" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:drawableTop="@mipmap/ic_launcher"
            android:gravity="center_horizontal"
            android:text="XX" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:drawableTop="@mipmap/ic_launcher"
            android:gravity="center_horizontal"
            android:text="我的" />
    </LinearLayout>

</LinearLayout>

3 SplashFragment 繼承了Fragment。

public class SplashFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        //顯示啓動App所見到的第一頁
        return inflater.inflate(R.layout.fragment_splash, container, false);
    }
}

4 MainActivity類中:

onCreate方法一進來就加載顯示SplashFragment,然後當MainActivity的窗體加載完畢後,在進行耗時的邏輯處理。這樣既能快速的顯示Splash界面,與此同時又加載了MainActivity的耗時的操作。節省了時間。

package com.example.startoptdemo;

import android.content.Context;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ViewStub;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {

    private Handler mHandler = new Handler();
    private ViewStub vsMain;

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

        //第一步就將SplashFragment加載進來顯示
        final SplashFragment splashFragment = new SplashFragment();
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.replace(R.id.frame, splashFragment);
        transaction.commit();

        vsMain = findViewById(R.id.content_main_viewstub);

        //判斷當前窗體加載完畢,等splash界面加載出來後再做耗時操作
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                //當窗體加載完畢的時候,立馬再加載MainActivity中的真正的佈局
                //將viewstub加載進來,在這裏可以進行MainActivity中的邏輯
                vsMain.inflate();
                //開啓延遲加載,目的是讓Fragment2秒後消失。這裏只是演示。
                mHandler.postDelayed(new DelayRunnable(MainActivity.this, splashFragment), 2000);
            }
        });


    }

    static class DelayRunnable implements Runnable {
        private WeakReference<Context> contextRef;
        private WeakReference<SplashFragment> fragmentRef;
        public DelayRunnable(Context context, SplashFragment f) {
            contextRef = new WeakReference<>(context);
            fragmentRef = new WeakReference<>(f);
        }
        @Override
        public void run() {
            //移除fragment
            if (contextRef != null) {
                SplashFragment splashFragment = fragmentRef.get();
                if (splashFragment == null) {
                    return;
                }
                FragmentActivity activity = (FragmentActivity) contextRef.get();
                FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
                transaction.remove(fragmentRef.get());
                transaction.commit();
            }
        }
    }
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章