啓動優化分爲三類:html
App 首次啓動或系統將 App 進程殺死以後啓動。python
此時,將經歷下面的流程:android
在以上步驟中,開發者能夠干涉的步驟有:shell
另外,相比於溫啓動和熱啓動,冷啓動的過程更復雜,且經歷的是完整的步驟,因此只要處理好了冷啓動,溫啓動和熱啓動天然而然也變好了。瀏覽器
App 啓動以後,用戶將 App 切至後臺,過了一會,再切回來,系統將 App 中正在顯示的 Activity 殺死,但 App 所在進程依然存在。bash
此時,主要經歷 Activity 的生命週期函數調用:架構
App 啓動以後,用戶將 App 切至後臺,過了一會,再切回來,App 所在的進程依然存在,App 正在顯示的 Activity 未被殺死。app
此時,主要經歷 Activity 的生命週期函數調用:異步
由前面的分析可知,在 App 啓動過程當中,開發者能夠干涉的步驟有:ide
接下來,我們就從這些方面講解如何進行啓動優化?
啓動時間檢測的方法有三種:
Google Play Console 會提供 App 的啓動時間,但這個功能對於廣大的中國開發者實際上並無太大的意義,因此,不贅述。
默認狀況下,應用啓動以後,Android Studio 會顯示該 App 當前界面啓動時長和啓動總時長。此處有兩個啓動時長的主要緣由是:
有時候,當前界面並非主 Activiy 對應的界面,而是通過跳轉以後的界面。所以,此時 Android Studio 就會顯示「當前界面啓動時長」和「啓動總時長」。若是當前界面就是主 Activity 對應的界面,那 Android Studio 只會顯示一個時長,由於此時「當前界面啓動時長」和「啓動總時長」同樣。
//1. 當前界面就是主 Activity 對應的界面
2019-09-12 12:10:41.491 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.MainActivity: +1s121ms
//2. 當前界面並非主 Activiy 對應的界面,而是通過跳轉以後的界面
2019-09-12 12:16:13.385 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.second.SecondActivity: +296ms (total +2s224ms)
複製代碼
Android Studio 默認提供的顯示當前 App 啓動時長的功能對當前設備的上的全部 App 均起做用,因此,若是開發者想要對比竟品和自家 App 啓動時長的差別的話,就能夠經過此方法。
另外,須要注意的是,Android Studio 默認提供的 App 「啓動時長展現功能」日誌對應的日誌類型是「Verbose」,Tag 是「Displayed」。
自定義 Application,在自定義 Application 中重寫 attachBaseContext() 方法,並在其中記錄起始時間,在 MainActivity 中重寫 onWindowFocusChanged() 方法,並在其中記錄結束時間。
//1. 自定義 Application
public class MyApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
LogUtils.recordStartTime();
}
}
//2. MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
LogUtils.recordEndTime();
}
}
//3. 起始時間(手動埋點)
2019-09-12 14:01:53.668 12323-12323/com.smart.a15_start_up_optimization E/Displayed: 486
//4. Android Studio 默認提供
//系統計算的起始時間比開發者手動埋點計算的時間長的主要緣由是系統計算的啓動時間包括:
//- 加載、啓動 App
//- 展現空白 Window
//- 建立 App 進程
//而開發者手動埋點只包括:
//- 建立 App 進程
2019-09-12 14:01:53.745 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.MainActivity: +885ms
複製代碼
除了前面的兩種方法以外,咱們還能經過 ADB 獲取指定 Activity 的啓動時長。
//1. 語法
adb shell am start -S -W package/activity name
//2. 示例
adb shell am start -S -W com.smart.a15_start_up_optimization/com.smart.a15_start_up_optimization.MainActivity
Stopping: com.smart.a15_start_up_optimization
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.smart.a15_start_up_optimization/.MainActivity }
Status: ok
Activity: com.smart.a15_start_up_optimization/.MainActivity
ThisTime: 479
TotalTime: 479
WaitTime: 505
Complete
//3. Log
//3.1 開發者手動埋點
2019-09-12 16:01:46.069 5315-5315/com.smart.a15_start_up_optimization E/Displayed: 334
//3.2 Android Studio 默認提供
2019-09-12 16:01:46.101 1510-1536/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.MainActivity: +479ms
複製代碼
ThisTime:啓動當前 Activity 所用時長。App 啓動時,當 App 中顯示的界面並非主 Activiy 對應的界面,而是通過跳轉以後的界面時,這個時長(ThisTime)就是當前界面對應的 Activity 的啓動所用時長,此時它將跟 TotalTime 不一樣。App 啓動時,當 App 中顯示的界面是主 Activiy 對應的界面時,這個時長就是主 Activity 啓動所用時長,此時它將跟 TotalTime 相同。
//1. 在 MainActivity 中直接啓動 SecondActivity
//1.1 ADB
adb shell am start -S -W com.smart.a15_start_up_optimization/com.smart.a15_start_up_optimization.MainActivity
Stopping: com.smart.a15_start_up_optimization
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.smart.a15_start_up_optimization/.MainActivity }
Status: ok
Activity: com.smart.a15_start_up_optimization/.second.SecondActivity
ThisTime: 170 //SecondActivity 啓動時長
TotalTime: 818 //從加載、啓動 App 直至 SecondActivity 啓動所用時長
WaitTime: 548 //啓動主 Activity 所用時長
Complete
zhangjihuidembp:MyApplication2019CustomView zhangjianhui$
//1.2 Log Android Studio 默認提供
2019-09-12 16:18:36.757 1510-1536/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.second.SecondActivity: +170ms (total +818ms)
複製代碼
TotalTime:啓動 App 中第一個能與用戶交互的 Activity 所用時長。App 啓動時,當 App 中顯示的界面並非主 Activiy 對應的界面,而是通過跳轉以後的界面時,這個時長(TotalTime)將跟 ThisTime 不一樣。App 啓動時,當 App 中顯示的界面是主 Activiy 對應的界面時,這個時長就是主 Activity 啓動所用時長,此時它將跟 ThisTime 相同。
WaitTime:啓動第一個 Activity 等待時長,也就是從加載、啓動 App 到最終顯示主 Activity 所用時長。所以,App 啓動時,當 App 中顯示的界面並非主 Activiy 對應的界面,而是通過跳轉以後的界面時,三個時長的關係是:
TotalTime > WaitTime > ThisTime
App 啓動時,當 App 中顯示的界面是主 Activiy 對應的界面時,這個時長就是主 Activity 啓動所用時長,此時它將跟 ThisTime 相同。
WaitTime > Total = ThisTime
Android Studio 提供了兩種分析 App 啓動時間的分析工具:
舉個例子,假如如今想查看 SecondActivity 的 onCreate() 方法執行狀況:
//1. SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//1. 開始追蹤
Debug.startMethodTracing(Constants.TRACE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
showDeviceMemory();
//2. 結束追蹤
Debug.stopMethodTracing();
}
private void showDeviceMemory(){
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int memory = activityManager.getMemoryClass();
int largeMemory = activityManager.getLargeMemoryClass();
Log.e(Constants.TAG, "Memory: " + memory + " LargeMemory: " + largeMemory);
int runtimeTotalMemory = (int)(Runtime.getRuntime().totalMemory() / (1024 * 1024));
int runtimeFreeMemory = (int)(Runtime.getRuntime().freeMemory() / (1024 * 1024));
int runtimeMaxMemory = (int)(Runtime.getRuntime().maxMemory() / (1024 * 1024));
Log.e(Constants.TAG, "Runtime TotalMemory: " + runtimeTotalMemory +
" Runtime FreeMemory: " + runtimeFreeMemory +
" Runtime MaxMemory: " + runtimeMaxMemory);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
複製代碼
Trace 路徑:
Trace 解析:
舉個例子,假如如今想查看 SixActivity 的 setContentView() 方法執行狀況:
//1. SixActivity
public class SixActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//1. 開始追蹤
TraceCompat.beginSection("SixActivity");
setContentView(R.layout.activity_six);
//2. 結束追蹤
TraceCompat.endSection();
}
}
複製代碼
Systrace 解析:
//1. window_background
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:opacity="opaque">
<!-- The background color, preferably the same as your normal theme -->
<item android:drawable="@android:color/white" />
<!-- Your product logo - 144dp color version of your app icon -->
<item>
<bitmap
android:gravity="center"
android:src="@drawable/bird_woodpecker" />
</item>
</layer-list>
//2. WindowBackgroundTheme
<style name="WindowBackgroundTheme" parent="@android:style/Theme.NoTitleBar.Fullscreen">
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/window_background</item>
</style>
//3. 在 AndroidManifest 文件中應用 Theme
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.smart.a15_start_up_optimization">
<application
android:name=".framework.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".third.ThirdActivity"
android:label="@string/third"
android:theme="@style/WindowBackgroundTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
//4. 在 Activity 中恢復實際的 Theme
public class ThirdActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.AppTheme);
setContentView(R.layout.activity_third);
}
}
複製代碼
自定義 Application 啓動時,可能存在的問題:
處理方法:
舉個例子:
//1. 自定義 Application,在主線程執行耗時操做
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Debug.startMethodTracing(Constants.APPLICATION_TRACE);
initData();
Debug.stopMethodTracing();
}
private void initData(){
//1. 在主線程執行耗時操做
showDeviceMemory();
//2. 在自線程執行耗時操做
// new Thread(){
// @Override
// public void run() {
// showDeviceMemory();
// }
// }.start();
}
private void showDeviceMemory(){
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int memory = activityManager.getMemoryClass();
int largeMemory = activityManager.getLargeMemoryClass();
Log.e(Constants.TAG, "Memory: " + memory + " LargeMemory: " + largeMemory);
int runtimeTotalMemory = (int)(Runtime.getRuntime().totalMemory() / (1024 * 1024));
int runtimeFreeMemory = (int)(Runtime.getRuntime().freeMemory() / (1024 * 1024));
int runtimeMaxMemory = (int)(Runtime.getRuntime().maxMemory() / (1024 * 1024));
Log.e(Constants.TAG, "Runtime TotalMemory: " + runtimeTotalMemory +
" Runtime FreeMemory: " + runtimeFreeMemory +
" Runtime MaxMemory: " + runtimeMaxMemory);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//執行結果(在主線程執行耗時操做):
2019-09-13 13:51:10.576 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +3s264ms
2019-09-13 13:51:18.192 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +3s333ms
2019-09-13 13:51:24.713 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +3s319ms
複製代碼
//2. 自定義 Application,在子線程執行耗時操做
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Debug.startMethodTracing(Constants.APPLICATION_TRACE);
initData();
Debug.stopMethodTracing();
}
private void initData(){
//1. 在主線程執行耗時操做
// showDeviceMemory();
//2. 在子線程執行耗時操做
new Thread(){
@Override
public void run() {
showDeviceMemory();
}
}.start();
}
private void showDeviceMemory(){
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int memory = activityManager.getMemoryClass();
int largeMemory = activityManager.getLargeMemoryClass();
Log.e(Constants.TAG, "Memory: " + memory + " LargeMemory: " + largeMemory);
int runtimeTotalMemory = (int)(Runtime.getRuntime().totalMemory() / (1024 * 1024));
int runtimeFreeMemory = (int)(Runtime.getRuntime().freeMemory() / (1024 * 1024));
int runtimeMaxMemory = (int)(Runtime.getRuntime().maxMemory() / (1024 * 1024));
Log.e(Constants.TAG, "Runtime TotalMemory: " + runtimeTotalMemory +
" Runtime FreeMemory: " + runtimeFreeMemory +
" Runtime MaxMemory: " + runtimeMaxMemory);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//執行結果(在子線程執行耗時操做):
2019-09-13 13:53:41.625 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +1s390ms
2019-09-13 13:53:44.739 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +1s228ms
2019-09-13 13:53:47.906 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +1s237ms
複製代碼
//3. 自定義 Application,在線程池中執行耗時操做
public class MyApplication extends Application {
//獲取當前設備上的可用 CPU 數量
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//保證最多有四條線程、最少有兩條線程在後臺運行,以免 CPU 在後臺工做時飽和
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static ExecutorService executorService;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// ★ 1. 手動埋點起點
//終點在 MainActivity
LogUtils.recordStartTime();
}
@Override
public void onCreate() {
super.onCreate();
Debug.startMethodTracing(Constants.APPLICATION_TRACE);
initData();
Debug.stopMethodTracing();
}
private void initData(){
//1. 在主線程執行耗時操做
// showDeviceMemory();
//2. 在自線程執行耗時操做
// new Thread(){
// @Override
// public void run() {
// showDeviceMemory();
// }
// }.start();
//3. 自定義線程池,以便線程資源可被重複利用
executorService = Executors.newFixedThreadPool(CORE_POOL_SIZE);
executorService.submit(new Runnable() {
@Override
public void run() {
showDeviceMemory();
}
});
}
private void showDeviceMemory(){
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int memory = activityManager.getMemoryClass();
int largeMemory = activityManager.getLargeMemoryClass();
Log.e(Constants.TAG, "Memory: " + memory + " LargeMemory: " + largeMemory);
int runtimeTotalMemory = (int)(Runtime.getRuntime().totalMemory() / (1024 * 1024));
int runtimeFreeMemory = (int)(Runtime.getRuntime().freeMemory() / (1024 * 1024));
int runtimeMaxMemory = (int)(Runtime.getRuntime().maxMemory() / (1024 * 1024));
Log.e(Constants.TAG, "Runtime TotalMemory: " + runtimeTotalMemory +
" Runtime FreeMemory: " + runtimeFreeMemory +
" Runtime MaxMemory: " + runtimeMaxMemory);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//執行結果(在線程池中執行耗時操做):
2019-09-16 10:40:39.050 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +1s487ms
2019-09-16 10:40:43.604 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +1s246ms
2019-09-16 10:40:48.099 1865-1887/? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +1s377ms
複製代碼
主 Activity 啓動時,可能存在的問題:
處理方法:
舉個例子:
//1. 嵌套佈局實現
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingLeft="@dimen/item_height"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/item_height"
android:paddingBottom="@dimen/padding_medium">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatar"
android:layout_width="@dimen/padding_ninety_six"
android:layout_height="@dimen/padding_ninety_six"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/padding_ninety_six"
android:scaleType="centerCrop"
android:src="@drawable/bird_woodpecker"
app:civ_border_color="@color/grey_800"
app:civ_border_width="@dimen/padding_micro_x" />
<LinearLayout
android:id="@+id/login_username_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_large"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_username" />
<EditText
android:id="@+id/login_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/padding_medium"
android:background="@null"
android:hint="@string/login_username_hint"
android:inputType="text"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/grey_700"
android:textCursorDrawable="@drawable/common_edit_text_cursor"
android:textSize="@dimen/font_micro" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/padding_micro_xx"
android:layout_marginTop="@dimen/padding_small"
android:layout_marginBottom="@dimen/padding_small"
android:background="@color/grey_300" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_medium"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_password" />
<EditText
android:id="@+id/login_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/padding_medium"
android:background="@null"
android:hint="@string/login_password_hint"
android:inputType="textPassword"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/grey_700"
android:textCursorDrawable="@drawable/common_edit_text_cursor"
android:textSize="@dimen/font_micro" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/padding_micro_xx"
android:layout_marginTop="@dimen/padding_small"
android:layout_marginBottom="@dimen/padding_small"
android:background="@color/grey_300" />
<TextView
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/padding_medium"
android:layout_marginTop="@dimen/padding_large"
android:layout_marginRight="@dimen/padding_medium"
android:background="@drawable/ripple_login"
android:clickable="true"
android:elevation="@dimen/divider_height"
android:gravity="center"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/login"
android:textColor="@color/grey_700"
android:textSize="@dimen/font_small"
android:textStyle="bold" />
</LinearLayout>
複製代碼
//2. 未經嵌套實現
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/item_height"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/item_height"
android:paddingBottom="@dimen/padding_medium">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatar"
android:layout_width="@dimen/padding_ninety_six"
android:layout_height="@dimen/padding_ninety_six"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/padding_ninety_six"
android:scaleType="centerCrop"
android:src="@drawable/bird_woodpecker"
app:civ_border_color="@color/grey_800"
app:civ_border_width="@dimen/padding_micro_x" />
<TextView
android:id="@+id/login_username_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/avatar"
android:layout_marginTop="@dimen/padding_large"
android:text="@string/login_username" />
<EditText
android:id="@+id/login_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/avatar"
android:layout_marginLeft="@dimen/padding_medium"
android:layout_marginTop="@dimen/padding_large"
android:layout_toRightOf="@id/login_username_label"
android:background="@null"
android:hint="@string/login_username_hint"
android:inputType="text"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/grey_700"
android:textCursorDrawable="@drawable/common_edit_text_cursor"
android:textSize="@dimen/font_micro" />
<View
android:id="@+id/login_username_divider"
android:layout_width="match_parent"
android:layout_height="@dimen/padding_micro_xx"
android:layout_below="@id/login_username_label"
android:layout_marginTop="@dimen/padding_small"
android:layout_marginBottom="@dimen/padding_small"
android:background="@color/grey_300" />
<TextView
android:id="@+id/login_password_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/login_username_divider"
android:layout_marginTop="@dimen/padding_medium"
android:text="@string/login_password" />
<EditText
android:id="@+id/login_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/login_username_divider"
android:layout_marginLeft="@dimen/padding_medium"
android:layout_marginTop="@dimen/padding_medium"
android:layout_toRightOf="@id/login_password_label"
android:background="@null"
android:hint="@string/login_password_hint"
android:inputType="textPassword"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/grey_700"
android:textCursorDrawable="@drawable/common_edit_text_cursor"
android:textSize="@dimen/font_micro" />
<View
android:id="@+id/login_password_divider"
android:layout_width="match_parent"
android:layout_height="@dimen/padding_micro_xx"
android:layout_below="@id/login_password_label"
android:layout_marginTop="@dimen/padding_small"
android:layout_marginBottom="@dimen/padding_small"
android:background="@color/grey_300" />
<TextView
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/login_password_divider"
android:layout_marginLeft="@dimen/padding_medium"
android:layout_marginTop="@dimen/padding_large"
android:layout_marginRight="@dimen/padding_medium"
android:background="@drawable/ripple_login"
android:clickable="true"
android:elevation="@dimen/divider_height"
android:gravity="center"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/login"
android:textColor="@color/grey_700"
android:textSize="@dimen/font_small"
android:textStyle="bold" />
</RelativeLayout>
複製代碼
上面兩個 XML 佈局文件最終實現的效果是同樣的,惟一不一樣的是,前者嵌套層級相對較多,後者沒有嵌套層級。固然,這只是在簡單的佈局文件中,若是是在複雜的佈局文件中,這種優化的效果是顯而易見的。所以,當主 Activity 中的佈局嵌套層級較多時,App 的啓動時間將會受到影響,因此,減小布局文件的嵌套層級勢在必行。
App 的啓動速度是用戶對 App 的第一體驗,因此,啓動很重要。
在 App 的啓動過程當中,有三個地方開發者是能夠進行優化的:
所以,開發者進行啓動優化的大方向也就肯定了,因此,當檢測到 App 啓動速度變慢的時候,只要從這三個方面分析就夠啦!