公司的新需求終於解決完了,離測試和發佈還有段時間,第一次體驗了下沒需求沒bug的感受,真是舒爽~而後翻了翻有什麼能夠學的。無心翻到了Android後期發展的五大趨勢。1、性能優化。2、高級UI。3、JNI/NDK開發。4、架構師。5、RN開發。這也許將會是個人進階趨勢。早已知道在瓶頸期的我,彷佛看到了突破的但願的。html
其實,關注個人或者在羣裏的小夥伴也知道,UI那塊我問題不大。可是高級UI就有難度了。咱們先無論他,一個一個來。先從性能優化來。其實我是拒絕寫這篇文章的。爲何?性能優化的分類不少,一個分類寫一篇感受篇幅量很小,結合在一塊兒寫有感受很大。而我目前打算總體的整理一下。java
那麼咱們先分析下性能優化有那幾個方面:1、內存優化。2、UI優化(佈局優化和繪製優化)。3、速度的優化(線程優化/網絡優化)。4、電量優化。5、啓動優化。應該就這些了。那麼這只是五大方面,裏面還結合了各類細節方面的。不急,咱們下面一個個的介紹。android
關於性能優化咱們能夠不知道其餘的,但必定要知道內存優化。由於內存泄漏能夠Android的常客。那麼什麼是內存泄漏呢?內存不在GC的掌控範圍以內了。那麼java的GC內存回收機制是什麼?某對象不在有任何引用的時候纔會進行回收。那麼GC回收機制的原理是什麼?又或者說能夠做爲GC Root引用點的是啥?或許有人聽不懂我在講啥。咱們先來看張圖。面試
當咱們向上尋找,一直尋找到GC Root的時候,此對象不會進行回收,例如,一個Activity。那麼若是咱們向上尋找,直到找到GC Root對象的時候,就說明它是不能夠回收的,例如,我定義了一個int a;可是這個數據,我整個頁面或者說整個項目都沒有用到,則這個對象會被GC掉。shell
java棧中引用的對象緩存
方法靜態引用的對象性能優化
方法常量引用的對象bash
Native中JNI引用的對象微信
Thread——「活着的」線程網絡
那麼咱們如何判斷一個對象是一個垃圾對象,能夠講他進行回收呢?舉了小例子教大家如何區分:
通常在學校吃飯,咱們有兩種狀況,第一:吃完飯就直接走人,碗筷留給阿姨來收拾處理。
第二:吃完以後把碗筷放到收盤處直接進行回收。
但咱們是個有素質的人,通常採用第二種狀況,但根據想法,咱們更傾向於第一種。
那麼通常在飯店或者KFC中,都是第一種狀況。
那麼此時,問題來了,若是我已經吃完飯,而後我並無離開飯店,作在位置上和朋友吹吹牛逼,談談理想,聊聊人生。
那麼桌上那一堆碗筷是收仍是不收?講道理是不能收的。雖然實際也是不能收的。由於顧客是上帝~~~複製代碼
So,咱們如何判斷一個對象是一個可回收的垃圾對象呢?這是咱們的一個主觀的判斷。可是有種狀況咱們是必需要考慮到的,沒錯,就是內存過多沒法釋放的時候,會直接致使OOM。整個項目boom炸了。什麼鬼?outofmemory。沒錯就是它。
咱們須要分析內存溢出的緣由,咱們先來看一張圖:
內存泄漏通常致使應用卡頓,極端狀況會致使項目boom。Boom的緣由是由於超過內存的閾值。緣由主要有兩方面:
代碼存在泄漏,內存沒法及時釋放致使oom(這個咱們後面說)
一些邏輯消耗了大量內存,沒法及時釋放或者超過致使oom
所謂消耗大量的內存的,絕大多數是由於圖片加載。這是咱們oom出現最頻繁的地方。我前面有寫過圖片加載的方法,一個是控制每次加載的數量,第二,保證每次滑動的時候不進行加載,滑動完進行加載。通常狀況使用先進後出,而不是先進先出。不過通常咱們圖片加載都是使用fresco或者Glide等開源庫。咱們來看下下面兩張圖:
對比兩張圖,咱們能夠在第一張的狀況出現了oom狀況,咱們經過log打印發現,處理的好像沒什麼問題,換句話說,若是我不放那0.8M的圖片。而後繼續不停的操做一樣會出現OOM,然而咱們就蒙了。沒什麼圖片加載怎麼就這麼崩掉了。
首先,咱們肯定咱們項目或者某幾個類裏面是否存在內存溢出的問題。咱們能夠經過以下方法:
Android-->System Information-->MemoryUsage查看Object裏面是否有沒有被釋放的Views和Activity
命令行模式:adb shell dumpsys meminfo 包名 -d
就那我公司的項目舉例把。首先,咱們在這邊能夠看到memory。CPU和net的使用狀況。咱們找到Object。看看咱們內存的消耗狀況。
隨便這麼一看,尼瑪蛋,1300左右的view和一個Activity。還有3個context。可怕。。能夠理解爲一個Activity裏面使用了將近1300個view。。。想都不敢想。。。
咱們能夠經過看Memory Monitor工具。 檢查一個一個的動做。(好比Activity的跳轉)。反覆屢次執行某一個操做,不斷的經過這個工具查看內存的大概變化狀況。 先後兩個內存變化增長了很多。
咱們能夠更仔細的查找泄漏的位置,在AS裏面使用 Heap SnapShot工具(堆棧快照)。如圖所示:咱們點擊後,他會進行一段時間的監控,而後會生成一個文件。咱們點擊咱們package tree view。咱們找到本身項目的包名。而後進行進一步的分析。首先看一下2個列表的列名到底指的什麼。
實例化對象的詳細信息:
咱們來隨便的看一下內存中的數量:
這仍是咱們剛進手機,一個bean就被調用了這麼屢次。簡直可怕。這個咱們能夠經過內存分析工具解決的。
性能優化工具:
Heap SnapShot工具
Heap Viewer工具
LeakCanary工具
MAT工具
TraceView工具(Device Monitor)
第三方分析工具:
MemoryAnalyzer
GT Home
iTest
由於我沒有這些工具,沒法進行演示。
咱們儘可能不要使用Activity的上下文,而是使用application的上下文,由於application的生命週期長,進程退出時纔會被銷燬。因此,單例模式是最容易形成內存溢出的本來所在,由於單例模式的生命週期的應該和application的生命週期同樣長,而不是和Activity的相同。
Animation也會致使內存溢出,爲何?由於咱們是經過view來進行演示的,致使view被Activity持有,而Activity又持有view。最後由於Activity沒法釋放,致使內存泄漏。解決方法是在Activity的ondestory()方法中調用Animation.cancle()進行中止,固然一些簡單的動畫咱們能夠經過自定義view來解決。至少我如今已經不多使用Animation了。沒有一個動畫是自定義view解決不了的。如何有,那就是兩個~~~。
UI優化主要包括佈局優化以及view的繪製優化。不急,咱們接下來一個一個慢慢看~~。先說下UI的優化究竟是什麼?有些時候咱們打開某個軟件,會出現卡頓的狀況。這就是UI的問題。那麼咱們想一下,什麼狀況會致使卡頓呢?通常是以下幾種狀況:
人爲在UI線程中作輕微耗時操做,致使UI線程卡頓;
佈局Layout過於複雜,沒法在16ms內完成渲染;
同一時間動畫執行的次數過多,致使CPU或GPU負載太重;
View過分繪製,致使某些像素在同一幀時間內被繪製屢次,從而使CPU或GPU負載太重;
View頻繁的觸發measure、layout,致使measure、layout累計耗時過多及整個View頻繁的從新渲染;
內存頻繁觸發GC過多(同一幀中頻繁建立內存),致使暫時阻塞渲染操做;
冗餘資源及邏輯等致使加載和執行緩慢;
臭名昭著的ANR;
能夠看見,上面這些致使卡頓的緣由都是咱們平時開發中很是常見的。有些人可能會以爲本身的應用用着還蠻OK的,其實那是由於你沒進行一些瞬時測試和壓力測試,一旦在這種環境下運行你的App你就會發現不少性能問題。
咱們對於UI性能的優化還能夠經過開發者選項中的GPU過分繪製工具來進行分析。在設置->開發者選項->調試GPU過分繪製(不一樣設備可能位置或者叫法不一樣)中打開調試後能夠看見以下圖(對settings當前界面過分繪製進行分析):
這圖看着太亂,咱們來一張簡潔明瞭的圖:
咱們的目標就是儘可能減小紅色Overdraw,看到更多的藍色區域。
能夠發現,開啓後在咱們想要調試的應用界面中能夠看到各類顏色的區域,具體含義以下:
Overdraw有時候是由於你的UI佈局存在大量重疊的部分,還有的時候是由於非必須的重疊背景。例如某個Activity有一個背景,而後裏面的Layout又有本身的背景,同時子View又分別有本身的背景。僅僅是經過移除非必須的背景圖片,這就可以減小大量的紅色Overdraw區域,增長藍色區域的佔比。這一措施可以顯著提高程序性能。
若是佈局中既能採用RealtiveLayout和LinearLayout,那麼直接使用LinearLayout,由於Relativelayout的佈局比較複雜,繪製的時候須要花費更多的CPU時間。若是須要多個LinearLayout或者Framelayout嵌套,那麼可採用Relativelayout。由於多層嵌套致使佈局的繪製有大部分是重複的,這會減小程序的性能。
咱們依舊打開設置-->開發者選項-->GPU呈現模式分析-->在屏幕上顯示爲條形圖,如圖所示:
固然,也能夠在執行完UI滑動操做後在命令行輸入以下命令查看命令行打印的GPU渲染數據(分析依據:Draw + Process + Execute = 完整的顯示一幀時間 < 16ms):
adb shell dumpsys gfxinfo [應用包名]複製代碼
隨着界面的刷新,界面上會以實時柱狀圖來顯示每幀的渲染時間,柱狀圖越高表示渲染時間越長,每一個柱狀圖偏上都有一根表明16ms基準的綠色橫線,每一條豎着的柱狀線都包含三部分(藍色表明測量繪製Display List的時間,紅色表明OpenGL渲染Display List所須要的時間,黃色表明CPU等待GPU處理的時間),只要咱們每一幀的總時間低於基準線就不會發生UI卡頓問題(個別超出基準線其實也不算啥問題的)。就簡單的看下咱們公司項目剛啓動的時候:
忽然就有那麼一種想吐槽的感受.....我記得以前我作了瘦身的優化,可是要讓我作性能優化,我以爲應該沒那麼簡單........
Android Studio和IntellJ idead都有自帶的代碼檢查工具。打開Analyze->Run Inspection by Name… –>unused resource 點擊開始檢測,等待一下後會發現以下結果:
咱們還能夠這樣,將鼠標放在代碼區點擊右鍵->Analyze->Inspect Code–>界面選擇你要檢測的模塊->點擊確認開始檢測,等待一下後會發現以下結果:
固然,我這只是截取了少一部分,咱們看下下面那個提示:@param v tag description is missing 。意味着v的類型缺乏了,要麼補上介紹,要麼直接刪除。
上面那兩種方法是最容易找到代碼缺陷以及無用代碼的地方。因此盡情的入坑去填坑把~~~
那麼什麼是繪製優化?繪製優化主要是指View的Ondraw方法須要避免執行大量的操做。我將分爲了2個方面。
ondraw方法不須要建立新的局部對象,這是由於ondraw方法是實時執行的,這樣會產品大量的臨時對象,致使佔用了更多內存,而且使系統不斷的GC。下降了執行效率。
Ondraw方法不須要執行耗時操做,在ondraw方法裏少使用循環,由於循環會佔用CPU的時間。致使繪製不流暢,卡頓等等。Google官方指出,view的繪製幀率穩定在60dps,這要求每幀的繪製時間不超過16ms(1000/60)。雖然很難保證,但咱們須要儘量的下降。
60dps是目前最合適的圖像顯示速度,也是絕大部分Android設備設置的調試頻率,若是在16ms內順利完成界面刷新操做能夠展現出流暢的畫面,而因爲任何緣由致使接收到VSYNC信號的時候沒法完成本次刷新操做,就會產生掉幀的現象,刷新幀率天然也就跟着降低(假定刷新幀率由正常的60fps降到30fps,用戶就會明顯感知到卡頓)。So,前面咱們說GPU的時候也談到了這個。總的而言,感受仍是蠻重要的.....
線程是咱們項目中不可缺乏的重要部分,由於咱們大多數數據都是從網絡獲取的。So,線程這個是必備用品。咱們依舊能夠經過Memory下面的Net進行網絡的監聽:
相信這個問題在座的各類沒少遇到過,那麼什麼是ANR?application not responding。應用程序無響應。那麼通常何時會出現ANR。Android官方規定:activity若是5s內無響應事件(屏幕觸摸事件或者鍵盤輸入事件)。BroadcastReceiver若是在10s內沒法處理完成。Service若是20s內沒法處理完成。這三種狀況會致使ANR。用張簡潔的圖來介紹把。看起來方便~~
上面說的三種致使ANR的狀況,絕大多數就是由於線程阻塞致使的。那麼咱們應該如何處理呢?Android系統爲咱們提供了若干組工具類來解決此問題。
Asynctask:爲UI線程與工做線程之間進行快速處理的切換提供一種簡單便捷的機制。適用於當下當即須要啓動,可是異步執行的生命週期短暫的場景。
HandlerThread:爲某些回調方法或者等待某些執行任務的執行設置一個專屬的線程,並提供線程任務的調度機制。
ThreadPool:把任務分解成不一樣的單元,分發到各個不一樣的線程上,進行同時併發處理。
IntentService:適合執行由Ui觸發的後臺任務。並能夠把這些任務執行的狀況經過必定的機制反饋給UI。
網絡請求耗時會給用戶帶來卡頓的產品體驗,雖然可使用Loading提高用戶體驗,但屬於治標不治本。例如,當網絡差的時候咱們公司的項目一個loading就是10多s。甚至更多.....我就記得我當時面試以前下了一次咱們公司的項目,由於網差的問題...一個loading一分多鐘。。當時砸手機的衝動都有了,別說卸軟件了....
通常多線程的狀況咱們能夠經過Asynctask處理。(這玩意我真沒怎麼用過- -)我前面有說過annotation。這是google官方推出的註解。比bufferknife強大不少。這個能夠快捷方便的處理多線程並且不會致使線程阻塞,並且你也能夠控制線程的順序,例如我要執行完線程A後,根據線程A的某個參數來執行線程B。以此類推.....
至於線程池麼,最多的仍是要說道圖片加載了~~。圖片加載用三方就好了~想看詳細介紹,我前面有說,固然除了這個還有下載操做。這就和IntentService有關聯了。通常下載我不多涉及到。。用過幾回android原生的downloadmanager。。感受略坑。
如今講網絡優化的重點了...重點..重點...,通常用到網最最最主要的是什麼?時間!!速度!!成功率!!,時間!!速度!!成功率!!,時間!!速度!!成功率!!重要的事說三遍哈。
這已經不是第一次在此文提到圖片了。可見圖片的重要性!!
使用WebP格式;一樣的照片,採用WebP格式可大幅節省流量,相對於JPG格式的圖片,流量能節省將近 25% 到 35 %;相對於 PNG 格式的圖片,流量能夠節省將近80%。最重要的是使用WebP以後圖片質量也沒有改變。So,去和後臺的小夥伴們商量吧~~~
使用縮略圖,我在前面寫圖片加載有說過,就是控制他的inside和option。而後進行圖片縮放。壓縮?講道理....我並不知道網絡圖片怎麼壓縮,but,我會縮放啊~~反正也不會失真。啦啦啦~咬我啊?
咱們能夠對服務端返回數據進行緩存,設定有效時間,有效時間以內不走網絡請求,減小流量消耗。對網絡的緩存能夠參見HttpResponseCache。
在某些狀況,咱們儘可能少使用GPS定位,若是條件容許,儘量使用網絡定位。
下載、上傳,咱們儘量使用斷點,說個簡單的,我在公司,準備下一個500M的遊戲,可是下到200M的時候我下班了,此時沒有了無線網,咱們能夠回家後用無線繼續下載。So,斷點續傳,斷點下載也是咱們的必修課~,因此我前面單獨提了一篇斷點續傳的文章。
刷新數據時,儘量使用局部刷新,而不是全局刷新,第1、界面會閃屏一下,網差的界面直接白屏一段時間也不是不可能。第2、流量的使用!!我又要拿咱們公司項目搞事情了。一個閃屏的緩存60+M。。。沒錯,就是60+M。簡直可怕,我清個三、5次緩存,在打開個三、5次。好了,2分鐘時間,我一個月流量就沒了。。。So,我前面提到的網絡緩存很重要,至於會不會加在項目中,我仍是要看了在說- - 一個不當心,整個項目炸了都有可能。。。
衆所周知,一個好的產品,除了功能強大,好的性能也必不可少。有調查顯示,近50%的受訪者由於apk太大而拒絕使用,近40%的受訪者會由於APP性能差而卸載,性能也是形成APP用戶沮喪的頭號緣由。
安卓應用的啓動方式分爲三種:冷啓動、暖啓動、熱啓動,不一樣的啓動方式決定了應用UI對用戶可見所須要花費的時間長短。顧名思義,冷啓動消耗的時間最長。基於冷啓動方式的優化工做也是最考驗產品用戶體驗的地方。談及優化以前,咱們先看看這三種啓動方式的應用場景,以及啓動過程當中系統都作了些什麼工做。
爲何說冷啓動是耗時最長的。冷啓動是在啓動應用前,系統沒有獲取到當前app的activity、Service等等。例如,第一次啓動app。又或者說殺死進程後第一次啓動。那麼對比其餘兩種方式。冷啓動天然是耗時最久的。
應用發生冷啓動時,系統必定會執行下面的三個任務:
開始加載並啓動應用
應用啓動後,顯示一個空白的啓動窗口(啓動閃屏頁)
建立應用信息
那麼建立應用信息,系統就須要作一屁股的事:
application的初始化
啓動UI線程
建立Activity
導入視圖(inflate view)
計算視圖大小(onmesure view)
獲得視圖排版(onlayout view)
繪製視圖(ondraw view)
這其中有兩個 creation 工做,分別爲 Application 和 Activity creation。他們均在 View 繪製展現以前。因此,在應用自定義的 Application 類和 第一個 Activity 類中,onCreate() 方法作的事情越多,冷啓動消耗的時間越長。
當應用中的 Activities 被銷燬,但在內存中常駐時,應用的啓動方式就會變爲暖啓動。相比冷啓動,暖啓動過程減小了對象初始化、佈局加載等工做,啓動時間更短。但啓動時,系統依然會展現閃屏頁,直到第一個 Activity 的內容呈現爲止。
相比暖啓動,熱啓動時應用作的工做更少,啓動時間更短。熱啓動產生的場景不少,常見如:用戶使用返回鍵退出應用,而後立刻又從新啓動應用。
咱們先對比下三種啓動的時間對比:冷啓動:
暖啓動 :
熱啓動:
咱們能夠看到三者的明顯的差距,一個冷啓動將近一分鐘,反正我是不想看,每次跑項目都好慢~那麼咱們應該怎麼作?看到有些人介紹說改變項目的theme。把它改爲launcher的theme。但我以爲,這種作測試的確沒問題。可是通常項目都會有閃屏頁。而後從閃屏跳轉到首頁。咱們能夠按照大多數的項目來改善。怎麼說的,咱們能夠看到通常項目都有倒計時顯示。也就是說倒計時結束就自動進入首頁。或者能夠直接跳過進入首頁。也就是說咱們能夠經過此方法來進行,也就是說只要他倒計時結束,無論請求是否所有獲取完咱們都直接進入首頁。咱們能夠在閃屏頁進行一些必要的加載,例如用戶信息,定位等等,那麼至於其餘的,咱們能夠進入主頁進行預加載。就和熱更新同樣,在用戶不知情的狀況下,默默的更新bug。So,對於一些網絡請求,例如廣告之類的。咱們能夠經過此方法進行預加載。
咱們還能夠這樣,閃屏頁咱們把他看成一個fragment嵌套在MainActivity中,那麼咱們能夠在進入閃屏時直接預加載主頁的view。倒計時咱們把閃屏頁remove掉直接顯示首頁。
經過上面的介紹,咱們對啓動優化有了必定的瞭解,其實總結的話很簡單。就是減小耗時操做,總結以下:
主線程中涉及到Shareperference可否在非UI線程執行。
Application的建立過程當中儘可能少的進行耗時操做。
減小布局的層次,而且生命週期回調的方法中儘可能減小耗時的操做。
有了UI優化、內存優化、代碼優化、網絡優化以後咱們在來講說應用開發中很重要的一個優化模塊—–電量優化。
其實大多數開發者對電量優化的重視程度極低,其實提到性能優化想到的就是內存優化,但咱們不能忽視其餘的優化,電量優化其實仍是必要的,例如愛奇藝、優酷等等的視頻播放器以及音樂播放器。衆所周知,音樂和視頻實際上是耗電量最大的。若是用戶一旦發現咱們的應用很是耗電,很差意思,他們大多會選擇卸載來解決此類問題。爲此,咱們須要進行優化。
其實咱們把上面那四種優化解決了,就是最好的電量優化。So,對於電量優化,我在此提一些建議:
須要進行網絡請求時,咱們需先判斷網絡當前的狀態。
在多網絡請求的狀況下,最好進行批量處理,儘可能避免頻繁的間隔網絡請求。
在同時有wifi和移動數據的狀況下,咱們應該直接屏幕移動數據的網絡請求,只有當wifi斷開時在調用,由於,wifi請求的耗電量遠比移動數據的耗電量低的低。
後臺任務要儘量少的喚醒CPU。(比方說,鎖屏時,QQ的消息提示行就是喚醒了CPU。可是它的提示只有在你打開鎖屏或者進行充電時纔會進行提示。)
性能優化是咱們進階的畢竟之路。So,咱們必需要會,至於「會」到什麼程度,就要看我的理解了。其實,上面介紹的只是性能問題的冰山一角,真正的優化,咱們是在項目中總結出來的。但,咱們不能一味的追求優化,就例如我,如今只是在進行優化的總結,而對於真正的實行,並無開始,由於,優化是有風險的,一個不當心,整個項目均可能炸了。因此這就須要你的經驗,以及各類總結,在改進行優化的地方先進行優化,看看效果如何,例如,UI的優化以及代碼的優化。能夠先拿一些網上的開源項目進行優化等等。最後,盡情的享受優化把~~~
《Android藝術探索》
第一時間獲取本人的技術文章請關注微信公衆號!