項(xiàng)目GitHub
本文要點(diǎn)
- 一般使用的卡頓優(yōu)化工具
- 卡頓問(wèn)題概述
- 卡頓問(wèn)題分析難點(diǎn)
- 關(guān)于CPU Profiler
- 關(guān)于Systrace
- 關(guān)于StrictMode
- 磁盤(pán)讀寫(xiě)違例檢測(cè)實(shí)戰(zhàn)
- 實(shí)例限制檢測(cè)實(shí)戰(zhàn)
一般使用的卡頓優(yōu)化工具
- CPU Profiler
- Systrace
- StrictMode
(strict adj.精確的; 絕對(duì)的; 嚴(yán)格的,嚴(yán)謹(jǐn)?shù)模?[植]筆直的
mode n.方式; 狀況; 時(shí)尚,風(fēng)尚; 調(diào)式 模式;)
卡頓問(wèn)題概述
- 很多性能問(wèn)題(如內(nèi)存占用高躬络、耗費(fèi)流量等)都相對(duì)不容易被發(fā)現(xiàn),
但是卡頓問(wèn)題卻是很容易被直觀感受到的搭儒; - 卡頓問(wèn)題較難排查穷当、定位;
卡頓問(wèn)題分析難點(diǎn)
- 可能的產(chǎn)生原因 繁雜:代碼淹禾、內(nèi)存馁菜、繪制、IO稀拐、【在主線程做UI處理火邓、IO操作耗時(shí)操作】等;
- 線上卡頓問(wèn)題,在線下難以復(fù)現(xiàn)铲咨,
卡頓問(wèn)題跟用戶屆時(shí)的現(xiàn)場(chǎng)環(huán)境有很大的關(guān)系躲胳; - 比如,
屆時(shí)用戶終端的磁盤(pán)IO空間不足纤勒,影響了APP的IO寫(xiě)入性能坯苹,
導(dǎo)致APP卡頓,這樣的場(chǎng)景有時(shí)候是很難復(fù)現(xiàn)的摇天;
【最好在問(wèn)題發(fā)生時(shí)候粹湃,就記錄下來(lái)用戶屆時(shí)的場(chǎng)景】
關(guān)于CPU Profiler
圖形的形式展示程序的執(zhí)行時(shí)間、調(diào)用棧泉坐、執(zhí)行次數(shù)等为鳄;
信息全面,包含了所有線程腕让、所有方法的調(diào)用時(shí)間孤钦;
運(yùn)行時(shí)開(kāi)銷比較嚴(yán)重,導(dǎo)致APP運(yùn)行時(shí)所有函數(shù)都會(huì)不等比地變慢纯丸,可能會(huì)帶偏優(yōu)化方向偏形;
-
使用方式
-
Debug.startMethodTracing();
【在需要監(jiān)控的代碼塊
前添加(注意它有四個(gè)重載方法)】 -
Debug.stopMethodTracing();
【在需要監(jiān)控的代碼塊
后添加】 - 生成的調(diào)試文件在sd卡:Android/data/packagename/files
- 上次在內(nèi)存優(yōu)化的實(shí)戰(zhàn)中,
其實(shí)已經(jīng)使用過(guò)觉鼻,提到過(guò)CPU Profiler了俊扭,
這里可以看一下App內(nèi)存優(yōu)化 之 內(nèi)存抖動(dòng)解決實(shí)戰(zhàn)!W钩隆H蟆!3╂ⅰ咒钟!
-
關(guān)于Systrace
監(jiān)控和
跟蹤Api調(diào)用
吹由、線程運(yùn)行
情況若未,生成Html報(bào)告
;需要在API 18以上使用倾鲫,推薦TraceCompat粗合;
-
使用方式
python systrace.py -t 10 [other-options][categories]
- 可以參考CSDN某博客;
或Android性能優(yōu)化 -- Systrace工具(有option或category的參數(shù)表格) - 輕量級(jí)乌昔,開(kāi)銷小
- 直觀反映CPU利用率
- 給出建議
關(guān)于StrictMode
嚴(yán)苛模式隙疚,Android提供的一種運(yùn)行時(shí)檢測(cè)機(jī)制;
如果在開(kāi)發(fā)階段對(duì)成千上萬(wàn)行的代碼進(jìn)行code review磕道,
可能效率是比較低下的供屉;
使用StrictMode之后,
系統(tǒng)會(huì)自動(dòng)檢測(cè)
出來(lái)主線程當(dāng)中違例
的一些情況,
同時(shí)按照代碼的配置
給出相應(yīng)的反應(yīng)
伶丐。方便悼做,強(qiáng)大,容易被忽視
-
主要檢測(cè):線程檢測(cè)策略哗魂、虛擬機(jī)檢測(cè)策略
- 線程檢測(cè)策略【
StrictMode.setThreadPolicy()
】:
如肛走,
自定義的耗時(shí)調(diào)用檢測(cè),如detectCustomSlowCalls()
录别;
磁盤(pán)讀取操作檢測(cè)朽色,detectDiskReads()
網(wǎng)絡(luò)操作檢測(cè),detectNetwork()
【detect vt.查明组题,發(fā)現(xiàn)葫男; 洞察; 偵察崔列,偵查腾誉; 】 - 虛擬機(jī)策略【
StrictMode.setVmPolicy()
】:
Activity泄漏檢測(cè),detectActivityLeaks()
SqlLite對(duì)象泄漏檢測(cè)峻呕,detectLeakedSqlLiteObjects()
限制實(shí)例數(shù)量檢測(cè)利职,setClassInstanceLimit(要限制的類實(shí)例,限制的數(shù)量)
- 線程檢測(cè)策略【
具體使用:
可以在Activity
或者Application
的onCreate()
中調(diào)用StrictMode的方法:
private boolean DEV_MODE = true;
private void initStrictMode() {
if (DEV_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectCustomSlowCalls() //API等級(jí)11瘦癌,使用StrictMode.noteSlowCode
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()// or .detectAll() for all detectable problems
.penaltyLog() //在Logcat 中打印違規(guī)異常信息
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.setClassInstanceLimit(NewsItem.class, 1)
.detectLeakedClosableObjects() //API等級(jí)11
.penaltyLog()
.build());
}
}
【調(diào)試技巧】
設(shè)置一個(gè)DEV_MODE
標(biāo)志位:
只有在線下開(kāi)發(fā)的環(huán)境時(shí)將之設(shè)置為true猪贪,才會(huì)使進(jìn)程打開(kāi)StrictMode
;【檢測(cè)策略的調(diào)用】
detect開(kāi)頭的方法讯私,
都是StrictMode提供的檢測(cè)策略热押,
調(diào)用過(guò)了,則StrictMode
便會(huì)進(jìn)行相應(yīng)的檢測(cè)和反應(yīng)斤寇;-
【響應(yīng)方式配置】
penaltyLog()
【penalty n.懲罰桶癣,刑罰,害處】是出現(xiàn)違規(guī)后用log打印出來(lái)娘锁,即指定StrictMode
的響應(yīng)方式牙寞,
StrictMode
除了打印log的方式,
還有其他響應(yīng)方式莫秆,
如penaltyDeath()
可以讓APP直接崩潰掉间雀,
penaltyDialog()
可以彈出一個(gè)Dialog等!D魇骸H切!7觳怠A狻9椴浴E缁А滤钱! 實(shí)戰(zhàn)一下:
磁盤(pán)讀寫(xiě)違例檢測(cè)(log的響應(yīng)方式
):
/**
* 模擬內(nèi)存泄露的Activity
*/
public class MemoryLeakActivity extends AppCompatActivity implements CallBack{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memoryleak);
ImageView imageView = findViewById(R.id.iv_memoryleak);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.splash);
imageView.setImageBitmap(bitmap);
CallBackManager.addCallBack(this);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.penaltyLog()
.build());
findViewById(R.id.iv_memoryleak).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
writeToExternalStorage();
}
});
}
/**
* 文件系統(tǒng)的操作
*/
public void writeToExternalStorage() {
try {
File externalStorage = Environment.getExternalStorageDirectory();
File mbFile = new File(externalStorage, "xxx.txt");
if (mbFile.exists()){
mbFile.createNewFile();
}
OutputStream output = new FileOutputStream(mbFile, true);
output.write("www.wooyun.org".getBytes());
output.flush();
output.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
CallBackManager.removeCallBack(this);
}
@Override
public void dpOperate() {
// do sth
}
}
Dialog
的響應(yīng)方式:以上IO違例的原因就是
在主線程做了IO操作了
,
這顯然是不行的或南,需要開(kāi)一個(gè)子線程給它整驳规!
-
實(shí)例限制檢測(cè):
public class TestApp extends Application {
static MemoryLeakActivity i = new MemoryLeakActivity();
static MemoryLeakActivity j = new MemoryLeakActivity();
@Override
public void onCreate() {
super.onCreate();
//實(shí)例限制檢測(cè) 測(cè)試
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.setClassInstanceLimit(MemoryLeakActivity.class, 1)
.detectLeakedClosableObjects() //API等級(jí)11
.penaltyLog()
.build());
}
}