編輯:關於Android編程
在業內,Android手機一直有著“越用越慢”的口碑。根據第三方的調研數據顯示,有77%的Android手機用戶承認自己曾遭遇過手機變慢的影響。他們不明白為什麼購買之初“如絲般順滑”的Android手機,在使用不到一年之後都會“卡頓”得讓人抓狂!根據我們初步的測試數據,手機長期所使用產生的磁盤碎片可以使得磁盤的寫入效率下降為原來的50%。是不是有一種“嚇死本寶寶了”的感覺。
那麼怎麼辦呢?筆者曾經對這一問題進行分析,且讓我一一向你道來。
故事的起因是,針對“Android系統越用越卡的問題”,騰訊某產品團隊希望在自身產品中進行優化,從而提升產品口碑。
經過簡單的分析討論,大家認為造成這種現象可能是由於兩個方面原因:內存、磁盤:
先說內存。為了保證應用可以快速被再次調起,Android在內存管理上采用如下策略:進程保持在內存中,在占用內存未超過阈值之前不會系統進行主動清理。但隨著應用的增多,試圖保持在內存中的進程將會增多,因此影響系統的流暢度。可以說,內存與系統卡頓的關系早已是業界的共識,其解決方案也比較明了,即賦予系統主動清理內存的能力,例如待機後殺掉不必要的進程。
再聊磁盤。長期使用Android手機必將產生大量的磁盤碎片,而磁盤碎片將會降低磁盤的讀寫性能,從而影響系統流暢度。但是磁盤碎片是否能對磁盤的讀寫性能造成了很大影響,以至於影響系統流暢度尚未可知,且暫時也沒有發現可以進行嘗試的潛在優化點。
於是,產品團隊找到了我們專項測試組,希望分析Android越用越卡與磁盤是否有關系,並初步探索系統在磁盤管理模式方面是否存在潛在優化點。這就有了下文。
其實,日常的生活經驗(例如SSD可以讓老筆記本煥發新生)已經我們能夠感覺得到磁盤對系統流暢度的影響很大。但是,這裡還是有再必要簡單說一下,磁盤是如何影響系統流暢度的。
開發過Android項目的同學都知道Android在使用網絡的最佳實踐是使用3級緩存的設計來提升系統的流暢度並節省流量:CPU首先嘗試從內存中加載圖片,若此時圖片存在在內存中則加載成功,否則內存會從磁盤中加載圖片,若此時圖片存在在磁盤中則加載成功,否則磁盤會最終向網絡中下載圖片。
其實上述的執行邏輯,也就解釋了磁盤是如何影響系統流暢度的:對於系統流暢度(其實也是各個應用的流暢度)影響最直接的就是CPU的執行效率,但是如果這個過程中內存、磁盤以及網絡的讀寫速度如果跟不上CPU的執行效率的話,就會造成CPU在處理任務的時候需要花費時間等待數據,從而影響了流暢度。
所以第一個問題就弄清楚了:磁盤的讀寫速度的降低會使得系統流暢度變差!那麼,我們要分析的問題就轉化成:磁盤在長期使用的過程中,其讀寫速度會不會降低。
為了分析清楚磁盤“磁盤在長期使用的過程中,其讀寫速度會不會降低”這個問題,我們有必要先弄明白Android磁盤所采用的讀寫機制。
通過資料查閱,我們了解到目前,Android手機大多采用NAND Flash架構的閃存卡來存儲內容。NAND Flash的內部存儲單位從小到大依次為:Page、Block、Plane、Die,而一個Device上可以封裝若干個Die。下圖就是一個NAND Flash組成結構的示意圖。
為了方便理解,針對一個Die,我們再抽象一下,Page、Block、Plane、Die的關系如下圖所示。
雖然NAND Flash的優點多多,但是為了延長驅動器的壽命,它的讀寫操作均是以Page為單位進行的,但擦除操作卻是按Block為單位進行的。
由於有大量的讀寫操作,於是我們的NAND Flash制定了如下的讀寫規則:
刪除數據時,芯片將標記這些Page為閒置狀態,但並不會立馬執行擦除操作。 寫入數據時,如果目前磁盤剩余空間充足,則由芯片指定Block後直接按Page為單位進行寫入即可。 寫入數據時,如果目前磁盤剩余空間不足,為了獲得足夠的空間,磁盤先將某塊Block的內容讀至緩存,然後再在該Block上進行擦除操作,最後將新內容與原先內容一起寫入至該Block。那麼問題來了!假如現在我要向磁盤中寫入一張圖片的數據,這個圖片的數據大小剛好為一個Page。最壞的情況就是,內存中恰好只有一個Block恰好有一個Page的無效數據可以擦除。為了存下這張圖片,於是主控就把這個Block的所有數據讀至緩存,擦除Block上的內容,再向緩存中加上這個4KB新數據後最後寫回Block中。
我的天啊,其實想存儲的就是1個Page的圖片內容,但是實際上確造成了整個Block的內容都被重新寫入,同時原本簡單一步搞定的事情被還被分成了前後四步執行(閃存讀取、緩存改、閃存擦除、閃存寫入)造成延遲大大增加,速度變慢。這就是傳說中的“寫入放大”(Write Amplification)問題。而“寫入放大”也說明了磁盤在長期使用的過程中,其讀寫速度(尤其是寫入速度)會存在降低的現象。
不過,既然“寫入放大”(Write Amplification)都這麼出名了,肯定不會沒有現成的解決方案的!這個很簡單,Google一下,我們就知道解決方案就是TRIM技術。
TRIM是一條ATA指令,由操作系統發送給閃存主控制器,告訴它哪些數據占的地址是“無效”的。在TRIM的幫助下,閃存主控制器就可以提前知道哪些Page是“無效”的,便可以在適當的時機做出優化,從而改善性能。這裡要強調下,TRIM只是條指令,讓操作系統告訴閃存主控制器這個Page已經“無效”就算完了,並沒有任何其它多余的操作。在測試的過程中,我們發現TRIM的觸發需要操作系統、驅動程序以及閃存主控三者都支持才能真正意義上實現。例如:
操作系統不支持的情況:Android 4.3以下均不支持 閃存主控不支持的情況:Samsung Galaxy Nexus(I9250)所選用的閃存不支持基於TRIM技術,目前常見有兩種方案可以解決“寫入放大”的問題:
discard選項。該方案將在掛載 ext4 分區時加上 discard 選項,此後操作系統在執行每一個磁盤操作時同時都會執行 TRIM 指令。該方案的優點是總體耗時短,但影響會到刪除文件時的性能。 fstrim命令。該方案將選擇合適的時機對整個分區執行TRIM操作。相對於方案一,該方案總體耗時較長,但不會影響正常操作時的磁盤性能。不得不說,如果從用戶的角度出發,還是FSTRIM的方法更靠譜一些,但如何尋找合適的TRIM時機就是一個比較講究的問題了。
根據前面的分析,我們不難理解在Android中的 TRIM 選擇通過fstrim命令的方式進行實現。那麼,Google又是如何設計觸發TRIM的時機呢?
通過走讀Android源碼(AOSP 4.4.4),可以了解到Android通過系統服務IdleMaintenanceService來進行系統狀態監控並決定何時觸發TRIM。根據IdleMaintenanceService.java源碼,我們繪制了fstrim的觸發示意圖如下:
注釋:
- 有/無操作:距屏幕熄滅"|屏保啟動已超過71分鐘
- 是/否電量充足:維護期20%,非維護期(充電狀態30%,非充電狀態80%)
- 是/否維護超時:啟動維護已超過71分鐘
- 是/否已到維護期:據上次啟動維護超過1天
了解了這麼多技術背景,那我們通過測試數據分析閃存碎片和TRIM對磁盤I/O性能的影響。根據測試目的,具體的測試設置如下:
測試目的:評估閃存碎片和TRIM對磁盤I/O性能的影響
測試方案:測試對象:LG Nexus 5 with cm-11-20140805-SNAPSHOT-M9-hammerhead
測試步驟:
重新刷機,使用Bonnie++測試SD卡目錄的I/O性能; 模擬長期使用SD卡的過程(期間需要避免TRIM觸發),使用Bonnie++測試SD卡目錄的I/O性能; 主動觸發TRIM,使用Bonnie++測試SD卡目錄的I/O性能。備注:
模擬長期使用SD卡的過程的方法:開發專用的測試應用,該應用將向SD卡目錄不停寫入大小隨機的文件,當SD卡剩余空間不足時將刪除所寫入的文件,然後繼續上述操作直到應用退出。避免TRIM觸發的方法:根據Android的觸發過程分析,只需設置屏幕常亮並即可避免TRIM的觸發。
測試數據:數據解讀:
通過反復擦寫SD卡,可以發現SD卡的I/O效率指標均存在一定幅度的下滑,其中反映磁盤空間分配性能及文件數據寫回性能的指標下滑明顯; Sequential Output-Block可以反映分配磁盤文件空間的效率,經反復擦寫SD卡後,該效率降低至原始值的15-20%,應該是大量的磁盤閒置數據塊造成的影響; Sequential Output-Rewrite可以反映文件系統緩存和數據傳輸的速度,經反復擦寫SD卡制造閒置數據塊後,該效率降低至原始值的50%。主動調用TRIM後,可以發現SD卡的I/O效率指標均恢復至接近原始值水平(但仍未完全達到初始狀態的水平)。
測試結論<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPqO6DQo8cD7U2lRSSU3O3tCntcTH6b/2z8KjrLOkxtrKudPDU0S/qKOstMXFzNC0yOvL2bbIu+HK3LW9w/fP1NOwz+w7PC9wPg0KVFJJTbbU0vLP0NbDyv2+3b/p1OyzybXESS9P0NTE3M/CvbXT0NK7tqi1xLvWuLTX99PDo7sgtPPBv7XEtsHQtLLZ1/e21FNEv6jU7LPJwcvSu7aowb+1xLK7v8m71ri0tcTL8LrEoaMNCjxoMiBpZD0="step-5fstrim系統自動觸發測試">Step 5:FSTRIM系統自動觸發測試完成了上面的工作,不由得讓我們大吃一鯨:原來TRIM對SD卡的讀寫速度的維護如此重要!前面也說到,Android選擇FSTRIM方案的來實現TRIM,那麼Android所設計的FSTRIM觸發時機有沒有什麼問題呢?
根據Android系統的設定,FSTRIM預期是每隔24小時觸發一次。所以,接下來我們需要評估一下,FSTRIM能否依據上述設定成功被系統觸發。
測試目的:分析FSTRIM能否被按時被系統觸發
測試方案:測試對象:2台Samsung Galaxy Nexus 及2台LG Nexus 5
測試步驟:
刷機後,安裝常用應用並啟動(均無SIM卡,其中1台設備開啟Wifi,另1台設備關閉Wifi); 進行Log記錄; 強制執行一次FSTRIM;滅屏等待30小時左右,提取Log記錄進行分析。
測試數據:數據解讀:
FSTRIM大多數情況會被自動觸發,但也存在無法觸發的情況;根據FSTRIM的觸發邏輯,是否開啟WIFI對FSTRIM的影響主要是有無推送消息(影響滅屏條件)以及不同的耗電。
測試結論:測試數據顯示FSTRIM大多數情況會被自動觸發,但也存在無法觸發的情況。可能的原因是:FSTRIM對電量的要求略高,所以一旦發生意外情況(如應用的PUSH消息)終止了計劃FSTRIM的執行之後,很長時間之內都無法再滿足FSTRIM的啟動條件。
所以,如需提高其觸發頻率,我們可以考慮降低觸發條件中對電量的要求。
根據前面的分析,我們可以從Android源碼及測試數據對前面兩個問題做出回答:
磁盤碎片(更准確的說法是SD卡中的閒置數據塊)會嚴重影響磁盤的讀寫性能,可能會導致Android系統越用越卡,而Android系統的FSTRIM對此有恢復的作用; 經過實驗分析, FSTRIM並不一定能夠按期(每天一次)執行。而導致這一問題的原因可能是IdleMaintenanceService對電量的要求過高(未充電狀態下大於80%)。當然,我們可以通過一下手段對這一問題做出優化嘗試:
FSTRIM對電量的要求略高,如需提高其觸發頻率可以從降低觸發條件中對電量的要求; 在必要的情況下,可以發送特定的Intent事件,使系統強制觸發FSTRIM。至此,我們也大致解答了項目組提出的問題,這個故事也基本可以告一段落了。
回顧整個分析問題的過程,我發現,作為一名專項測試人員,盡管我並不需要實際編寫項目中的任何一句代碼,但這並不意味著我不需要了解Android及其Framework的代碼。實際上,只有在平時的學習和工作中了解其工作機制的基礎上,我們才能設計出合理的測試方案,從而更好的完成工作。
前言本文給大家分享一個使用Android開發寫字板功能Dem、簡單操作內存中的圖像、對圖像進行簡單的處理、繪制直線、以達到寫字板的效果效果圖如下XML布局代碼<Re
AndroidStudio上github使用要想在AndroidStudio上面使用github,首先要下載git工具然後在AndroidStudio上面設置git工具的
RecyclerView多個item布局的寫法(頭布局+腳布局)上圖github下載源碼Initial commit第一次提交的代碼,為本文內容以下的為主要代碼,看注釋即
tinyalsa位於Android源碼的external/tinyalsa位置。關於tinyalsa,tinyalsa是Google在Android 4.0之後推的基於a