1.布局优化
2.绘制优化
3.响应速度优化和ANR日志分析
布局优化
布局优化思想就是,尽量减少布局文件的层级,以便减少android绘制的工作量.
删除无用的控件和层级,如果在相同层级的情况下,尽量用LinearLayout.而不用RelativeLayout.一个View能展示出来,需要依次经过measure,layout和draw三个过程才最终将一个View绘制出来,而两者过程存在不同的差异
RelativeLayout
因为RelativeLayout允许A,B 2个子View,横向上B依赖A,纵向上A依赖B。所以RelativeLayout在onMeasure测量子控件的时候,会对子View做两次measure(需要横向纵向分别进行一次排序测量)
LinearLayout
LinearLayout会先判断线性规则,然后执行对应方向上的测量
1.RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,也会调用子View2次onMeasure
2.RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。
3.在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout。Google给开发者默新建了个RelativeLayout,而自己却在DecorView中用了个LinearLayout。因为DecorView的层级深度是已知而且固定的,上面一个标题栏,下面一个内容栏。采用RelativeLayout并不会降低层级深度,所以此时在根节点上用LinearLayout是效率最高的。而之所以给开发者默认新建了个RelativeLayout,是希望开发者能采用尽量少的View层级来表达布局以实现性能最优,因为复杂的View嵌套对性能的影响会更大一些。
使用<include>标签复用布局,include只支持android:layout_开头的属性
使用<merge>标签减少布局层级
<!-- 未使用merge -->
<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="wrap_content" >
...
</LinearLayout>
<!-- 使用mrge -->
<merge 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="wrap_content" >
...
</merge>
在布局根节点和上一级布局根节点相同的情况下,使用merge标签会减少布局层级
使用ViewStub
<ViewStub
android:id="@+id/view_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/布局C" />
加载的两种方式:
((ViewStub)findViewById(R.id.view_stub)).setVisibility(View.VISIBLE);
View view = ((ViewStub)findViewById(R.id.view_stub)).inflate();
当ViewStub被加载完成之后,就会被内部的布局替换掉,这个时候ViewStub就不是布局的一部分了ViewStub不支持merge标签
绘制优化
绘制优化即是在View的onDraw方法要避免大量的操作.
不要在onDraw里面创建对象,因为onDraw可能会被频繁调用,导致产生大量的临时对象,占用过多的内存,也会导致系统更加频繁的gc ,降低程序的执行效率
不要做耗时的任务,不要执行成千上万的循环操作,大量的循环会抢占CPU的时间片,导致view的绘制过程不顺畅
响应速度优化和ANR日志分析
响应速度优化的思想是避免在主线程做耗时操作,响应速度慢,一般体现在Activity的启动速度上,如果主线程做太多事情,会导致Activity启动时出现黑屏现象,甚至出现ANR.
android规定,Activity如果5秒无法响应屏幕触摸事件,或者键盘输入事件,就会出现ANR,BroadcastReceiver如果10秒还未执行完操作也会出现ANR
当出现ANR,系统会在/data/anr/目录下创建一个文件traces.txt
adb pull traces.txt导出文件,查看ANR的原因
模拟ANR 主线程耗时操作
在onCreate方法中耗时操作.
setContentView(R.layout.activity_test_anr);
findViewById(R.id.anr_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SystemClock.sleep(30000);
}
});
然后导出traces.txt文件,并且找到本应用的信息
----- pid 28803 at 2017-10-09 17:17:12 -----
Cmd line: 这里是应用的包名
JNI: CheckJNI is off; workarounds are off; pins=0; globals=271
DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
"main" prio=5 tid=1 TIMED_WAIT
| group="main" sCount=1 dsCount=0 obj=0x415baca8 self=0x414f3580
| sysTid=28803 nice=0 sched=0/0 cgrp=apps handle=1074295124
| state=S schedstat=( 0 0 0 ) utm=31 stm=31 core=6
at java.lang.VMThread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:1013)
at java.lang.Thread.sleep(Thread.java:995)
at android.os.SystemClock.sleep(SystemClock.java:115)
at 应用包名.TestANRActivity$1.onClick(TestANRActivity.java:18)
at android.view.View.performClick(View.java:4445)
at android.view.View$PerformClick.run(View.java:18429)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:736)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:570)
at dalvik.system.NativeStart.main(Native Method)
可以看到是在应用包名.TestANRActivity$1.onClick然后调用了SystemClock.sleep方法导致的ANR.
模拟ANR同步锁,等待
findViewById(R.id.anr_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
testANR();
}
}).start();
SystemClock.sleep(10);//延迟10毫秒,确保先执行子线程获得锁
init();
}
});
}
private synchronized void testANR() {
SystemClock.sleep(30*10000);
}
private synchronized void init() {
}
先让testANR()方法获取到锁,然后init()方法再去获取相同的锁,但是锁被testANR()同步住了,导致子线程和主线程抢占同步锁,发生ANR
----- pid 24899 at 2017-10-09 17:35:10 -----
Cmd line:应用包名
JNI: CheckJNI is off; workarounds are off; pins=0; globals=271
DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
"main" prio=5 tid=1 MONITOR
| group="main" sCount=1 dsCount=0 obj=0x415baca8 self=0x414f3580
| sysTid=24899 nice=0 sched=0/0 cgrp=apps handle=1074295124
| state=S schedstat=( 0 0 0 ) utm=32 stm=25 core=2
at 应用包名.TestANRActivity.init(TestANRActivity.java:~0)
- waiting to lock <0x41733870> (a 应用包名.TestANRActivity) held by tid=15 (Thread-203)
at 应用包名.TestANRActivity.access$100(TestANRActivity.java:8)
at 应用包名.TestANRActivity$1.onClick(TestANRActivity.java:25)
at android.view.View.performClick(View.java:4445)
at android.view.View$PerformClick.run(View.java:18429)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:736)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:570)
at dalvik.system.NativeStart.main(Native Method)
traces.txt日志分析,可以看到
at 应用包名.TestANRActivity.init(TestANRActivity.java:~0)
waiting to lock <0x41733870> (a winbons.com.myapplication.TestANRActivity) held by tid=15 (Thread-203)
init等待锁,这个锁被tid=15 (Thread-203)持有了.
"Thread-203" prio=5 tid=15 TIMED_WAIT
| group="main" sCount=1 dsCount=0 obj=0x41a9da90 self=0x6ba96b58
| sysTid=25026 nice=0 sched=0/0 cgrp=apps handle=1805145208
| state=S schedstat=( 0 0 0 ) utm=0 stm=0 core=5
at java.lang.VMThread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:1013)
at java.lang.Thread.sleep(Thread.java:995)
at android.os.SystemClock.sleep(SystemClock.java:115)
at 应用包名.TestANRActivity.testANR(TestANRActivity.java:30)
at 应用包名.TestANRActivity.access$000(TestANRActivity.java:8)
at 应用包名.TestANRActivity$1$1.run(TestANRActivity.java:20)
at java.lang.Thread.run(Thread.java:841)
可以看到tid=15 (Thread-203) 是一个子线程,正在 应用包名.TestANRActivity.testANR中执行SystemClock.sleep,导致的问题.
平时出现ANR问题,我们就可以通过traces.txt文件来分析具体的原因,定位以及解决问题