启动优化分为三类: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 启动速度变慢的时候,只要从这三个方面分析就够啦!