1.布局優(yōu)化
2.繪制優(yōu)化
3.響應(yīng)速度優(yōu)化和ANR日志分析
布局優(yōu)化
布局優(yōu)化思想就是,盡量減少布局文件的層級(jí),以便減少android繪制的工作量.
刪除無(wú)用的控件和層級(jí),如果在相同層級(jí)的情況下,盡量用LinearLayout.而不用RelativeLayout.一個(gè)View能展示出來(lái),需要依次經(jīng)過(guò)measure奴愉,layout和draw三個(gè)過(guò)程才最終將一個(gè)View繪制出來(lái),而兩者過(guò)程存在不同的差異
RelativeLayout
因?yàn)镽elativeLayout允許A轨蛤,B 2個(gè)子View但狭,橫向上B依賴A,縱向上A依賴B甲献。所以RelativeLayout在onMeasure測(cè)量子控件的時(shí)候,會(huì)對(duì)子View做兩次measure(需要橫向縱向分別進(jìn)行一次排序測(cè)量)
LinearLayout
LinearLayout會(huì)先判斷線性規(guī)則,然后執(zhí)行對(duì)應(yīng)方向上的測(cè)量
1.RelativeLayout會(huì)讓子View調(diào)用2次onMeasure颂翼,LinearLayout 在有weight時(shí)晃洒,也會(huì)調(diào)用子View2次onMeasure
2.RelativeLayout的子View如果高度和RelativeLayout不同,則會(huì)引發(fā)效率問(wèn)題朦乏,當(dāng)子View很復(fù)雜時(shí)球及,這個(gè)問(wèn)題會(huì)更加嚴(yán)重。如果可以呻疹,盡量使用padding代替margin吃引。
3.在不影響層級(jí)深度的情況下,使用LinearLayout和FrameLayout而不是RelativeLayout。Google給開發(fā)者默新建了個(gè)RelativeLayout,而自己卻在DecorView中用了個(gè)LinearLayout镊尺。因?yàn)镈ecorView的層級(jí)深度是已知而且固定的朦佩,上面一個(gè)標(biāo)題欄,下面一個(gè)內(nèi)容欄庐氮。采用RelativeLayout并不會(huì)降低層級(jí)深度语稠,所以此時(shí)在根節(jié)點(diǎn)上用LinearLayout是效率最高的。而之所以給開發(fā)者默認(rèn)新建了個(gè)RelativeLayout,是希望開發(fā)者能采用盡量少的View層級(jí)來(lái)表達(dá)布局以實(shí)現(xiàn)性能最優(yōu)弄砍,因?yàn)閺?fù)雜的View嵌套對(duì)性能的影響會(huì)更大一些仙畦。
使用<include>標(biāo)簽復(fù)用布局,include只支持android:layout_開頭的屬性
使用<merge>標(biāo)簽減少布局層級(jí)
<!-- 未使用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>
在布局根節(jié)點(diǎn)和上一級(jí)布局根節(jié)點(diǎn)相同的情況下,使用merge標(biāo)簽會(huì)減少布局層級(jí)
使用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();
當(dāng)ViewStub被加載完成之后,就會(huì)被內(nèi)部的布局替換掉,這個(gè)時(shí)候ViewStub就不是布局的一部分了ViewStub不支持merge標(biāo)簽
繪制優(yōu)化
繪制優(yōu)化即是在View的onDraw方法要避免大量的操作.
不要在onDraw里面創(chuàng)建對(duì)象,因?yàn)閛nDraw可能會(huì)被頻繁調(diào)用,導(dǎo)致產(chǎn)生大量的臨時(shí)對(duì)象,占用過(guò)多的內(nèi)存,也會(huì)導(dǎo)致系統(tǒng)更加頻繁的gc ,降低程序的執(zhí)行效率
不要做耗時(shí)的任務(wù),不要執(zhí)行成千上萬(wàn)的循環(huán)操作,大量的循環(huán)會(huì)搶占CPU的時(shí)間片,導(dǎo)致view的繪制過(guò)程不順暢
響應(yīng)速度優(yōu)化和ANR日志分析
響應(yīng)速度優(yōu)化的思想是避免在主線程做耗時(shí)操作,響應(yīng)速度慢,一般體現(xiàn)在Activity的啟動(dòng)速度上,如果主線程做太多事情,會(huì)導(dǎo)致Activity啟動(dòng)時(shí)出現(xiàn)黑屏現(xiàn)象,甚至出現(xiàn)ANR.
android規(guī)定,Activity如果5秒無(wú)法響應(yīng)屏幕觸摸事件,或者鍵盤輸入事件,就會(huì)出現(xiàn)ANR,BroadcastReceiver如果10秒還未執(zhí)行完操作也會(huì)出現(xiàn)ANR
當(dāng)出現(xiàn)ANR,系統(tǒng)會(huì)在/data/anr/目錄下創(chuàng)建一個(gè)文件traces.txt
adb pull traces.txt導(dǎo)出文件,查看ANR的原因
模擬ANR 主線程耗時(shí)操作
在onCreate方法中耗時(shí)操作.
setContentView(R.layout.activity_test_anr);
findViewById(R.id.anr_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SystemClock.sleep(30000);
}
});
然后導(dǎo)出traces.txt文件,并且找到本應(yīng)用的信息
----- pid 28803 at 2017-10-09 17:17:12 -----
Cmd line: 這里是應(yīng)用的包名
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 應(yīng)用包名.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)
可以看到是在應(yīng)用包名.TestANRActivity$1.onClick然后調(diào)用了SystemClock.sleep方法導(dǎo)致的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毫秒,確保先執(zhí)行子線程獲得鎖
init();
}
});
}
private synchronized void testANR() {
SystemClock.sleep(30*10000);
}
private synchronized void init() {
}
先讓testANR()方法獲取到鎖,然后init()方法再去獲取相同的鎖,但是鎖被testANR()同步住了,導(dǎo)致子線程和主線程搶占同步鎖,發(fā)生ANR
----- pid 24899 at 2017-10-09 17:35:10 -----
Cmd line:應(yīng)用包名
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 應(yīng)用包名.TestANRActivity.init(TestANRActivity.java:~0)
- waiting to lock <0x41733870> (a 應(yīng)用包名.TestANRActivity) held by tid=15 (Thread-203)
at 應(yīng)用包名.TestANRActivity.access$100(TestANRActivity.java:8)
at 應(yīng)用包名.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 應(yīng)用包名.TestANRActivity.init(TestANRActivity.java:~0)
waiting to lock <0x41733870> (a winbons.com.myapplication.TestANRActivity) held by tid=15 (Thread-203)
init等待鎖,這個(gè)鎖被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 應(yīng)用包名.TestANRActivity.testANR(TestANRActivity.java:30)
at 應(yīng)用包名.TestANRActivity.access$000(TestANRActivity.java:8)
at 應(yīng)用包名.TestANRActivity$1$1.run(TestANRActivity.java:20)
at java.lang.Thread.run(Thread.java:841)
可以看到tid=15 (Thread-203) 是一個(gè)子線程,正在 應(yīng)用包名.TestANRActivity.testANR中執(zhí)行SystemClock.sleep,導(dǎo)致的問(wèn)題.
平時(shí)出現(xiàn)ANR問(wèn)題,我們就可以通過(guò)traces.txt文件來(lái)分析具體的原因,定位以及解決問(wèn)題