最近在研究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作为案例: 如下效果图
布局代码,如下
<?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.使用
使用<?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.利用控件自身属性
使用控件自身属性,减少嵌套层级
例如常见的线性排列菜单布局,如下
用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();