效能優化之卡頓分析-計算並優化記憶體抖動和耗時操作
1 卡頓(卡UI執行緒)
(1)外部引起的:Activity裡面直接進行網路訪問/大檔案的IO操作。
(2)記憶體引起的:記憶體抖動的問題,new Object obj = null;執行耗時方法。
(3)View本身的卡頓:自定義View要注意的,能否優化:
2 優化主方向
(1)避免讓主執行緒執行耗時的操作
(2)優化自定義View卡頓問題
3 解決方法(有問題???)
3.1 步驟一:使用Allocation Tracing來定位大致的情況
3.2 步驟二:使用TraceView來確定詳細的問題所在
3.2.1 TraceView工具能做什麼?
從程式碼層面分析效能問題,針對每個方法來分析,比如當我們發現我們的應用出現卡頓的時候,我們可以來分析出現卡頓時在方法的呼叫上有沒有很耗時的操作,關注以下兩個問題:
(1)呼叫次數不多,但是每一次執行都很耗時
(2)方法耗時不大,但是呼叫次數太多
簡單一點來說就是:我們能找到頻繁被呼叫的方法,也能找到執行非常耗時的方法,前者可能會造成Cpu頻繁呼叫,手機發燙的問題,後者就是卡頓的問題
3.2.2 參考連結
3.2.3 TraceView工具啟動
(1)開啟App操作應用後,start開始追蹤,點選stop後停止追蹤並且自動開啟traceview分析面板:
(2)開啟traceview分析面板:
展開一個方法後可以看到有兩部分:Parent表示呼叫這個方法的方法,可以叫做父方法;Children表示這個方法中呼叫的其他方法,可以叫做子方法。
Profile面板中各列作用說明:
3.2.4 記憶體抖動分析
(1)圖例演示
(2)找到問題所在:rowAsStr += “, “;
/**
* com.example.android.mobileperf.compute.MemoryChurnActivity的以下方法:
* 排序後列印二維陣列,一行行列印
*/
public void imPrettySureSortingIsFree() {
// 優化以前
int dimension = 300;
int[][] lotsOfInts = new int[dimension][dimension];
Random randomGenerator = new Random();
for(int i = 0; i < lotsOfInts.length; i++) {
for (int j = 0; j < lotsOfInts[i].length; j++) {
lotsOfInts[i][j] = randomGenerator.nextInt();
}
}
for (int i = 0; i < lotsOfInts.length; i++) {
String rowAsStr = "";
//排序
int[] sorted = getSorted(lotsOfInts[i]);
//拼接列印
for (int j = 0; j < lotsOfInts[i].length; j++) {
rowAsStr += sorted[j];
if(j < (lotsOfInts[i].length - 1)){
rowAsStr += ", ";
}
}
}
//優化以後
// StringBuilder sb = new StringBuilder();
// String rowAsStr = "";
// for(int i = 0; i < lotsOfInts.length; i++) {
// //清除上一行
// sb.delete(0,rowAsStr.length());
// //排序
// int[] sorted = getSorted(lotsOfInts[i]);
// //拼接列印
// for (int j = 0; j < lotsOfInts[i].length; j++) {
//// rowAsStr += sorted[j];
// sb.append(sorted[j]);
// if(j < (lotsOfInts[i].length - 1)){
//// rowAsStr += ", ";
// sb.append(", ");
// }
// }
// rowAsStr = sb.toString();
// Log.i("ricky", "Row " + i + ": " + rowAsStr);
// }
}
3.2.5 執行耗時方法
(1)圖例演示
(2)找到問題所在:遞迴問題導致
// com.example.android.mobileperf.compute.CachingActivity
//優化前
public int computeFibonacci(int pInFibSequence) {
//0 1 1 2 3 5 8
if (pInFibSequence <= 2) {
return 1;
} else {
return computeFibonacci(pInFibSequence - 1)+ computeFibonacci(pInFibSequence - 2);
}
}
//優化後的斐波那契數列的非遞迴演算法 caching快取+批處理思想
public int computeFibonacci(int pInFibSequence) {
int prev = 0;
int current = 1;
int newValue;
for (int i=1; i<pInFibSequence; i++) {
newValue = current + prev;
prev = current;
current = newValue;
}
return current;
}
(3)解決問題思路
儘量避免讓【主執行緒】執行耗時的操作,讓它能快速處理UI事件和Broadcast訊息。
4 如何使用Traceview計算某個類的耗時
(1)在onCreate開始和結尾打上trace.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Debug.startMethodTracing("textracing");//開始
sleep();
login();
Debug.stopMethodTracing();//結束
}
private void sleep() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
(2)執行程式, 會在手機sdcard或者本地儲存上生成一個”textracing.trace”的檔案。注意: 需要給程式加上寫儲存的許可權:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
(3)MAC:通過adb pull將其匯出到本地,然後將textracing.trace從手機傳到電腦上
adb pull /sdcard/Traceview.trace ~/temp
(4)Windows:直接將textracing.trace從手機傳到電腦上(textracing.trace存放在根目錄)
(5)Android Studio直接開啟