1. 程式人生 > >Android面試題-與效能優化相關面試題六

Android面試題-與效能優化相關面試題六

本文配套視訊

原始碼分析相關面試題

與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):

丟幀給使用者的感覺就是卡頓, 而且如果運算過於複雜, 丟幀會更多, 導致介面常常處於停滯狀態, 卡到爆.

  • 歡迎關注微信公眾號,長期推薦技術文章和技術視訊