啟動(dòng)優(yōu)化
冷啟動(dòng)
冷啟動(dòng)指的是應(yīng)用程序從頭開(kāi)始:系統(tǒng)的進(jìn)程沒(méi)有麸折,直到此開(kāi)始,創(chuàng)建了應(yīng)用程序的進(jìn)程粘昨。 在應(yīng)用程序自設(shè)備啟動(dòng)以來(lái)第一次啟動(dòng)或系統(tǒng)殺死應(yīng)用程序等情況下會(huì)發(fā)生冷啟動(dòng)垢啼。
Click Event ->IPC ->Process.start ->ActivityThread ->bindApplication ->LifeCycle ->ViewRootImpl
熱啟動(dòng)
應(yīng)用駐留在內(nèi)存中,會(huì)從后臺(tái)放回到前臺(tái)展示张肾,避免重復(fù)對(duì)象初始化芭析、UI布局和渲染。
后臺(tái)->前臺(tái)
溫啟動(dòng)
用戶退出了應(yīng)用吞瞪,但隨后又重啟馁启,這時(shí)候會(huì)進(jìn)程和Activity生命周期都會(huì)重走。
LifeCycle
可使用一下工具獲取啟動(dòng)時(shí)間:
TraceView
wall time:方法執(zhí)行總時(shí)間
cpu time:cpu在這個(gè)方法上使用的時(shí)間adb命令:adb shell am start -W –n <packageName>/<activityName>
加上 -s 就是殺掉重啟應(yīng)用
如果只關(guān)心某個(gè)應(yīng)用自身啟動(dòng)耗時(shí)尸饺,參考TotalTime进统;
如果關(guān)心系統(tǒng)啟動(dòng)應(yīng)用耗時(shí),參考WaitTime浪听;
如果關(guān)心應(yīng)用有界面Activity啟動(dòng)耗時(shí)螟碎,參考ThisTime。
優(yōu)化小技巧:
- Theme主題切換:感覺(jué)上快了迹栓。在app啟動(dòng)時(shí)候使用一個(gè)帶logo的主題掉分,MainActivity起來(lái)后,在super.onCreate(savedInstanceState)前把主題setTheme回來(lái)克伊。
- 異步優(yōu)化:使用封裝的線程池酥郭,子線程(每個(gè)子線程只干一件事)分擔(dān)主線程任務(wù),并行減少時(shí)間愿吹。
注意事項(xiàng):
1.不符合異步要求不从,要改;
2.需要在某階段完成犁跪,使用CountDownLatch保證線程同步椿息;
3.區(qū)分CPU密集型和IO密集型任務(wù)。CPU密集型則不能創(chuàng)建過(guò)多線程坷衍,會(huì)消耗CPU運(yùn)算資源寝优,而IO密集型則可以創(chuàng)建多個(gè)線程去操作。 - 啟動(dòng)器優(yōu)化
1.區(qū)分主線程枫耳、子線程運(yùn)用乏矾,非必主線程運(yùn)行的都可以移植到子線程去做;
2.執(zhí)行時(shí)機(jī)、順序依賴钻心;
3.使用IdleHandler優(yōu)化凄硼;主要用在我們希望能夠在當(dāng)前線程消息隊(duì)列空閑時(shí)做些事情。譬如 UI 線程在顯示完成后扔役,如果線程空閑我們就可以提前準(zhǔn)備其他內(nèi)容的情況下帆喇,不過(guò)最好不要做耗時(shí)操作。
內(nèi)存優(yōu)化
工具:
- Memory Profiler
- Memory Analyzer
內(nèi)存抖動(dòng)觀察:內(nèi)存使用圖會(huì)程鋸齒狀亿胸,忽高忽低。
- 使用Memory Profiler初步排查
- 找循環(huán)或者頻繁調(diào)用的地方
內(nèi)存泄漏:可用內(nèi)存減少预皇,頻繁GC侈玄。
內(nèi)存溢出:OOM、程序異常退出吟温。
獲取Bitmap占用內(nèi)存:
- getByteCount
- 寬高一像素占用內(nèi)存
圖片優(yōu)化:圖片加載要控制寬高序仙,壓縮編碼。
可使用hook方法去監(jiān)聽(tīng)內(nèi)存實(shí)時(shí)使用情況:
- ARTHook:無(wú)侵入性鲁豪、通用性強(qiáng)潘悼、兼容問(wèn)題大,開(kāi)源方案不能帶到線上環(huán)境爬橡。
- Epic:是一個(gè)虛擬機(jī)層面治唤、以Java Method為粒度的運(yùn)行時(shí)Hook框架。
優(yōu)化方案:
- 開(kāi)啟LargeHeap屬性糙申,有機(jī)會(huì)獲取2倍的系統(tǒng)分配內(nèi)存宾添。注意:作為程序員的我們應(yīng)該努力減少內(nèi)存的使用,盡量想回收和復(fù)用的方法柜裸,而不是想方設(shè)法增大內(nèi)存缕陕。當(dāng)內(nèi)存很大的時(shí)候,每次gc的時(shí)間也會(huì)長(zhǎng)一些疙挺,性能會(huì)下降的扛邑。
- onTrimMemory,這個(gè)方法執(zhí)行铐然,證明系統(tǒng)內(nèi)存不足蔬崩,必須要做資源釋放愿伴,以保存app為上策驹溃,避免被系統(tǒng)殺死。
- 使用SparseArray等就轧。
- 謹(jǐn)慎使用SharedPreference险掀,讀取的時(shí)候會(huì)加載全部數(shù)據(jù)到內(nèi)存中沪袭,性能差。
- 謹(jǐn)慎使用外部庫(kù),要清楚外部庫(kù)寫(xiě)法冈绊,是否本身就存在內(nèi)存泄漏等問(wèn)題侠鳄。
布局優(yōu)化
一個(gè)像數(shù)最好只被繪制一次∷佬可在開(kāi)發(fā)者選項(xiàng)里面伟恶,開(kāi)啟調(diào)試GPU過(guò)度繪制,可以看到頁(yè)面繪制情況毅该。
優(yōu)化方法:
- 16ms內(nèi)刷新不卡頓博秫,刷新頻率60Hz,在這個(gè)范圍內(nèi)可認(rèn)為是流暢不卡頓的眶掌。
- InflaterLayout.Factory:當(dāng)我們使用自定義view時(shí)挡育,需要在xml中使用完整類(lèi)名,系統(tǒng)實(shí)際就是根據(jù)完整類(lèi)名進(jìn)行反射構(gòu)建朴爬。我們可以自己new出view避免系統(tǒng)反射調(diào)用即寒,提高效率。
- AsyncLayoutInflater:WorkThread加載布局召噩,回調(diào)主線程母赵。加載布局xml文件是一個(gè)IO操作過(guò)程。
- X2C:框架具滴,APT編譯器翻譯XML為Java代碼凹嘲,解決IO讀取、反射使用等問(wèn)題抵蚊。但不支持merge標(biāo)簽和使用系統(tǒng)Style施绎。
- 減少View樹(shù)層級(jí),盡量使用ConstraintLayout贞绳、不嵌套使用RelativeLayout谷醉、不在嵌套LinearLayout中使用weight。
- 使用merge標(biāo)簽冈闭,共用父布局俱尼,但只能繪制在根view中。
- 使用Viewstub萎攒,高效占位符遇八,延遲初始化。
- 去掉多余背景色耍休,減少?gòu)?fù)雜shape使用刃永。
- 自定義view重疊時(shí)候要使用clipRect指定繪制區(qū)域,onDraw中避免創(chuàng)建大對(duì)象羊精、耗時(shí)操作斯够。
卡頓優(yōu)化
工具:CPU Profilder、Systrace、StrictMode读规。
自動(dòng)化卡頓檢測(cè)方案原理:
- 消息處理機(jī)制抓督,一個(gè)線程只有一個(gè)Looper。
- mLogging對(duì)象在每個(gè)message處理前后被調(diào)用束亏。
- 主線程發(fā)生卡頓铃在,是在dispatchMessage執(zhí)行耗時(shí)操作。
具體檢測(cè)卡頓做法:
- Looper在其loop方法中的死循環(huán)中有個(gè)mLogging對(duì)象碍遍,在執(zhí)行的時(shí)候打印了一個(gè)Dispatching to日志定铜,執(zhí)行完成的時(shí)候有打印了一個(gè)Finished to日志。
- 可以自定義Printer對(duì)象怕敬,讓Handler的日志都通過(guò)我們自定義的Printer進(jìn)行打印宿稀,然后收集日志信息,匹配Dispatching to和Finished to字段赖捌,如果在設(shè)定的某個(gè)時(shí)間內(nèi)只有Dispatching to字段而沒(méi)有Finished to字段,那么就說(shuō)明發(fā)生了卡頓矮烹。發(fā)生卡頓后我們就收集此時(shí)的調(diào)用棧信息越庇。
ANR介紹
會(huì)發(fā)生ANR的情況:
- 按鍵5s。
- 廣播奉狈,前臺(tái)10s卤唉,后臺(tái)60s。
- 服務(wù)仁期,前臺(tái)20s桑驱,后臺(tái)200s。
檢測(cè)方案:
- Android Performance Monitor(BlockCanary):監(jiān)控msg跛蛋,如果應(yīng)用發(fā)生卡頓熬的,一定是在dispatchMessage中執(zhí)行了耗時(shí)操作。通過(guò)給主線程的Looper設(shè)置一個(gè)Printer赊级,打點(diǎn)統(tǒng)計(jì)dispatchMessage方法執(zhí)行的時(shí)間押框,如果超出閥值,表示發(fā)生卡頓理逊,則dump出各種信息橡伞,提供開(kāi)發(fā)者分析性能瓶頸。
- ANR-WatchDog庫(kù)原理:開(kāi)始線程->post改變數(shù)值->sleep一段時(shí)間->檢測(cè)數(shù)值是否被修改->沒(méi)有修改晋被,證明發(fā)生ANR兑徘。
- Lancet:輕量級(jí)AOP庫(kù),可使用它進(jìn)行方法執(zhí)行時(shí)間檢測(cè)羡洛。
線程優(yōu)化
線程使用情況:
- 任意時(shí)刻挂脑,只有一個(gè)線程占用CPU,處于運(yùn)行狀態(tài)。
- 多線程并發(fā):輪流獲取CPU使用權(quán)最域。
- JVM負(fù)責(zé)線程調(diào)度:按照特定機(jī)制分配CPU使用權(quán)谴分。
Android線程調(diào)度:
- 設(shè)置線程nice值:在process中定義,值越小镀脂,優(yōu)先級(jí)越高牺蹄,默認(rèn)是0,在Android中薄翅,nice的取值范圍為-20~19沙兰。
- cgroup,更嚴(yán)格的群組調(diào)度策略翘魄,保證前臺(tái)線程獲取更多的CPU鼎天。
Android異步方式:
- Thread(直接new不易復(fù)用,頻繁創(chuàng)建開(kāi)銷(xiāo)大)
- HandlerThread(串行執(zhí)行暑竟,不斷從隊(duì)列中獲取任務(wù))
- IntentService(不占用主線程斋射,優(yōu)先級(jí)高,完成就結(jié)束)
- AsyncTask(線程池但荤,無(wú)需處理線程切換)
- 線程池(易復(fù)用罗岖、定時(shí)、任務(wù)隊(duì)列腹躁、并發(fā)數(shù)控制)
- RxJava
注意事項(xiàng):
- 不要直接new Thread桑包,不易復(fù)用。
- 線程要有對(duì)應(yīng)的名字纺非,出問(wèn)題時(shí)候容易定位哑了。
- 線程池要提取公共類(lèi),避免重復(fù)創(chuàng)建占用資源烧颖。
- 線程注意設(shè)置優(yōu)先級(jí)弱左,合理利用資源。
- IO密集型任務(wù)不消耗CPU倒信,可以創(chuàng)建大的核心池科贬。
- CPU密集型則需要少于CPU核心數(shù),對(duì)CPU占用較大鳖悠。
網(wǎng)絡(luò)優(yōu)化
工具:
- Network Profiler
- 抓包工具(Charles榜掌,F(xiàn)iddler,Wireshark乘综,TcpDump)
- TrafficStats
- NetworkStatsManager
技巧:
- 數(shù)據(jù)緩存憎账,設(shè)置過(guò)期時(shí)間,避免重新獲取卡辰。
- 加上版本判斷胞皱,只獲取有變化的數(shù)據(jù)邪意。
- 數(shù)據(jù)壓縮,GZip壓縮反砌。
- 圖片壓縮雾鬼,縮略圖展示,使用webP格式宴树。
- 合并請(qǐng)求策菜,減少次數(shù)。
- DNS優(yōu)化(阿里云)酒贬。
- 協(xié)議版本升級(jí)又憨。
http/0.9版本:1991年,原型版本,功能簡(jiǎn)陋锭吨,只有一個(gè)命令GET,只支持純文本內(nèi)容蠢莺,該版本已過(guò)時(shí)。
http/1.0版本: 1996年5月,支持cache, MIME, method等零如。
http/1.1版本: 1997年1月,默認(rèn)建立持久連接躏将,并能很好地配合代理服務(wù)器工作。還支持以管道方式在同時(shí)發(fā)送多個(gè)請(qǐng)求考蕾,以便降低線路負(fù)載耸携,提高傳輸速度。
http/2 版本: 2015年5月作為互聯(lián)網(wǎng)標(biāo)準(zhǔn)正式發(fā)布,頭部信息和數(shù)據(jù)體都是二進(jìn)制辕翰,引入頭信息壓縮機(jī)制等。
電量?jī)?yōu)化
工具:
- BatteryManager
- Battery Historian
- PowerManager.WakeLock
方案:
- 特別注意視頻播放狈谊。
- 傳感器使用喜命。
- 線程使用(AOP Run方法)。
- 定位(考慮網(wǎng)絡(luò)定位代替GPS)等河劝。
- 使用JobScheduler壁榕。
apk瘦身優(yōu)化
可以在Android Studio里面Build中選擇Analyze APK,看到apk包內(nèi)各個(gè)資源大小赎瞎。
方案:
- 代碼混淆(Proguard牌里、AndResGuard資源混淆)。
- 第三方庫(kù)刪減從簡(jiǎn)處理务甥。
- 避免使用枚舉牡辽,枚舉不僅僅會(huì)造成性能的問(wèn)題,還會(huì)占用大量的內(nèi)存敞临。
- 刪除無(wú)用代碼使用态辛。
- 刪除無(wú)用資源文件,圖片壓縮或使用webp格式挺尿。
- so移除奏黑,abiFilters設(shè)置指定so架構(gòu)炊邦,代碼加載對(duì)應(yīng)so庫(kù)。
列表頁(yè)卡頓優(yōu)化
- 布局優(yōu)化(減少層級(jí)熟史,異步inflate)馁害。
- 圖片壓縮(大小、分辨率蹂匹、編碼格式)優(yōu)化碘菜,滑動(dòng)時(shí)候取消加載。
- 使用viewHolder怒详、convertView復(fù)用炉媒。
- 耗時(shí)任務(wù)異步處理。
- 注意字符串拼接昆烁,使用StringBuilder吊骤。
存儲(chǔ)優(yōu)化
- 異步IO。
- sp存儲(chǔ)性能差静尼,可用騰訊的MMKV代替白粉。
MMKV 是基于 mmap 內(nèi)存映射的 key-value 組件,底層序列化/反序列化使用 protobuf 實(shí)現(xiàn)鼠渺,性能高鸭巴,穩(wěn)定性強(qiáng)。
WebView優(yōu)化
- VasSonic:騰訊開(kāi)源的拦盹、解決webview首屏提速的框架鹃祖。
- WebView在Application中提前初始化,復(fù)用普舆。
- WebView緩存恬口。
- 資源文件本地存儲(chǔ)。