乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Android性能優(yōu)化典范

       老匹夫 2015-06-16

      Android性能優(yōu)化典范 - 第2季


         
         
           


             








       



             
               | Comments
             
           


         
       



      android_perf_patterns_season_2



      Google前幾天剛發(fā)布了Android性能優(yōu)化典范第2季的課程,一共20個(gè)短視頻,包括的內(nèi)容大致有:電量?jī)?yōu)化,網(wǎng)絡(luò)優(yōu)化,Wear上如何做優(yōu)化,使用對(duì)象池來(lái)提高效率,LRU Cache,Bitmap的縮放,緩存,重用,PNG壓縮,自定義View的性能,提升設(shè)置alpha之后View的渲染性能,以及Lint,StictMode等等工具的使用技巧。
      下面是對(duì)這些課程的總結(jié)摘要,認(rèn)知有限,理解偏差的地方請(qǐng)多多指教!



      1)Battery Drain and Networking



      對(duì)于手機(jī)程序,網(wǎng)絡(luò)操作相對(duì)來(lái)說(shuō)是比較耗電的行為。優(yōu)化網(wǎng)絡(luò)操作能夠顯著節(jié)約電量的消耗。在性能優(yōu)化第1季里面有提到過(guò),手機(jī)硬件的各個(gè)模塊的耗電量是不一樣的,其中移動(dòng)蜂窩模塊對(duì)電量消耗是比較大的,另外蜂窩模塊在不同工作強(qiáng)度下,對(duì)電量的消耗也是有差異的。當(dāng)程序想要執(zhí)行某個(gè)網(wǎng)絡(luò)請(qǐng)求之前,需要先喚醒設(shè)備,然后發(fā)送數(shù)據(jù)請(qǐng)求,之后等待返回?cái)?shù)據(jù),最后才慢慢進(jìn)入休眠狀態(tài)。這個(gè)流程如下圖所示:



      android_perf_2_network_request_mode






      在上面那個(gè)流程中,蜂窩模塊的電量消耗差異如下圖所示:



      android_perf_2_battery_drain_mode



      從圖示中可以看到,激活瞬間,發(fā)送數(shù)據(jù)的瞬間,接收數(shù)據(jù)的瞬間都有很大的電量消耗,所以,我們應(yīng)該從如何傳遞網(wǎng)絡(luò)數(shù)據(jù)以及何時(shí)發(fā)起網(wǎng)絡(luò)請(qǐng)求這兩個(gè)方面來(lái)著手優(yōu)化。



      1.1)何時(shí)發(fā)起網(wǎng)絡(luò)請(qǐng)求



      首先我們需要區(qū)分哪些網(wǎng)絡(luò)請(qǐng)求是需要及時(shí)返回結(jié)果的,哪些是可以延遲執(zhí)行的。例如,用戶主動(dòng)下拉刷新列表,這種行為需要立即觸發(fā)網(wǎng)絡(luò)請(qǐng)求,并等待數(shù)據(jù)返回。但是對(duì)于上傳用戶操作的數(shù)據(jù),同步程序設(shè)置等等行為則屬于可以延遲的行為。我們可以通過(guò)Battery Historian這個(gè)工具來(lái)查看關(guān)于移動(dòng)蜂窩模塊的電量消耗(關(guān)于這部分的細(xì)節(jié),請(qǐng)點(diǎn)擊Android性能優(yōu)化之電量篇)。在Mobile Radio那一行會(huì)顯示蜂窩模塊的電量消耗情況,紅色的部分代表模塊正在工作,中間的間隔部分代表模塊正在休眠狀態(tài),如果看到有一段區(qū)間,紅色與間隔頻繁的出現(xiàn),那就說(shuō)明這里有可以優(yōu)化的行為。如下圖所示:



      android_perf_2_battery_mobile_radio



      對(duì)于上面可以優(yōu)化的部分,我們可以有針對(duì)性的把請(qǐng)求行為捆綁起來(lái),延遲到某個(gè)時(shí)刻統(tǒng)一發(fā)起請(qǐng)求。如下圖所示:



      android_perf_2_battery_batch_delay



      經(jīng)過(guò)上面的優(yōu)化之后,我們?cè)倩仡^使用Battery Historian導(dǎo)出電量消耗圖,可以看到喚醒狀態(tài)與休眠狀態(tài)是連續(xù)大塊間隔的,這樣的話,總體電量的消耗就會(huì)變得更少。



      android_perf_2_battery_mobile_radio_2



      當(dāng)然,我們甚至可以把請(qǐng)求的任務(wù)延遲到手機(jī)網(wǎng)絡(luò)切換到WiFi,手機(jī)處于充電狀態(tài)下再執(zhí)行。在前面的描述過(guò)程中,我們會(huì)遇到的一個(gè)難題是如何把網(wǎng)絡(luò)請(qǐng)求延遲,并批量進(jìn)行執(zhí)行。還好,Android提供了JobScheduler來(lái)幫助我們達(dá)成這個(gè)目標(biāo)。



      1.2)如何傳遞網(wǎng)絡(luò)數(shù)據(jù)



      關(guān)于這部分主要會(huì)涉及到Prefetch(預(yù)取)與Compressed(壓縮)這兩個(gè)技術(shù)。對(duì)于Prefetch的使用,我們需要預(yù)先判斷用戶在此次操作之后,后續(xù)零散的請(qǐng)求是否很有可能會(huì)馬上被觸發(fā),可以把后面5分鐘有可能會(huì)使用到的零散請(qǐng)求都一次集中執(zhí)行完畢。對(duì)于Compressed的使用,在上傳與下載數(shù)據(jù)之前,使用CPU對(duì)數(shù)據(jù)進(jìn)行壓縮與解壓,可以很大程度上減少網(wǎng)絡(luò)傳輸?shù)臅r(shí)間。



      想要知道我們的應(yīng)用程序中網(wǎng)絡(luò)請(qǐng)求發(fā)生的時(shí)間,每次請(qǐng)求的數(shù)據(jù)量等等信息,可以通過(guò)Android Studio中的Networking Traffic Tool來(lái)查看詳細(xì)的數(shù)據(jù),如下圖所示:



      android_perf_2_battery_network_tracking



      2)Wear & Sensors



      在Android Wear上會(huì)大量的使用Sensors來(lái)實(shí)現(xiàn)某些特殊功能,如何在盡量節(jié)約電量的前提下利用好Sensor會(huì)是我們需要特別注意的問(wèn)題。下面會(huì)介紹一些在Android Wear上的最佳實(shí)踐典范。



      盡量減少刷新請(qǐng)求,例如我們可以在不需要某些數(shù)據(jù)的時(shí)候盡快注銷監(jiān)聽(tīng),減小刷新頻率,對(duì)Sensor的數(shù)據(jù)做批量處理等等。那么如何做到這些優(yōu)化呢?




      • 首先我們需要盡量使用Android平臺(tái)提供的既有運(yùn)動(dòng)數(shù)據(jù),而不是自己去實(shí)現(xiàn)監(jiān)聽(tīng)采集數(shù)據(jù),因?yàn)榇蠖鄶?shù)Android Watch自身記錄Sensor數(shù)據(jù)的行為是有經(jīng)過(guò)做電量?jī)?yōu)化的。

      • 其次在Activity不需要監(jiān)聽(tīng)某些Sensor數(shù)據(jù)的時(shí)候需要盡快釋放監(jiān)聽(tīng)注冊(cè)。

      • 還有我們需要盡量控制更新的頻率,僅僅在需要刷新顯示數(shù)據(jù)的時(shí)候才觸發(fā)獲取最新數(shù)據(jù)的操作。

      • 另外我們可以針對(duì)Sensor的數(shù)據(jù)做批量處理,待數(shù)據(jù)累積一定次數(shù)或者某個(gè)程度的時(shí)候才更新到UI上。

      • 最后當(dāng)Watch與Phone連接起來(lái)的時(shí)候,可以把某些復(fù)雜操作的事情交給Phone來(lái)執(zhí)行,Watch只需要等待返回的結(jié)果。




      更對(duì)關(guān)于Sensors的知識(shí),可以點(diǎn)擊這里



      3)Smooth Android Wear Animation



      Android Material Design風(fēng)格的應(yīng)用采用了大量的動(dòng)畫(huà)來(lái)進(jìn)行UI切換,優(yōu)化動(dòng)畫(huà)的性能不僅能夠提升用戶體驗(yàn)還可以減少電量的消耗,下面會(huì)介紹一些簡(jiǎn)單易行的方法。



      在Android里面一個(gè)相對(duì)操作比較繁重的事情是對(duì)Bitmap進(jìn)行旋轉(zhuǎn),縮放,裁剪等等。例如在一個(gè)圓形的鐘表圖上,我們把時(shí)鐘的指針摳出來(lái)當(dāng)做單獨(dú)的圖片進(jìn)行旋轉(zhuǎn)會(huì)比旋轉(zhuǎn)一張完整的圓形圖的所形成的幀率要高56%。



      android_perf_2_waer_animation



      另外盡量減少每次重繪的元素可以極大的提升性能,假如某個(gè)鐘表界面上有很多需要顯示的復(fù)雜組件,我們可以把這些組件做拆分處理,例如把背景圖片單獨(dú)拎出來(lái)設(shè)置為一個(gè)獨(dú)立的View,通過(guò)setLayerType()方法使得這個(gè)View強(qiáng)制用Hardware來(lái)進(jìn)行渲染。至于界面上哪些元素需要做拆分,他們各自的更新頻率是多少,需要有針對(duì)性的單獨(dú)討論。



      如何使用Systrace等工具來(lái)查看某些View的渲染性能,在前面的章節(jié)里面有提到過(guò),感興趣的可以點(diǎn)擊這里



      對(duì)于大多數(shù)應(yīng)用中的動(dòng)畫(huà),我們會(huì)使用PropertyAnimation或者ViewAnimation來(lái)操作實(shí)現(xiàn),Android系統(tǒng)會(huì)自動(dòng)對(duì)這些Animation做一定的優(yōu)化處理,在Android上面學(xué)習(xí)到的大多數(shù)性能優(yōu)化的知識(shí)同樣也適用于Android Wear。



      想要獲取更多關(guān)于Android Wear中動(dòng)畫(huà)效果的優(yōu)化,請(qǐng)點(diǎn)擊WatchFace這個(gè)范例。



      4)Android Wear Data Batching



      在Android Training里面有關(guān)于Wear上面如何利用Wearable API與Phone進(jìn)行溝通協(xié)作的課程(詳情請(qǐng)點(diǎn)擊這里)。因?yàn)镻hone的CPU與電量都比Wear要強(qiáng)大,另外Phone還可以直接接入網(wǎng)絡(luò),而Wear要接入網(wǎng)絡(luò)則相對(duì)更加困難,所以我們?cè)陂_(kāi)發(fā)Wear應(yīng)用的時(shí)候需要盡量做到把復(fù)雜的操作交給Phone來(lái)執(zhí)行。例如我們可以讓Phone來(lái)獲取天氣信息,然后把數(shù)據(jù)返回Wear進(jìn)行顯示。更進(jìn)一步,在之前的性能優(yōu)化課程里面我們有學(xué)習(xí)過(guò)如何使用JobScheduler來(lái)延遲批量處理任務(wù),假設(shè)Phone收到來(lái)自Wear的其中一個(gè)任務(wù)是每隔5分鐘檢查一次天氣情況,那么Phone使用JobScheduler執(zhí)行檢查天氣任務(wù)之后,先判斷這次返回的結(jié)果和之前是否有差異,僅僅當(dāng)天氣發(fā)生變化的時(shí)候,才有必要把結(jié)果通知到Wear,或者僅僅把變化的某一項(xiàng)數(shù)據(jù)通知給Wear,這樣可以更大程度上減少Wear的電量消耗。



      下面我們總結(jié)一下如何優(yōu)化Wear的性能與電量:




      • 僅僅在真正需要刷新界面的時(shí)候才發(fā)出請(qǐng)求

      • 盡量把計(jì)算復(fù)雜操作的任務(wù)交給Phone來(lái)處理

      • Phone僅僅在數(shù)據(jù)發(fā)生變化的時(shí)候才通知到Wear

      • 把零碎的數(shù)據(jù)請(qǐng)求捆綁一起再進(jìn)行操作




      5)Object Pools



      在程序里面經(jīng)常會(huì)遇到的一個(gè)問(wèn)題是短時(shí)間內(nèi)創(chuàng)建大量的對(duì)象,導(dǎo)致內(nèi)存緊張,從而觸發(fā)GC導(dǎo)致性能問(wèn)題。對(duì)于這個(gè)問(wèn)題,我們可以使用對(duì)象池技術(shù)來(lái)解決它。通常對(duì)象池中的對(duì)象可能是bitmaps,views,paints等等。關(guān)于對(duì)象池的操作原理,不展開(kāi)述說(shuō)了,請(qǐng)看下面的圖示:



      android_perf_2_object_pool



      使用對(duì)象池技術(shù)有很多好處,它可以避免內(nèi)存抖動(dòng),提升性能,但是在使用的時(shí)候有一些內(nèi)容是需要特別注意的。通常情況下,初始化的對(duì)象池里面都是空白的,當(dāng)使用某個(gè)對(duì)象的時(shí)候先去對(duì)象池查詢是否存在,如果不存在則創(chuàng)建這個(gè)對(duì)象然后加入對(duì)象池,但是我們也可以在程序剛啟動(dòng)的時(shí)候就事先為對(duì)象池填充一些即將要使用到的數(shù)據(jù),這樣可以在需要使用到這些對(duì)象的時(shí)候提供更快的首次加載速度,這種行為就叫做預(yù)分配。使用對(duì)象池也有不好的一面,程序員需要手動(dòng)管理這些對(duì)象的分配與釋放,所以我們需要慎重地使用這項(xiàng)技術(shù),避免發(fā)生對(duì)象的內(nèi)存泄漏。為了確保所有的對(duì)象能夠正確被釋放,我們需要保證加入對(duì)象池的對(duì)象和其他外部對(duì)象沒(méi)有互相引用的關(guān)系。



      6)To Index or Iterate?



      遍歷容器是編程里面一個(gè)經(jīng)常遇到的場(chǎng)景。在Java語(yǔ)言中,使用Iterate是一個(gè)比較常見(jiàn)的方法??墒窃贏ndroid開(kāi)發(fā)團(tuán)隊(duì)中,大家卻盡量避免使用Iterator來(lái)執(zhí)行遍歷操作。下面我們看下在Android上可能用到的三種不同的遍歷方法:



      android_perf_2_iterate_1



      android_perf_2_iterate_for_loop



      android_perf_2_iterate_simple_loop



      使用上面三種方式在同一臺(tái)手機(jī)上,使用相同的數(shù)據(jù)集做測(cè)試,他們的表現(xiàn)性能如下所示:



      android_perf_2_iterate_result



      從上面可以看到for index的方式有更好的效率,但是因?yàn)椴煌脚_(tái)編譯器優(yōu)化各有差異,我們最好還是針對(duì)實(shí)際的方法做一下簡(jiǎn)單的測(cè)量比較好,拿到數(shù)據(jù)之后,再選擇效率最高的那個(gè)方式。



      7)The Magic of LRU Cache



      這小節(jié)我們要討論的是緩存算法,在Android上面最常用的一個(gè)緩存算法是LRU(Least Recently Use),關(guān)于LRU算法,不展開(kāi)述說(shuō),用下面一張圖演示下含義:



      android_perf_2_lru_mode



      LRU Cache的基礎(chǔ)構(gòu)建用法如下:



      android_perf_2_lru_key_value



      為了給LRU Cache設(shè)置一個(gè)比較合理的緩存大小值,我們通常是用下面的方法來(lái)做界定的:



      android_perf_2_lru_size



      使用LRU Cache時(shí)為了能夠讓Cache知道每個(gè)加入的Item的具體大小,我們需要Override下面的方法:



      android_perf_2_lru_sizeof



      使用LRU Cache能夠顯著提升應(yīng)用的性能,可是也需要注意LRU Cache中被淘汰對(duì)象的回收,否者會(huì)引起嚴(yán)重的內(nèi)存泄露。



      8)Using LINT for Performance Tips



      Lint是Android提供的一個(gè)靜態(tài)掃描應(yīng)用源碼并找出其中的潛在問(wèn)題的一個(gè)強(qiáng)大的工具。



      android_perf_2_lint_overview



      例如,如果我們?cè)趏nDraw方法里面執(zhí)行了new對(duì)象的操作,Lint就會(huì)提示我們這里有性能問(wèn)題,并提出對(duì)應(yīng)的建議方案。Lint已經(jīng)集成到Android Studio中了,我們可以手動(dòng)去觸發(fā)這個(gè)工具,點(diǎn)擊工具欄的Analysis -> Inspect Code,觸發(fā)之后,Lint會(huì)開(kāi)始工作,并把結(jié)果輸出到底部的工具欄,我們可以逐個(gè)查看原因并根據(jù)指示做相應(yīng)的優(yōu)化修改。



      Lint的功能非常強(qiáng)大,他能夠掃描各種問(wèn)題。當(dāng)然我們可以通過(guò)Android Studio設(shè)置找到Lint,對(duì)Lint做一些定制化掃描的設(shè)置,可以選擇忽略掉那些不想Lint去掃描的選項(xiàng),我們還可以針對(duì)部分掃描內(nèi)容修改它的提示優(yōu)先級(jí)。



      建議把與內(nèi)存有關(guān)的選項(xiàng)中的嚴(yán)重程度標(biāo)記為紅色的Error,對(duì)于Layout的性能問(wèn)題標(biāo)記為黃色Warning。



      9)Hidden Cost of Transparency



      這小節(jié)會(huì)介紹如何減少透明區(qū)域?qū)π阅艿挠绊?。通常?lái)說(shuō),對(duì)于不透明的View,顯示它只需要渲染一次即可,可是如果這個(gè)View設(shè)置了alpha值,會(huì)至少需要渲染兩次。原因是包含alpha的view需要事先知道混合View的下一層元素是什么,然后再結(jié)合上層的View進(jìn)行Blend混色處理。



      在某些情況下,一個(gè)包含alpha的View有可能會(huì)觸發(fā)改View在HierarchyView上的父View都被額外重繪一次。下面我們看一個(gè)例子,下圖演示的ListView中的圖片與二級(jí)標(biāo)題都有設(shè)置透明度。



      android_perf_2_trans_listview



      大多數(shù)情況下,屏幕上的元素都是由后向前進(jìn)行渲染的。在上面的圖示中,會(huì)先渲染背景圖(藍(lán),綠,紅),然后渲染人物頭像圖。如果后渲染的元素有設(shè)置alpha值,那么這個(gè)元素就會(huì)和屏幕上已經(jīng)渲染好的元素做blend處理。很多時(shí)候,我們會(huì)給整個(gè)View設(shè)置alpha的來(lái)達(dá)到fading的動(dòng)畫(huà)效果,如果我們圖示中的ListView做alpha逐漸減小的處理,我們可以看到ListView上的TextView等等組件會(huì)逐漸融合到背景色上。但是在這個(gè)過(guò)程中,我們無(wú)法觀察到它其實(shí)已經(jīng)觸發(fā)了額外的繪制任務(wù),我們的目標(biāo)是讓整個(gè)View逐漸透明,可是期間ListView在不停的做Blending的操作,這樣會(huì)導(dǎo)致不少性能問(wèn)題。



      如何渲染才能夠得到我們想要的效果呢?我們可以先按照通常的方式把View上的元素按照從后到前的方式繪制出來(lái),但是不直接顯示到屏幕上,而是使用GPU預(yù)處理之后,再又GPU渲染到屏幕上,GPU可以對(duì)界面上的原始數(shù)據(jù)直接做旋轉(zhuǎn),設(shè)置透明度等等操作。使用GPU進(jìn)行渲染,雖然第一次操作相比起直接繪制到屏幕上更加耗時(shí),可是一旦原始紋理數(shù)據(jù)生成之后,接下去的操作就比較省時(shí)省力。



      android_perf_2_trans_hw_layer



      如何才能夠讓GPU來(lái)渲染某個(gè)View呢?我們可以通過(guò)setLayerType的方法來(lái)指定View應(yīng)該如何進(jìn)行渲染,從SDK 16開(kāi)始,我們還可以使用ViewPropertyAnimator.alpha().withLayer()來(lái)指定。如下圖所示:



      android_perf_2_trans_setlayertype



      另外一個(gè)例子是包含陰影區(qū)域的View,這種類型的View并不會(huì)出現(xiàn)我們前面提到的問(wèn)題,因?yàn)樗麄儾⒉淮嬖趯盈B的關(guān)系。



      android_perf_2_trans_overlap



      為了能夠讓渲染器知道這種情況,避免為這種View占用額外的GPU內(nèi)存空間,我們可以做下面的設(shè)置。



      android_perf_2_trans_override_lap



      通過(guò)上面的設(shè)置以后,性能可以得到顯著的提升,如下圖所示:



      android_perf_2_trans_overlap_compare



      10)Avoiding Allocations in onDraw()



      我們都知道應(yīng)該避免在onDraw()方法里面執(zhí)行導(dǎo)致內(nèi)存分配的操作,下面講解下為何需要這樣做。



      首先onDraw()方法是執(zhí)行在UI線程的,在UI線程盡量避免做任何可能影響到性能的操作。雖然分配內(nèi)存的操作并不需要花費(fèi)太多系統(tǒng)資源,但是這并不意味著是免費(fèi)無(wú)代價(jià)的。設(shè)備有一定的刷新頻率,導(dǎo)致View的onDraw方法會(huì)被頻繁的調(diào)用,如果onDraw方法效率低下,在頻繁刷新累積的效應(yīng)下,效率低的問(wèn)題會(huì)被擴(kuò)大,然后會(huì)對(duì)性能有嚴(yán)重的影響。



      android_perf_2_ondraw_gc



      如果在onDraw里面執(zhí)行內(nèi)存分配的操作,會(huì)容易導(dǎo)致內(nèi)存抖動(dòng),GC頻繁被觸發(fā),雖然GC后來(lái)被改進(jìn)為執(zhí)行在另外一個(gè)后臺(tái)線程(GC操作在2.3以前是同步的,之后是并發(fā)),可是頻繁的GC的操作還是會(huì)影響到CPU,影響到電量的消耗。



      那么簡(jiǎn)單解決頻繁分配內(nèi)存的方法就是把分配操作移動(dòng)到onDraw()方法外面,通常情況下,我們會(huì)把onDraw()里面new Paint的操作移動(dòng)到外面,如下面所示:



      android_perf_2_ondraw_paint



      11)Tool: Strict Mode



      UI線程被阻塞超過(guò)5秒,就會(huì)出現(xiàn)ANR,這太糟糕了。防止程序出現(xiàn)ANR是很重要的事情,那么如何找出程序里面潛在的坑,預(yù)防ANR呢?很多大部分情況下執(zhí)行很快的方法,但是他們有可能存在巨大的隱患,這些隱患的爆發(fā)就很容易導(dǎo)致ANR。



      Android提供了一個(gè)叫做Strict Mode的工具,我們可以通過(guò)手機(jī)設(shè)置里面的開(kāi)發(fā)者選項(xiàng),打開(kāi)Strict Mode選項(xiàng),如果程序存在潛在的隱患,屏幕就會(huì)閃現(xiàn)紅色。我們也可以通過(guò)StrictMode API在代碼層面做細(xì)化的跟蹤,可以設(shè)置StrictMode監(jiān)聽(tīng)那些潛在問(wèn)題,出現(xiàn)問(wèn)題時(shí)如何提醒開(kāi)發(fā)者,可以對(duì)屏幕閃紅色,也可以輸出錯(cuò)誤日志。下面是官方的代碼示例:



      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      
      public void onCreate() {
           if (DEVELOPER_MODE) {
               StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                       .detectDiskReads()
                       .detectDiskWrites()
                       .detectNetwork()   // or .detectAll() for all detectable problems
                       .penaltyLog()
                       .build());
               StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                       .detectLeakedSqlLiteObjects()
                       .detectLeakedClosableObjects()
                       .penaltyLog()
                       .penaltyDeath()
                       .build());
           }
           super.onCreate();
      }
      



      12)Custom Views and Performance



      Android系統(tǒng)有提供超過(guò)70多種標(biāo)準(zhǔn)的View,例如TextView,ImageView,Button等等。在某些時(shí)候,這些標(biāo)準(zhǔn)的View無(wú)法滿足我們的需要,那么就需要我們自己來(lái)實(shí)現(xiàn)一個(gè)View,這節(jié)會(huì)介紹如何優(yōu)化自定義View的性能。



      通常來(lái)說(shuō),針對(duì)自定義View,我們可能犯下面三個(gè)錯(cuò)誤:




      • Useless calls to onDraw():我們知道調(diào)用View.invalidate()會(huì)觸發(fā)View的重繪,有兩個(gè)原則需要遵守,第1個(gè)是僅僅在View的內(nèi)容發(fā)生改變的時(shí)候才去觸發(fā)invalidate方法,第2個(gè)是盡量使用ClipRect等方法來(lái)提高繪制的性能。

      • Useless pixels:減少繪制時(shí)不必要的繪制元素,對(duì)于那些不可見(jiàn)的元素,我們需要盡量避免重繪。

      • Wasted CPU cycles:對(duì)于不在屏幕上的元素,可以使用Canvas.quickReject把他們給剔除,避免浪費(fèi)CPU資源。另外盡量使用GPU來(lái)進(jìn)行UI的渲染,這樣能夠極大的提高程序的整體表現(xiàn)性能。




      最后請(qǐng)時(shí)刻牢記,盡量提高View的繪制性能,這樣才能保證界面的刷新幀率盡量的高。更多關(guān)于這部分的內(nèi)容,可以看這里



      13)Batching Background Work Until Later



      優(yōu)化性能時(shí)大多數(shù)時(shí)候討論的都是如何減少不必要的操作,但是選擇何時(shí)去執(zhí)行某些操作同樣也很重要。在第1季以及上一期的性能優(yōu)化之電量篇里面,我們有提到過(guò)移動(dòng)蜂窩模塊的電量消耗模型。為了避免我們的應(yīng)用程序過(guò)多的頻繁消耗電量,我們需要學(xué)習(xí)如何把后臺(tái)任務(wù)打包批量,并選擇一個(gè)合適的時(shí)機(jī)進(jìn)行觸發(fā)執(zhí)行。下圖是每個(gè)應(yīng)用程序各自執(zhí)行后臺(tái)任務(wù)導(dǎo)致的電量消耗示意圖:



      android_perf_2_batching_bg_1



      因?yàn)橄裆厦婺菢幼鰰?huì)導(dǎo)致浪費(fèi)很多電量,我們需要做的是把部分應(yīng)用的任務(wù)延遲處理,等到一定時(shí)機(jī),這些任務(wù)一并進(jìn)行處理。結(jié)果如下面的示意圖:



      android_perf_2_batching_bg_2



      執(zhí)行延遲任務(wù),通常有下面三種方式:



      1)AlarmManager



      使用AlarmManager設(shè)置定時(shí)任務(wù),可以選擇精確的間隔時(shí)間,也可以選擇非精確時(shí)間作為參數(shù)。除非程序有很強(qiáng)烈的需要使用精確的定時(shí)喚醒,否者一定要避免使用他,我們應(yīng)該盡量使用非精確的方式。



      2)SyncAdapter



      我們可以使用SyncAdapter為應(yīng)用添加設(shè)置賬戶,這樣在手機(jī)設(shè)置的賬戶列表里面可以找到我們的應(yīng)用。這種方式功能更多,但是實(shí)現(xiàn)起來(lái)比較復(fù)雜。我們可以從這里看到官方的培訓(xùn)課程:http://developer./training/sync-adapters/index.html



      3)JobSchedulor



      這是最簡(jiǎn)單高效的方法,我們可以設(shè)置任務(wù)延遲的間隔,執(zhí)行條件,還可以增加重試機(jī)制。



      14)Smaller Pixel Formats



      常見(jiàn)的png,jpeg,webp等格式的圖片在設(shè)置到UI上之前需要經(jīng)過(guò)解碼的過(guò)程,而解壓時(shí)可以選擇不同的解碼率,不同的解碼率對(duì)內(nèi)存的占用是有很大差別的。在不影響到畫(huà)質(zhì)的前提下盡量減少內(nèi)存的占用,這能夠顯著提升應(yīng)用程序的性能。



      Android的Heap空間是不會(huì)自動(dòng)做兼容壓縮的,意思就是如果Heap空間中的圖片被收回之后,這塊區(qū)域并不會(huì)和其他已經(jīng)回收過(guò)的區(qū)域做重新排序合并處理,那么當(dāng)一個(gè)更大的圖片需要放到heap之前,很可能找不到那么大的連續(xù)空閑區(qū)域,那么就會(huì)觸發(fā)GC,使得heap騰出一塊足以放下這張圖片的空閑區(qū)域,如果無(wú)法騰出,就會(huì)發(fā)生OOM。如下圖所示:



      android_perf_2_pixel_heap_free



      所以為了避免加載一張超大的圖片,需要盡量減少這張圖片所占用的內(nèi)存大小,Android為圖片提供了4種解碼格式,他們分別占用的內(nèi)存大小如下圖所示:



      android_perf_2_pixel_format



      隨著解碼占用內(nèi)存大小的降低,清晰度也會(huì)有損失。我們需要針對(duì)不同的應(yīng)用場(chǎng)景做不同的處理,大圖和小圖可以采用不同的解碼率。在Android里面可以通過(guò)下面的代碼來(lái)設(shè)置解碼率:



      android_perf_2_pixel_decode



      15)Smaller PNG Files



      盡量減少PNG圖片的大小是Android里面很重要的一條規(guī)范。相比起JPEG,PNG能夠提供更加清晰無(wú)損的圖片,但是PNG格式的圖片會(huì)更大,占用更多的磁盤空間。到底是使用PNG還是JPEG,需要設(shè)計(jì)師仔細(xì)衡量,對(duì)于那些使用JPEG就可以達(dá)到視覺(jué)效果的,可以考慮采用JPEG即可。我們可以通過(guò)Google搜索到很多關(guān)于PNG壓縮的工具,如下圖所示:



      android_perf_2_png_tools



      這里要介紹一種新的圖片格式:Webp,它是由Google推出的一種既保留png格式的優(yōu)點(diǎn),又能夠減少圖片大小的一種新型圖片格式。關(guān)于Webp的更多細(xì)節(jié),請(qǐng)點(diǎn)擊這里



      16)Pre-scaling Bitmaps



      對(duì)bitmap做縮放,這也是Android里面最遇到的問(wèn)題。對(duì)bitmap做縮放的意義很明顯,提示顯示性能,避免分配不必要的內(nèi)存。Android提供了現(xiàn)成的bitmap縮放的API,叫做createScaledBitmap(),使用這個(gè)方法可以獲取到一張經(jīng)過(guò)縮放的圖片。



      android_perf_2_sacle_bitmap_created



      上面的方法能夠快速的得到一張經(jīng)過(guò)縮放的圖片,可是這個(gè)方法能夠執(zhí)行的前提是,原圖片需要事先加載到內(nèi)存中,如果原圖片過(guò)大,很可能導(dǎo)致OOM。下面介紹其他幾種縮放圖片的方式。



      inSampleSize能夠等比的縮放顯示圖片,同時(shí)還避免了需要先把原圖加載進(jìn)內(nèi)存的缺點(diǎn)。我們會(huì)使用類似像下面一樣的方法來(lái)縮放bitmap:



      android_perf_2_sacle_bitmap_code



      android_perf_2_sacle_bitmap_insamplesize



      另外,我們還可以使用inScaled,inDensity,inTargetDensity的屬性來(lái)對(duì)解碼圖片做處理,源碼如下圖所示:



      android_perf_2_sacle_bitmap_inscale



      還有一個(gè)經(jīng)常使用到的技巧是inJustDecodeBounds,使用這個(gè)屬性去嘗試解碼圖片,可以事先獲取到圖片的大小而不至于占用什么內(nèi)存。如下圖所示:



      android_perf_2_sacle_bitmap_injust



      17)Re-using Bitmaps



      我們知道bitmap會(huì)占用大量的內(nèi)存空間,這節(jié)會(huì)講解什么是inBitmap屬性,如何利用這個(gè)屬性來(lái)提升bitmap的循環(huán)效率。前面我們介紹過(guò)使用對(duì)象池的技術(shù)來(lái)解決對(duì)象頻繁創(chuàng)建再回收的效率問(wèn)題,使用這種方法,bitmap占用的內(nèi)存空間會(huì)差不多是恒定的數(shù)值,每次新創(chuàng)建出來(lái)的bitmap都會(huì)需要占用一塊單獨(dú)的內(nèi)存區(qū)域,如下圖所示:



      android_perf_2_inbitmap_old



      為了解決上圖所示的效率問(wèn)題,Android在解碼圖片的時(shí)候引進(jìn)了inBitmap屬性,使用這個(gè)屬性可以得到下圖所示的效果:



      android_perf_2_inbitmap_new



      使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經(jīng)存在的內(nèi)存區(qū)域,新解碼的bitmap會(huì)嘗試去使用之前那張bitmap在heap中所占據(jù)的pixel data內(nèi)存區(qū)域,而不是去問(wèn)內(nèi)存重新申請(qǐng)一塊區(qū)域來(lái)存放bitmap。利用這種特性,即使是上千張的圖片,也只會(huì)僅僅只需要占用屏幕所能夠顯示的圖片數(shù)量的內(nèi)存大小。下面是如何使用inBitmap的代碼示例:



      android_perf_2_inbitmap_code



      使用inBitmap需要注意幾個(gè)限制條件:




      • 在SDK 11 -> 18之間,重用的bitmap大小必須是一致的,例如給inBitmap賦值的圖片大小為100-100,那么新申請(qǐng)的bitmap必須也為100-100才能夠被重用。從SDK 19開(kāi)始,新申請(qǐng)的bitmap大小必須小于或者等于已經(jīng)賦值過(guò)的bitmap大小。

      • 新申請(qǐng)的bitmap與舊的bitmap必須有相同的解碼格式,例如大家都是8888的,如果前面的bitmap是8888,那么就不能支持4444與565格式的bitmap了。




      我們可以創(chuàng)建一個(gè)包含多種典型可重用bitmap的對(duì)象池,這樣后續(xù)的bitmap創(chuàng)建都能夠找到合適的“模板”去進(jìn)行重用。如下圖所示:



      android_perf_2_inbitmap_pool



      Google介紹了一個(gè)開(kāi)源的加載bitmap的庫(kù):Glide,這里面包含了各種對(duì)bitmap的優(yōu)化技巧。



      18)The Performance Lifecycle



      大多數(shù)開(kāi)發(fā)者在沒(méi)有發(fā)現(xiàn)嚴(yán)重性能問(wèn)題之前是不會(huì)特別花精力去關(guān)注性能優(yōu)化的,通常大家關(guān)注的都是功能是否實(shí)現(xiàn)。當(dāng)性能問(wèn)題真的出現(xiàn)的時(shí)候,請(qǐng)不要慌亂。我們通常采用下面三個(gè)步驟來(lái)解決性能問(wèn)題。



      Gather:收集數(shù)據(jù)



      我們可以通過(guò)Android SDK里面提供的諸多工具來(lái)收集CPU,GPU,內(nèi)存,電量等等性能數(shù)據(jù),



      Insight:分析數(shù)據(jù)



      通過(guò)上面的步驟,我們獲取到了大量的數(shù)據(jù),下一步就是分析這些數(shù)據(jù)。工具幫我們生成了很多可讀性強(qiáng)的表格,我們需要事先了解如何查看表格的數(shù)據(jù),每一項(xiàng)代表的含義,這樣才能夠快速定位問(wèn)題。如果分析數(shù)據(jù)之后還是沒(méi)有找到問(wèn)題,那么就只能不停的重新收集數(shù)據(jù),再進(jìn)行分析,如此循環(huán)。



      Action:解決問(wèn)題



      定位到問(wèn)題之后,我們需要采取行動(dòng)來(lái)解決問(wèn)題。解決問(wèn)題之前一定要先有個(gè)計(jì)劃,評(píng)估這個(gè)解決方案是否可行,是否能夠及時(shí)的解決問(wèn)題。



      19)Tools not Rules



      雖然前面介紹了很多調(diào)試的方法,處理技巧,規(guī)范建議等等,可是這并不意味著所有的情況都適用,我們還是需要根據(jù)當(dāng)時(shí)的情景做特定靈活的處理。



      20)Memory Profiling 101



      圍繞Android生態(tài)系統(tǒng),不僅僅有Phone,還有Wear,TV,Auto等等。對(duì)這些不同形態(tài)的程序進(jìn)行性能優(yōu)化,都離不開(kāi)內(nèi)存調(diào)試這個(gè)步驟。這節(jié)中介紹的內(nèi)容大部分和Android性能優(yōu)化典范Android性能優(yōu)化之內(nèi)存篇重合,不展開(kāi)了。



      首發(fā)于CSDN:Android性能優(yōu)化典范(二)

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多