標籤:
前幾天在G+上看到Google Developers網站,有一個Android系列的文章,分享到個人微博,周末閑來沒事就學寫了下,把它們簡單的翻譯了下,沒想到一發不可收拾,六篇文章全部都翻譯完了,有些地方省略了部分樣本的描述或者換了另一種表述,如果有理解的不準確的地方,還望指正
原文:Developing for Android, I:Understanding the Mobile Context
context或者這些建議為何如此重要
對於理解這些最佳實務的相關上下文是非常重要的。特別是明白移動端存在一些嚴重的限制,和台式機以及伺服器端的計算是完全不同的。因此,在開發應用的時候,如果沒有將這些限制考慮進去,將會導致很大的效能問題和記憶體消耗,這不是針對於某一個應用而言,而是針對整個裝置,因為很多擁有相同效能問題的app共同導致了一個效能很差的裝置。
以下使一些重要的約束,限制和現實,在過去以及將來的行動裝置中都很重要。
行動裝置的記憶體是很有限的。當然不是指所有的裝置,不過對於大多數的移動系統是這樣的。對於開發人員來說,我們使用的手機可能是比大多數使用者現在或者將來所使用的裝置更快更好,也更新。比如,擁有一個帶有 1GB-2GB記憶體Nexus 5對於我們來說是比較合理的,512M的記憶體配置的手機在美國以及一些新興的低端市場是很常見的。因此,以2GB或者更大記憶體的標準去衡量一個App是不現實的。
明白Android會運行多個Activity和多個並行的Service也是很重要的。在最近App列表中切換而不是重新啟動一個App的方式對於創造出一種很好的體驗是非常重要的。但是這樣意味著如果這些app消耗了比它們本應該消耗的更多記憶體,那麼留給其它的應用的系統記憶體就很少了。如果這種低記憶體情況發生,應用就不能保留在後台,系統會幹掉app的進程,使用者就被迫以重新啟動的方式去開啟一個App,顯然這樣的體驗就比較差。
即使最進階別的行動裝置的CPU也要遠遠比台式裝置的CPU慢。當然即使你意識到了這一點,你也應該考慮那些大多數使用了比你更慢的CPU的使用者。適用於記憶體的建議也同樣適用於CPU:世界範圍的低端裝置一直在售賣,因此不要以你自己相對較好的裝置的效能為基準,因為跑在那些大多數擁有較慢處理器和更低記憶體配置的裝置上的差別是非常大的。
另一個問題是,CPU的處理器是不會一直保持在最大的速度的。當系統認為電池的發熱程度達到了臨界點的時候,就會導致CPU降頻。一般來說,這些情況發生的時候使用者可能不會注意到,比如螢幕關閉了,或者輸入事件不響應了,或者動畫不再運行了。降頻對App來說有兩層含意:(1)你的app可能在很多情況下降低運轉速度,因此,即使是一個不錯的CPU,你也只能夠擷取一個有限的速度 (2)你的app可能做了一些操作導致CPU無法降頻,使得CPU始終維持在處理速度最大化的狀態(比如,頻繁,長時間或者無休止的動畫應該被避免,因為在動畫期間,為了讓動畫更加流暢,系統將試圖以最大的功率來運行)。顯然,你不想去權衡較高的CPU速率和使用對降低電池的使用壽命的影響,因此當你不需要的時候,盡量避免它們。
GPU效能的建議類似於CPU,當然還有些額外需要注意的地方:
上傳是消耗資源的
在任何系統上上傳大大紋理(bitmap)都是比較消耗資源的,bitmap越大,操作時間越長。因此,這意味著頻繁大進行bitmap相關大圖形化操作會導致效能問題,比如bitmap,paths(會被光柵化到Bitmap中),和大量到新的或者不同的text(有種問題是大量的非英文的字元集合的導致的問題)
我們通常面臨的問題並不是幾何的繪製,甚至一些紋理的繪製,而是大量的像素填充到高解析度的裝置上。這些高解析度的螢幕會導致一個效能問題,因為在動畫某一幀中硬體不能夠填充如此多的像素。比較常見的是過渡繪製,由於重疊的內容,比如window背景,內容背景和一些半透明的View,就會導致應用會多次重繪同一塊地區。
記憶體 == 效能
在這個系列中,很多的指導會圍繞著記憶體的使用來討論。但有一點很重要,就是記憶體是和運行期間的效能以及電池的壽命是緊緊關聯的。因為你分配的記憶體越多,裝置就要為你的應用做更多的工作。記憶體配置和回收增加了運行期間的活動會。大的heap記憶體意味著你的你的app佔用了更多的記憶體,但是裝置總體的記憶體是減少了,會導致其它的活動被迫減少自己的記憶體開銷,或者直接被kill掉,這樣將導致裝置的總體效能,因為使用者會在不同的活動中切換,而這些Activity需要重新啟動。更的的Heaps也會導致更長時間的GC中斷,因為一個較大的heap記憶體會導致記憶體的分配和回收消耗更長的時間。這些所有的行為都會消耗更多的電量,因為裝置所做的工作越多,電池就會消耗越多也更有限。
因此這個系列的文章很多涉及到了效能問題,事實就是這些技術對於寫出一個好的App來講,是非常重要的。
因此這篇文檔的最佳實務所涉及到的點不是記憶體就是效能,這些技術也是能夠寫出一個好的app所必須的。
上面很多部分也提到,就是開發人員所使用的裝置可能比大多數未來使用者的裝置都要好。雖然在2015年2GB已經是主流,但是很多在世界範圍的很多新興市場仍然賣著512M的裝置。
更平滑的幀率
對於Android的最佳體驗是低於16毫秒的幀率。也就是說應用必須在16毫秒內完成所有的輸入,布局,繪製和其它所有的事情。這個速度允許系統在動畫和輸入事件的過程中以60幀美妙的速度渲染。動畫必須能在60幀率的時間內完成必要部分的重繪已完成平滑的動效。問題更多的是,很多應用通常可以在16ms內完成渲染,但它丟棄了當前幀,並且之後不能夠延續之前的幀率,這種不連續的間隔會容易會引起使用者的注意。
平滑的完成一幀意味著任何特殊的幀需要執行所有的渲染代碼(包括framework發送給GPU和CPU繪製到緩衝區的命令)都要在16ms內完成。這也就是為什麼即使在記憶體回收事件中丟失了5ms也會有很大的影響。因為它嚴重限制了一幀時間內剩餘的繪製時間。越接近16ms,在記憶體回收事件觸發的時候,就越容易導致卡頓。
緩衝區只能夠在
有兩個運行環境需要主要:Dalvik和ART。在5.0版本之前,Android使用Dalvik。開發人員可以在4.4版本中選擇ART進行測試。但是它只會在5.0的系統中運行。
Dalvik是採用的時 JIT編譯器,能夠執行一些細微的最佳化,但是和其它的很多JIT編譯器是不同的。ART是一個Ahead-of-Time 編譯器,比Dalvik會做更多的最佳化。然而,不論是Dalvik還是ART提供的最佳化水平與伺服器和台式機運行平台都是不一樣的。比如方法的內聯和escape analysis。有一些內聯,ART會在葉子方法上執行,但是更進一步的最佳化只可能在將來的版本中出現,因為未來會有新的最佳化編譯器。由於app開發人員可能需要支援老得版本,他們需要繼續關心現在和之前編譯器的限制。
通常,ART效能要比Dalvik提升30 - 200+%。ART的編譯器會執行更多的最佳化,比如,實質性的提升介面的分發。ART最佳化的範圍要更大些,記憶體配置也更快。最終,應用的線程會更少的受限於記憶體回收,短時間的中斷次數也會減少。
記憶體回收行程
記憶體回收行程是一個通過運行期間釋放那些不再引用的記憶體的過程。GC也是導致嚴重性能問題的原因之一,如果GC所做的工作超過了那些必須的工作,那麼留給應用平滑的幀率的時間就越少。
在Dalvik和ART中的記憶體回收行程實質上是不同的。一個主要的區別就是Dalvik不是一個移動的回收器。就是說所有的指派至都將呆在Heap的同一個地方,這對於Dalvik來說,為新的對象找到可分配的記憶體就會更加困難和耗時,特別是當Heap顆粒化和片段化並且又有大量對象急需記憶體的時候。Heap片段化也會導致GC更頻繁的中斷,因為Dalvik會嘗試清理從那些無用的heap。這些GC中斷是非常消耗資源的,並且在正常的情況下一個比較快的手機也會很容易耗費掉10-20ms的時間。很重要的是,記憶體回收的時間與在堆中對象的數量成正比,這是儘可能避免指派至的另一個原因。
ART會動態提升記憶體回收的效率。比如,ART是一個移動的記憶體回收行程,在應用長時間單中止時,它會讓heap變得緊湊而不會影響使用者的體驗。而且,這裡有一些獨立的專門用於大對象的heap,比如像bitmap這樣的對象,ART就會更快的找到記憶體,而不是有序的遍曆片段化的heap。在ART中的中斷,通常在2-3ms間。
儘管ART相對於Dalvik來說的記憶體回收來說有一個很大的效能提升。但是在寫Android app的時候,仍然需要注意的是即使是2-3ms的時間對於超過16ms幀率的界限也是足夠的。因此,儘管記憶體回收在5.0之後不再是耗資源的行為,但也是始終需要儘可能避免的,特別是在執行動畫的情況下,可能會導致一些讓使用者明顯感覺的丟幀。
UI Thread
很多的效能和卡頓問題是由於我們在UI線程中做了大量的工作。Android是一個獨立線程的UI系統,在這裡所有發生在View上的操作,包括View的繪製,都發生在Activity的UI線程上。任何發生在相同UI線程的操作,即使是View的繪製也可能導致卡頓,因為它沒有時間在16ms的時間內達到一個平滑的幀率。
在5.0版本裡,framework引入了“Render Thread”,用於向GPU發送實際渲染的操作。這個線程減輕了一些UI Thread減少的操作。但是輸入,滾動和動畫仍然在UI thread,因為thread必須能夠響應操作。
儲存的指標在不同的Android裝置上是不同的,但是可能是比較慢而且是受限制的。行動裝置只有8G(2015年,在低於中等水平的裝置中很常見)甚至4G搭配一個SDcard,去儲存整個系統,所有的應用以及所有的音頻,很容易就滿了。在這種情況下,一個app可能導致使用者為它刪除其它內容以便擷取更多空間,或者因為每有足夠的空間就直接卸載它。
儲存效能也是需要關心的點。行動裝置和電腦裝置的硬碟相比是完全不同的。
同時,也要注意到外部儲存SDcard的記憶體在I/O效能上可能有很大的不確定性,其依賴於供應商,晶片和速度指標。但是App不應該阻止使用者使用SDcard,因為很多裝置確實記憶體比較小,需要它作為擴充。
位於在城市中的大多數軟體開發人員來說擁有一些現代的基礎設施和移動網路是很簡單的。但很多其它地區並不具備這樣的設施。更別說LTE或4G了。很多國家的地區還是2G的網路而且可能還要承受大量資料的傳輸。這樣就會導致兩個通用的問題:
依賴於快速的網路速度
App嚴重依賴大媒體資料(Video,audio,images)在網路基礎較差的地方可能沒有選擇。但是避免下載,直到條件允許的情況下再去下載也是App體驗的一部分
多度同步
也許你的App希望更新一些資訊,但是使用者並不需要它,更重要的問題,裝置並不應該承受所有應用程式與網路不斷互動的情況。這種動態會很容易地使裝置持續工作而不能夠進行休眠,最終影響電池的續航。
每一個裝置都是一個村落
使用者的裝置上會安裝很多的應用,包括系統的UI和Launcher App。如果你的App使用了越多的資源(記憶體,CPU,GPU,電池),那麼其它應用就會越少,那麼呈現給的使用者的裝置就會越差。如果你的處理越多,當系統需要更多記憶體的時候,就越有可能試圖從記憶體中幹掉它,那就意味著你的App就要花費更長時間去開啟,從而導致一種很不好的體驗。
因此,在Android裝置上成為一個良好的公民吧。也是每個App應該去做的,因為如果每個應用都貪婪,那麼最終痛苦是裝置和使用者。
平民的災難
行動裝置最大的問題是一些app在自己感興趣的情況下活動導致了裝置的整體效能和體驗。分析特定app可能不是應用需要修複的標誌性的問題。但是對裝置整體的影響,所有的app都遭受資源的限制,終端使用者的體驗很差。
一個好的例子是觀察某個應用是否太頻繁的同步。那意味著特定的應用在以特定的頻率訪問伺服器。但是如果使用者有超過100個應用都在這樣做,相比較那些延遲,批處理的同步系統來說,結果就是裝置將永遠不能夠休眠並且很快耗完電。
Google Developing for Android 一 - 相關上下文介紹