Android应用性能优化-启动加速

赵小温

最近在研究Android性能优化的相关内容,最早是为了解决应用内打开一个网页时跳转卡顿的问题,尝试引入第三方WebView组件,但是却引入了另一个问题,第三方WebView组件的初始化是放在Application中的,这就导致App的启动时间的延长。今天就从Application和Activity两方面来讲讲如何进行优化加速。

一.Application的加速 App的启动时间是用户点击app icon后到app的第一个界面呈现给用户所需的时间,缩短这段时间,将第一个界面快速展现给用户,可以大大提升用户的体验。 对于Application的优化,目前主要采用的有两种方式,一个是减少Application中onCreate方法的执行时间,一个是利用theme和Drawable加速第一个界面的展现。

1.减少onCreate方法的执行时间

用Android studio新建一个应用,我们会发现启动速度是挺快的,但是随着应用的复杂度增加,集成的第三方组件的越来越多,在onCreate中对第三方组件的初始化操作也越来越多,这是会明显发现App的启动出现卡顿,第一个界面呈现之前的白屏或黑屏时间增加,这都是由于onCreate执行时间过长导致的。 为了解决这个问题,可以利用IntentService来处理耗时的初始化操作。

IntentService的代码如下:
public static void start(Context context) { Intent intent = new Intent(context, DwdInitService.class); intent.setAction(ACTION_INIT_WHEN_APP_CREATE); context.startService(intent); }

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

在Application中start即可

DwdInitService.start(this);

做了一个X5WebView的初始化操作,效果还是比较明显的。

2.优化加速第一个界面的展现

前面讲过一个App启动时,总是会有一个白屏或黑屏出现,从用户体验上来讲是特别不好的,如何消除这个白屏呢?这里我们可以用自定义theme和Drawable来解决这个问题。 这里用一个简单demo作为案例: 如下效果图

description 布局代码,如下

<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff">

<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginBottom="24dp" android:src="@drawable/ic_launcher"/> </FrameLayout>

代码很简单,但是每次启动时,你会发现在这个页面显示之前会有一段白屏。现对代码做如下改造

a.定义一个loading.xml的Drawable
在这里设置背景和logo图片

<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item> <shape android:shape="rectangle" > <solid android:color="#ffffff" /> </shape> </item> <item android:bottom="48dp"> <bitmap android:gravity="center" android:src="@drawable/ic_launcher" /> </item> </layer-list>

b.style中定义一个theme,windowBackground设置背景为之前的loading.xml

<style name="Theme.Default.NoActionBar" parent="@style/AppTheme" > <item name="android:windowBackground">@drawable/loading</item> </style>

c.将定义的theme设置给LoadingActivity
Ok,完成,现在启动App,白屏不见了,用户体验也得到了提升。

二.Activity的优化加速

进入App后,页面之间的跳转速度也是用户体验的重要部分,例如打开一个内嵌网页,点击触发按钮后,会出现一个卡顿后才跳转过去。

对于一个Activity的优化,同时是缩减onCreate方法的执行时间。onCreate方法往往有两部分构成,一是setContentView()用来实现布局,一是在onCreate做的数据初始化和填充。

第二点比较容易理解,onCreate中尽量减少耗时的数据读取和计算工作,可以使用异步回调的方式来实现,减少UI主线程的占用。

现在从setContentView来看,布局中每一个控件都需要初始化、布局、绘制,这些多是需要消耗时间的操作,从而降低显示速度。并且在onCreate中没有耗时的数据操作的情况下,通过TraceView工具监测到setContentView()几乎占用了从onCreate()开始到onResume()结束之间所有时间的99%。

减少setContentView花费的时间:

1.减少布局嵌套层级
a.使用相对布局
减少线性布局的使用,尽量使用相对布局,减少嵌套层级。嵌套多个使用layout_weight属性的LinearLayout实例会花费更大的代价,因为每一个子布局都要测量两次;虽然相对布局比较繁琐,但是可以减少嵌套层级,也就减少了绘制时间。

b.使用标签合并布局
使用标签合并布局,减少布局层级,但merage标签只对FrameLayout布局起作用,而Activity内容视图的parent就是一个FrameLayout。 未使用merge标签代码如下 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="merge标签"/> </RelativeLayout>

使用merge标签的代码

<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="merge标签"/> </merge>

该布局在使用merge标签后,布局层级也相应减少。 c.利用控件自身属性 使用控件自身属性,减少嵌套层级 例如常见的线性排列菜单布局,如下

description 用LinearLayout实现代码如下:

<LinearLayout android:layout_width="match_parent" android:layout_height="62dip" android:layout_marginTop="20dp" android:orientation="horizontal" android:gravity="center_vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/order" android:layout_marginLeft="28dp" /> <TextView android:id="@+id/my_order_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:gravity="center_vertical" android:text="@string/my_order" android:textColor="@color/c3_dwd" android:textSize="18sp" /> </LinearLayout>

使用TextView的属性drawableRight代码如下

<TextView android:id="@+id/my_order" android:layout_width="match_parent" android:layout_height="62dip" android:layout_marginTop="20dp" android:drawableLeft="@drawable/order" android:drawablePadding="15dip" android:gravity="center_vertical" android:paddingLeft="28dip" android:text="@string/my_order" android:textColor="@color/c3_dwd" android:textSize="18sp" />

代码量和嵌套层级都相应减少,效果也完美实现。

2.使用ViewStub延迟展开

ViewStub是轻量级且不可见的视图,当需要时,在自己的布局中可以用它来推迟展开布局,在需要展开的时候,通过inflate展开,同时通过设置标志位在onResume中展开布局也是一种方法。
特性: (1).ViewStub只能Inflate一次,之后ViewStub对象会被置为空。按句话说,某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了。 (2).ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。 使用场景: (1).在程序的运行期间,某个布局在Inflate后,就不会有变化,除非重新启动。(复杂布局) (2).想要控制显示与隐藏的是一个布局文件,而非某个View。 案例:用ViewStub优化后,展开时间可以减少1/2到2/3, <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center_horizontal"> <ViewStub android:id="@+id/viewstub_demo_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout="@layout/viewstub_demo_text_layout"/> <ViewStub android:id="@+id/viewstub_demo_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout="@layout/viewstub_demo_image_layout"/> </LinearLayout>

使用一下代码,在onCreate()方法里展开布局

ViewStub viewStub = (ViewStub)findViewById(R.id.viewstub_demo_image); viewStub.inflate();