在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Android/ 管理應(yīng)用的內(nèi)存
檢測(cè)常用的手勢(shì)
優(yōu)化layout的層級(jí)
用戶輸入
管理應(yīng)用的內(nèi)存
聯(lián)系人信息
開(kāi)發(fā)輔助程序
Android多媒體
添加語(yǔ)音功能
顯示位置地址
提供向下與橫向?qū)Ш?/span>
支持游戲控制器
訪問(wèn)可穿戴數(shù)據(jù)層
處理多點(diǎn)觸控手勢(shì)
全屏沉浸式應(yīng)用
為多線程創(chuàng)建管理器
數(shù)據(jù)保存
Intent的發(fā)送
更新Notification
優(yōu)化下載以高效地訪問(wèn)網(wǎng)絡(luò)
打印
打包可穿戴應(yīng)用
接收從其他App傳送來(lái)的數(shù)據(jù)
發(fā)送與接收消息
建立靈活動(dòng)態(tài)的UI
處理鍵盤(pán)輸入
Building a Work Policy Controller
建立測(cè)試環(huán)境
創(chuàng)建表盤(pán)
分享文件
顯示Notification進(jìn)度
實(shí)現(xiàn)自適應(yīng)UI流(Flows)
使用設(shè)備管理策略增強(qiáng)安全性
使用能感知版本的組件
執(zhí)行網(wǎng)絡(luò)操作
建立文件分享
添加移動(dòng)
更新你的Security Provider來(lái)對(duì)抗SSL漏洞利用
支持鍵盤(pán)導(dǎo)航
創(chuàng)建和監(jiān)視地理圍欄
發(fā)送并同步數(shù)據(jù)
使用BigView樣式
無(wú)線連接設(shè)備
提供向上導(dǎo)航與歷史導(dǎo)航
最小化定期更新造成的影響
實(shí)現(xiàn)向下的導(dǎo)航
支持不同的屏幕大小
Android 可穿戴應(yīng)用
添加動(dòng)畫(huà)
顯示聯(lián)系人頭像
使用OpenGL ES顯示圖像
處理輸入法可見(jiàn)性
分享文件
保持設(shè)備喚醒
淡化系統(tǒng)Bar
使用NFC分享文件
保存到Preference
Android聯(lián)系人信息與位置信息
創(chuàng)建標(biāo)準(zhǔn)的網(wǎng)絡(luò)請(qǐng)求
使用Drawables
管理Bitmap的內(nèi)存使用
管理Activity的生命周期
按需加載視圖
傳輸資源
為可穿戴設(shè)備創(chuàng)建自定義UI
在一個(gè)線程中執(zhí)行一段特定的代碼
性能優(yōu)化
隱藏導(dǎo)航欄
創(chuàng)建目錄瀏覽器
為多種大小的屏幕進(jìn)行規(guī)劃
View間漸變
使用觸摸手勢(shì)
高效加載大圖
使用CursorLoader在后臺(tái)加載數(shù)據(jù)
創(chuàng)建抽屜式導(dǎo)航(navigation drawer)
管理音頻焦點(diǎn)
創(chuàng)建后臺(tái)服務(wù)
創(chuàng)建功能測(cè)試
創(chuàng)建使用Material Design的應(yīng)用
停止與重啟Activity
添加一個(gè)簡(jiǎn)便的分享功能
啟動(dòng)Activity時(shí)保留導(dǎo)航
TV應(yīng)用清單
創(chuàng)建向后兼容的UI
?# 優(yōu)化自定義View
創(chuàng)建單元測(cè)試
在UI上顯示Bitmap
建立OpenGL ES的環(huán)境
構(gòu)建表盤(pán)服務(wù)
JNI Tips
建立搜索界面
實(shí)現(xiàn)自定義View的繪制
使用HTTPS與SSL
按需操控BroadcastReceiver
分享簡(jiǎn)單的數(shù)據(jù)
繪制形狀
Android位置信息
創(chuàng)建并運(yùn)行可穿戴應(yīng)用
執(zhí)行 Sync Adpater
獲取最后可知位置
創(chuàng)建 Android 項(xiàng)目
實(shí)現(xiàn)高效的導(dǎo)航
退出全屏的Activity
創(chuàng)建Card
兼容音頻輸出設(shè)備
同步數(shù)據(jù)單元
傳輸數(shù)據(jù)時(shí)避免消耗大量電量
保存到文件
緩存Bitmap
提供配置 Activity
調(diào)度重復(fù)的鬧鐘
實(shí)現(xiàn)輔助功能
重復(fù)的下載是冗余的
隱藏狀態(tài)欄
實(shí)現(xiàn)自定義的網(wǎng)絡(luò)請(qǐng)求
規(guī)劃界面和他們之間的關(guān)系
使用Sync Adapter傳輸數(shù)據(jù)
TV應(yīng)用內(nèi)搜索
響應(yīng)觸摸事件
使用Google Cloud Messaging(已廢棄)
控制相機(jī)
Android網(wǎng)絡(luò)連接與云服務(wù)
請(qǐng)求分享一個(gè)文件
處理TV硬件
響應(yīng)UI可見(jiàn)性的變化
使用網(wǎng)絡(luò)服務(wù)發(fā)現(xiàn)
指定輸入法類(lèi)型
優(yōu)化電池壽命
創(chuàng)建TV應(yīng)用
獲取聯(lián)系人列表
拖拽與縮放
啟動(dòng)與停止線程池中的線程
創(chuàng)建 Sync Adpater
使用 WiFi P2P 服務(wù)發(fā)現(xiàn)
開(kāi)始使用Material Design
代理至新的APIs
使用include標(biāo)簽重用layouts
使得View可交互
高效顯示Bitmap
創(chuàng)建企業(yè)級(jí)應(yīng)用
Fragments之間的交互
創(chuàng)建與執(zhí)行測(cè)試用例
綜合:設(shè)計(jì)我們的樣例 App
繪制表盤(pán)
建立簡(jiǎn)單的用戶界面
自定義動(dòng)畫(huà)
開(kāi)發(fā)輔助服務(wù)
避免出現(xiàn)程序無(wú)響應(yīng)ANR(Keeping Your App Responsive)
使用ViewPager實(shí)現(xiàn)屏幕滑動(dòng)
設(shè)計(jì)高效的導(dǎo)航
Android分享操作(Building Apps with Content Sharing)
提供向后的導(dǎo)航
保持向下兼容
創(chuàng)建TV播放應(yīng)用
縮放View
使用 WiFi 建立 P2P 連接
Android后臺(tái)任務(wù)
連接到網(wǎng)絡(luò)
為 Notification 添加頁(yè)面
使TV應(yīng)用是可被搜索的
添加Action Bar
使用Material的主題
啟動(dòng)另一個(gè)Activity
顯示正在播放卡片
適配不同的系統(tǒng)版本
輕松錄制視頻
創(chuàng)建可穿戴的應(yīng)用
創(chuàng)建自定義的布局
重新創(chuàng)建Activity
使用CursorLoader執(zhí)行查詢?nèi)蝿?wù)
使用舊的APIs實(shí)現(xiàn)新API的效果
使用備份API
安全要點(diǎn)
Android入門(mén)基礎(chǔ):從這里開(kāi)始
保存并搜索數(shù)據(jù)
根據(jù)網(wǎng)絡(luò)連接類(lèi)型來(lái)調(diào)整下載模式
使用Tabs創(chuàng)建Swipe視圖
SMP(Symmetric Multi-Processor) Primer for Android
解析 XML 數(shù)據(jù)
使用 Volley 傳輸網(wǎng)絡(luò)數(shù)據(jù)
建立ActionBar
Android交互設(shè)計(jì)
使用Intent修改聯(lián)系人信息
增加搜索功能
輕松拍攝照片
定義形狀
測(cè)試你的Activity
在 Notifcation 中接收語(yǔ)音輸入
與其他應(yīng)用的交互
管理系統(tǒng)UI
追蹤手勢(shì)移動(dòng)
Android界面設(shè)計(jì)
執(zhí)行 Android 程序
顯示確認(rèn)界面
創(chuàng)建Lists與Cards
打印HTML文檔
創(chuàng)建TV應(yīng)用
為多屏幕設(shè)計(jì)
定義Shadows與Clipping視圖
使用Fragment建立動(dòng)態(tài)UI
接收Activity返回的結(jié)果
布局變更動(dòng)畫(huà)
定位常見(jiàn)的問(wèn)題
自定義ActionBar的風(fēng)格
定義Layouts
發(fā)送簡(jiǎn)單的網(wǎng)絡(luò)請(qǐng)求
啟動(dòng)與銷(xiāo)毀Activity
與UI線程通信
非UI線程處理Bitmap
創(chuàng)建TV布局
提升Layout的性能
報(bào)告任務(wù)執(zhí)行狀態(tài)
判斷并監(jiān)測(cè)網(wǎng)絡(luò)連接狀態(tài)
兼容不同的設(shè)備
處理按鍵動(dòng)作
優(yōu)化性能和電池使用時(shí)間
給其他App發(fā)送簡(jiǎn)單的數(shù)據(jù)
Implementing App Restrictions
向后臺(tái)服務(wù)發(fā)送任務(wù)請(qǐng)求
展示Card翻轉(zhuǎn)動(dòng)畫(huà)
管理ViewGroup中的觸摸事件
兼容不同的屏幕密度
通過(guò)藍(lán)牙進(jìn)行調(diào)試
為可穿戴設(shè)備創(chuàng)建Notification
控制音量與音頻播放
獲取聯(lián)系人詳情
在表盤(pán)上顯示信息
提供向上的導(dǎo)航
滾動(dòng)手勢(shì)動(dòng)畫(huà)
幫助用戶在TV上找到內(nèi)容
創(chuàng)建TV導(dǎo)航
為索引指定App內(nèi)容
ActionBar的覆蓋疊加
Android Wear 上的位置檢測(cè)
保護(hù)安全與隱私的最佳策略
Ensuring Compatibility with Managed Profiles
解決云同步的保存沖突
獲取位置更新
創(chuàng)建List
測(cè)試程序
管理網(wǎng)絡(luò)的使用情況
為App內(nèi)容開(kāi)啟深度鏈接
推薦TV內(nèi)容
建立一個(gè)Notification
管理音頻播放
設(shè)計(jì)表盤(pán)
拍照
處理控制器輸入動(dòng)作
判斷并監(jiān)測(cè)設(shè)備的底座狀態(tài)與類(lèi)型
處理查詢的結(jié)果
保存到數(shù)據(jù)庫(kù)
支持多個(gè)游戲控制器
創(chuàng)建 Stub Content Provider
使得ListView滑動(dòng)順暢
處理數(shù)據(jù)層的事件
創(chuàng)建TV應(yīng)用的第一步
使得你的App內(nèi)容可被Google搜索
將 Notification 放成一疊
創(chuàng)建 Stub 授權(quán)器
暫停與恢復(fù)Activity
管理設(shè)備的喚醒狀態(tài)
Android圖像與動(dòng)畫(huà)
打印照片
云同步
創(chuàng)建TV直播應(yīng)用
為Notification賦加可穿戴特性
提供一個(gè)Card視圖
建立請(qǐng)求隊(duì)列(RequestQueue)
適配不同的語(yǔ)言
創(chuàng)建詳情頁(yè)
測(cè)試UI組件
接收其他設(shè)備的文件
創(chuàng)建自定義View
建立第一個(gè)App
創(chuàng)建2D Picker
監(jiān)測(cè)電池的電量與充電狀態(tài)
打印自定義文檔
抽象出新的APIs
通知提示用戶
獲取文件信息
運(yùn)用投影與相機(jī)視角
在IntentService中執(zhí)行后臺(tái)任務(wù)
多線程操作
創(chuàng)建一個(gè)Fragment
添加Action按鈕
在不同的 Android 系統(tǒng)版本支持控制器
維護(hù)兼容性
發(fā)送文件給其他設(shè)備
創(chuàng)建TV游戲應(yīng)用
創(chuàng)建自定義的View類(lèi)
代碼性能優(yōu)化建議
Intent過(guò)濾
適配不同的屏幕

管理應(yīng)用的內(nèi)存

編寫(xiě):kesenhoo - 原文:http://developer.android.com/training/articles/memory.html

Random Access Memory(RAM)在任何軟件開(kāi)發(fā)環(huán)境中都是一個(gè)很寶貴的資源。這一點(diǎn)在物理內(nèi)存通常很有限的移動(dòng)操作系統(tǒng)上,顯得尤為突出。盡管Android的Dalvik虛擬機(jī)扮演了常規(guī)的垃圾回收的角色,但這并不意味著你可以忽視app的內(nèi)存分配與釋放的時(shí)機(jī)與地點(diǎn)。

為了GC能夠從app中及時(shí)回收內(nèi)存,我們需要注意避免內(nèi)存泄露(通常由于在全局成員變量中持有對(duì)象引用而導(dǎo)致)并且在適當(dāng)?shù)臅r(shí)機(jī)(下面會(huì)講到的lifecycle callbacks)來(lái)釋放引用對(duì)象。對(duì)于大多數(shù)app來(lái)說(shuō),Dalvik的GC會(huì)自動(dòng)把離開(kāi)活動(dòng)線程的對(duì)象進(jìn)行回收。

這篇文章會(huì)解釋Android是如何管理app的進(jìn)程與內(nèi)存分配,以及在開(kāi)發(fā)Android應(yīng)用的時(shí)候如何主動(dòng)的減少內(nèi)存的使用。關(guān)于Java的資源管理機(jī)制,請(qǐng)參考其它書(shū)籍或者線上材料。如果你正在尋找如何分析你的內(nèi)存使用情況的文章,請(qǐng)參考這里Investigating Your RAM Usage

第1部分: Android是如何管理內(nèi)存的

Android并沒(méi)有為內(nèi)存提供交換區(qū)(Swap space),但是它有使用pagingmemory-mapping(mmapping)的機(jī)制來(lái)管理內(nèi)存。這意味著任何你修改的內(nèi)存(無(wú)論是通過(guò)分配新的對(duì)象還是去訪問(wèn)mmaped pages中的內(nèi)容)都會(huì)貯存在RAM中,而且不能被paged out。因此唯一完整釋放內(nèi)存的方法是釋放那些你可能hold住的對(duì)象的引用,當(dāng)這個(gè)對(duì)象沒(méi)有被任何其他對(duì)象所引用的時(shí)候,它就能夠被GC回收了。只有一種例外是:如果系統(tǒng)想要在其他地方重用這個(gè)對(duì)象。

1) 共享內(nèi)存

Android通過(guò)下面幾個(gè)方式在不同的進(jìn)程中來(lái)實(shí)現(xiàn)共享RAM:

  • 每一個(gè)app的進(jìn)程都是從一個(gè)被叫做Zygote的進(jìn)程中fork出來(lái)的。Zygote進(jìn)程在系統(tǒng)啟動(dòng)并且載入通用的framework的代碼與資源之后開(kāi)始啟動(dòng)。為了啟動(dòng)一個(gè)新的程序進(jìn)程,系統(tǒng)會(huì)fork Zygote進(jìn)程生成一個(gè)新的進(jìn)程,然后在新的進(jìn)程中加載并運(yùn)行app的代碼。這使得大多數(shù)的RAM pages被用來(lái)分配給framework的代碼,同時(shí)使得RAM資源能夠在應(yīng)用的所有進(jìn)程中進(jìn)行共享。

  • 大多數(shù)static的數(shù)據(jù)被mmapped到一個(gè)進(jìn)程中。這不僅僅使得同樣的數(shù)據(jù)能夠在進(jìn)程間進(jìn)行共享,而且使得它能夠在需要的時(shí)候被paged out。例如下面幾種static的數(shù)據(jù):
    • Dalvik 代碼 (放在一個(gè)預(yù)鏈接好的 .odex 文件中以便直接mapping)
    • App resources (通過(guò)把資源表結(jié)構(gòu)設(shè)計(jì)成便于mmapping的數(shù)據(jù)結(jié)構(gòu),另外還可以通過(guò)把APK中的文件做aligning的操作來(lái)優(yōu)化)
    • 傳統(tǒng)項(xiàng)目元素,比如 .so 文件中的本地代碼.
  • 在很多情況下,Android通過(guò)顯式的分配共享內(nèi)存區(qū)域(例如ashmem或者gralloc)來(lái)實(shí)現(xiàn)一些動(dòng)態(tài)RAM區(qū)域能夠在不同進(jìn)程間進(jìn)行共享。例如,window surfaces在app與screen compositor之間使用共享的內(nèi)存,cursor buffers在content provider與client之間使用共享的內(nèi)存。

關(guān)于如何查看app所使用的共享內(nèi)存,請(qǐng)查看Investigating Your RAM Usage

2) 分配與回收內(nèi)存

這里有下面幾點(diǎn)關(guān)于Android如何分配與回收內(nèi)存的事實(shí):

  • 每一個(gè)進(jìn)程的Dalvik heap都有一個(gè)受限的虛擬內(nèi)存范圍。這就是邏輯上講的heap size,它可以隨著需要進(jìn)行增長(zhǎng),但是會(huì)有一個(gè)系統(tǒng)為它所定義的上限。
  • 邏輯上講的heap size和實(shí)際物理上使用的內(nèi)存數(shù)量是不等的,Android會(huì)計(jì)算一個(gè)叫做Proportional Set Size(PSS)的值,它記錄了那些和其他進(jìn)程進(jìn)行共享的內(nèi)存大小。(假設(shè)共享內(nèi)存大小是10M,一共有20個(gè)Process在共享使用,根據(jù)權(quán)重,可能認(rèn)為其中有0.3M才能真正算是你的進(jìn)程所使用的)
  • Dalvik heap與邏輯上的heap size不吻合,這意味著Android并不會(huì)去做heap中的碎片整理用來(lái)關(guān)閉空閑區(qū)域。Android僅僅會(huì)在heap的尾端出現(xiàn)不使用的空間時(shí)才會(huì)做收縮邏輯heap size大小的動(dòng)作。但是這并不是意味著被heap所使用的物理內(nèi)存大小不能被收縮。在垃圾回收之后,Dalvik會(huì)遍歷heap并找出不使用的pages,然后使用madvise(系統(tǒng)調(diào)用)把那些pages返回給kernal。因此,成對(duì)的allocations與deallocations大塊的數(shù)據(jù)可以使得物理內(nèi)存能夠被正常的回收。然而,回收碎片化的內(nèi)存則會(huì)使得效率低下很多,因?yàn)槟切┧槠姆峙漤?yè)面也許會(huì)被其他地方所共享到。

3) 限制應(yīng)用的內(nèi)存

為了維持多任務(wù)的功能環(huán)境,Android為每一個(gè)app都設(shè)置了一個(gè)硬性的heap size限制。準(zhǔn)確的heap size限制會(huì)因?yàn)椴煌O(shè)備的不同RAM大小而各有差異。如果你的app已經(jīng)到了heap的限制大小并且再嘗試分配內(nèi)存的話,會(huì)引起OutOfMemoryError的錯(cuò)誤。

在一些情況下,你也許想要查詢當(dāng)前設(shè)備的heap size限制大小是多少,然后決定cache的大小??梢酝ㄟ^(guò)getMemoryClass()來(lái)查詢。這個(gè)方法會(huì)返回一個(gè)整數(shù),表明你的應(yīng)用的heap size限制是多少M(fèi)b(megabates)。

4) 切換應(yīng)用

Android并不會(huì)在用戶切換不同應(yīng)用時(shí)候做交換內(nèi)存的操作。Android會(huì)把那些不包含foreground組件的進(jìn)程放到LRU cache中。例如,當(dāng)用戶剛開(kāi)始啟動(dòng)了一個(gè)應(yīng)用,系統(tǒng)會(huì)為它創(chuàng)建了一個(gè)進(jìn)程,但是當(dāng)用戶離開(kāi)這個(gè)應(yīng)用,此進(jìn)程并不會(huì)立即被銷(xiāo)毀。系統(tǒng)會(huì)把這個(gè)進(jìn)程放到cache中,如果用戶后來(lái)再回到這個(gè)應(yīng)用,此進(jìn)程就能夠被完整恢復(fù),從而實(shí)現(xiàn)應(yīng)用的快速切換。

如果你的應(yīng)用中有一個(gè)被緩存的進(jìn)程,這個(gè)進(jìn)程會(huì)占用暫時(shí)不需要使用到的內(nèi)存,這個(gè)暫時(shí)不需要使用的進(jìn)程,它被保留在內(nèi)存中,這會(huì)對(duì)系統(tǒng)的整體性能有影響。因此當(dāng)系統(tǒng)開(kāi)始進(jìn)入低內(nèi)存狀態(tài)時(shí),它會(huì)由系統(tǒng)根據(jù)LRU的規(guī)則與其他因素選擇綜合考慮之后決定殺掉某些進(jìn)程,為了保持你的進(jìn)程能夠盡可能長(zhǎng)久的被緩存,請(qǐng)參考下面的章節(jié)學(xué)習(xí)何時(shí)釋放你的引用。

對(duì)于那些不在foreground的進(jìn)程,Android是如何決定kill掉哪一類(lèi)進(jìn)程的問(wèn)題,請(qǐng)參考Processes and Threads.

第2部分: 你的應(yīng)用該如何管理內(nèi)存

你應(yīng)該在開(kāi)發(fā)過(guò)程的每一個(gè)階段都考慮到RAM的有限性,甚至包括在開(kāi)始編寫(xiě)代碼之前的設(shè)計(jì)階段就應(yīng)該考慮到RAM的限制性。我們可以使用多種設(shè)計(jì)與實(shí)現(xiàn)方式,他們有著不同的效率,即使這些方式只是相同技術(shù)的不斷組合與演變。

為了使得你的應(yīng)用性能效率更高,你應(yīng)該在設(shè)計(jì)與實(shí)現(xiàn)代碼時(shí),遵循下面的技術(shù)要點(diǎn)。

1) 珍惜Services資源

如果你的應(yīng)用需要在后臺(tái)使用service,除非它被觸發(fā)并執(zhí)行一個(gè)任務(wù),否則其他時(shí)候service都應(yīng)該是停止?fàn)顟B(tài)。另外需要注意當(dāng)這個(gè)service完成任務(wù)之后因?yàn)橥V箂ervice失敗而引起的內(nèi)存泄漏。

當(dāng)你啟動(dòng)一個(gè)service,系統(tǒng)會(huì)傾向?yàn)榱吮A暨@個(gè)service而一直保留service所在的進(jìn)程。這使得進(jìn)程的運(yùn)行代價(jià)很高,因?yàn)橄到y(tǒng)沒(méi)有辦法把service所占用的RAM空間騰出來(lái)讓給其他組件,另外service還不能被paged out。這減少了系統(tǒng)能夠存放到LRU緩存當(dāng)中的進(jìn)程數(shù)量,它會(huì)影響app之間的切換效率。它甚至?xí)?dǎo)致系統(tǒng)內(nèi)存使用不穩(wěn)定,從而無(wú)法繼續(xù)保持住所有目前正在運(yùn)行的service。

限制你的service的最好辦法是使用IntentService, 它會(huì)在處理完交代給它的intent任務(wù)之后盡快結(jié)束自己。更多信息,請(qǐng)閱讀Running in a Background Service.

當(dāng)一個(gè)Service已經(jīng)不再需要的時(shí)候還繼續(xù)保留它,這對(duì)Android應(yīng)用的內(nèi)存管理來(lái)說(shuō)是最糟糕的錯(cuò)誤之一。因此千萬(wàn)不要貪婪的使得一個(gè)Service持續(xù)保留。不僅僅是因?yàn)樗鼤?huì)使得你的應(yīng)用因?yàn)镽AM空間的不足而性能糟糕,還會(huì)使得用戶發(fā)現(xiàn)那些有著常駐后臺(tái)行為的應(yīng)用并且可能卸載它。

2) 當(dāng)UI隱藏時(shí)釋放內(nèi)存

當(dāng)用戶切換到其它應(yīng)用并且你的應(yīng)用 UI不再可見(jiàn)時(shí),你應(yīng)該釋放你的應(yīng)用UI上所占用的所有內(nèi)存資源。在這個(gè)時(shí)候釋放UI資源可以顯著的增加系統(tǒng)緩存進(jìn)程的能力,它會(huì)對(duì)用戶體驗(yàn)有著很直接的影響。

為了能夠接收到用戶離開(kāi)你的UI時(shí)的通知,你需要實(shí)現(xiàn)Activtiy類(lèi)里面的onTrimMemory()回調(diào)方法。你應(yīng)該使用這個(gè)方法來(lái)監(jiān)聽(tīng)到TRIM_MEMORY_UI_HIDDEN級(jí)別的回調(diào),此時(shí)意味著你的UI已經(jīng)隱藏,你應(yīng)該釋放那些僅僅被你的UI使用的資源。

請(qǐng)注意:你的應(yīng)用僅僅會(huì)在所有UI組件的被隱藏的時(shí)候接收到onTrimMemory()的回調(diào)并帶有參數(shù)TRIM_MEMORY_UI_HIDDEN。這與onStop()的回調(diào)是不同的,onStop會(huì)在activity的實(shí)例隱藏時(shí)會(huì)執(zhí)行,例如當(dāng)用戶從你的app的某個(gè)activity跳轉(zhuǎn)到另外一個(gè)activity時(shí)前面activity的onStop()會(huì)被執(zhí)行。因此你應(yīng)該實(shí)現(xiàn)onStop回調(diào),并且在此回調(diào)里面釋放activity的資源,例如釋放網(wǎng)絡(luò)連接,注銷(xiāo)監(jiān)聽(tīng)廣播接收者。除非接收到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)的回調(diào),否者你不應(yīng)該釋放你的UI資源。這確保了用戶從其他activity切回來(lái)時(shí),你的UI資源仍然可用,并且可以迅速恢復(fù)activity。

3) 當(dāng)內(nèi)存緊張時(shí)釋放部分內(nèi)存

在你的app生命周期的任何階段,onTrimMemory的回調(diào)方法同樣可以告訴你整個(gè)設(shè)備的內(nèi)存資源已經(jīng)開(kāi)始緊張。你應(yīng)該根據(jù)onTrimMemory回調(diào)中的內(nèi)存級(jí)別來(lái)進(jìn)一步?jīng)Q定釋放哪些資源。

  • TRIM_MEMORY_RUNNING_MODERATE:你的app正在運(yùn)行并且不會(huì)被列為可殺死的。但是設(shè)備此時(shí)正運(yùn)行于低內(nèi)存狀態(tài)下,系統(tǒng)開(kāi)始觸發(fā)殺死LRU Cache中的Process的機(jī)制。
  • TRIM_MEMORY_RUNNING_LOW:你的app正在運(yùn)行且沒(méi)有被列為可殺死的。但是設(shè)備正運(yùn)行于更低內(nèi)存的狀態(tài)下,你應(yīng)該釋放不用的資源用來(lái)提升系統(tǒng)性能(但是這也會(huì)直接影響到你的app的性能)。
  • TRIM_MEMORY_RUNNING_CRITICAL:你的app仍在運(yùn)行,但是系統(tǒng)已經(jīng)把LRU Cache中的大多數(shù)進(jìn)程都已經(jīng)殺死,因此你應(yīng)該立即釋放所有非必須的資源。如果系統(tǒng)不能回收到足夠的RAM數(shù)量,系統(tǒng)將會(huì)清除所有的LRU緩存中的進(jìn)程,并且開(kāi)始?xì)⑺滥切┲氨徽J(rèn)為不應(yīng)該殺死的進(jìn)程,例如那個(gè)包含了一個(gè)運(yùn)行態(tài)Service的進(jìn)程。

同樣,當(dāng)你的app進(jìn)程正在被cached時(shí),你可能會(huì)接受到從onTrimMemory()中返回的下面的值之一:

  • TRIM_MEMORY_BACKGROUND: 系統(tǒng)正運(yùn)行于低內(nèi)存狀態(tài)并且你的進(jìn)程正處于LRU緩存名單中最不容易殺掉的位置。盡管你的app進(jìn)程并不是處于被殺掉的高危險(xiǎn)狀態(tài),系統(tǒng)可能已經(jīng)開(kāi)始?xì)⒌鬖RU緩存中的其他進(jìn)程了。你應(yīng)該釋放那些容易恢復(fù)的資源,以便于你的進(jìn)程可以保留下來(lái),這樣當(dāng)用戶回退到你的app的時(shí)候才能夠迅速恢復(fù)。
  • TRIM_MEMORY_MODERATE: 系統(tǒng)正運(yùn)行于低內(nèi)存狀態(tài)并且你的進(jìn)程已經(jīng)已經(jīng)接近LRU名單的中部位置。如果系統(tǒng)開(kāi)始變得更加內(nèi)存緊張,你的進(jìn)程是有可能被殺死的。
  • TRIM_MEMORY_COMPLETE: 系統(tǒng)正運(yùn)行與低內(nèi)存的狀態(tài)并且你的進(jìn)程正處于LRU名單中最容易被殺掉的位置。你應(yīng)該釋放任何不影響你的app恢復(fù)狀態(tài)的資源。

因?yàn)閛nTrimMemory()的回調(diào)是在API 14才被加進(jìn)來(lái)的,對(duì)于老的版本,你可以使用[onLowMemory](http://developer.android.com/reference/android/content/ComponentCallbacks.html#onLowMemory())回調(diào)來(lái)進(jìn)行兼容。onLowMemory相當(dāng)與`TRIM_MEMORY_COMPLETE`。

Note: 當(dāng)系統(tǒng)開(kāi)始清除LRU緩存中的進(jìn)程時(shí),盡管它首先按照LRU的順序來(lái)操作,但是它同樣會(huì)考慮進(jìn)程的內(nèi)存使用量。因此消耗越少的進(jìn)程則越容易被留下來(lái)。

4) 檢查你應(yīng)該使用多少的內(nèi)存

正如前面提到的,每一個(gè)Android設(shè)備都會(huì)有不同的RAM總大小與可用空間,因此不同設(shè)備為app提供了不同大小的heap限制。你可以通過(guò)調(diào)用[getMemoryClass()](http://developer.android.com/reference/android/app/ActivityManager.html#getMemoryClass())來(lái)獲取你的app的可用heap大小。如果你的app嘗試申請(qǐng)更多的內(nèi)存,會(huì)出現(xiàn)`OutOfMemory`的錯(cuò)誤。

在一些特殊的情景下,你可以通過(guò)在manifest的application標(biāo)簽下添加largeHeap=true的屬性來(lái)聲明一個(gè)更大的heap空間。如果你這樣做,你可以通過(guò)[getLargeMemoryClass()](http://developer.android.com/reference/android/app/ActivityManager.html#getLargeMemoryClass())來(lái)獲取到一個(gè)更大的heap size。

然而,能夠獲取更大heap的設(shè)計(jì)本意是為了一小部分會(huì)消耗大量RAM的應(yīng)用(例如一個(gè)大圖片的編輯應(yīng)用)。不要輕易的因?yàn)槟阈枰褂么罅康膬?nèi)存而去請(qǐng)求一個(gè)大的heap size。只有當(dāng)你清楚的知道哪里會(huì)使用大量的內(nèi)存并且為什么這些內(nèi)存必須被保留時(shí)才去使用large heap. 因此請(qǐng)盡量少使用large heap。使用額外的內(nèi)存會(huì)影響系統(tǒng)整體的用戶體驗(yàn),并且會(huì)使得GC的每次運(yùn)行時(shí)間更長(zhǎng)。在任務(wù)切換時(shí),系統(tǒng)的性能會(huì)變得大打折扣。

另外, large heap并不一定能夠獲取到更大的heap。在某些有嚴(yán)格限制的機(jī)器上,large heap的大小和通常的heap size是一樣的。因此即使你申請(qǐng)了large heap,你還是應(yīng)該通過(guò)執(zhí)行g(shù)etMemoryClass()來(lái)檢查實(shí)際獲取到的heap大小。

5) 避免bitmaps的浪費(fèi)

當(dāng)你加載一個(gè)bitmap時(shí),僅僅需要保留適配當(dāng)前屏幕設(shè)備分辨率的數(shù)據(jù)即可,如果原圖高于你的設(shè)備分辨率,需要做縮小的動(dòng)作。請(qǐng)記住,增加bitmap的尺寸會(huì)對(duì)內(nèi)存呈現(xiàn)出2次方的增加,因?yàn)閄與Y都在增加。

Note:在Android 2.3.x (API level 10)及其以下, bitmap對(duì)象的pixel data是存放在native內(nèi)存中的,它不便于調(diào)試。然而,從Android 3.0(API level 11)開(kāi)始,bitmap pixel data是分配在你的app的Dalvik heap中, 這提升了GC的工作效率并且更加容易Debug。因此如果你的app使用bitmap并在舊的機(jī)器上引發(fā)了一些內(nèi)存問(wèn)題,切換到3.0以上的機(jī)器上進(jìn)行Debug。

6) 使用優(yōu)化的數(shù)據(jù)容器

利用Android Framework里面優(yōu)化過(guò)的容器類(lèi),例如SparseArray, SparseBooleanArray, 與 LongSparseArray。 通常的HashMap的實(shí)現(xiàn)方式更加消耗內(nèi)存,因?yàn)樗枰粋€(gè)額外的實(shí)例對(duì)象來(lái)記錄Mapping操作。另外,SparseArray更加高效在于他們避免了對(duì)key與value的autobox自動(dòng)裝箱,并且避免了裝箱后的解箱。

7) 請(qǐng)注意內(nèi)存開(kāi)銷(xiāo)

對(duì)你所使用的語(yǔ)言與庫(kù)的成本與開(kāi)銷(xiāo)有所了解,從開(kāi)始到結(jié)束,在設(shè)計(jì)你的app時(shí)謹(jǐn)記這些信息。通常,表面上看起來(lái)無(wú)關(guān)痛癢(innocuous)的事情也許實(shí)際上會(huì)導(dǎo)致大量的開(kāi)銷(xiāo)。例如:

  • Enums的內(nèi)存消耗通常是static constants的2倍。你應(yīng)該盡量避免在Android上使用enums。
  • 在Java中的每一個(gè)類(lèi)(包括匿名內(nèi)部類(lèi))都會(huì)使用大概500 bytes。
  • 每一個(gè)類(lèi)的實(shí)例花銷(xiāo)是12-16 bytes。
  • 往HashMap添加一個(gè)entry需要額一個(gè)額外占用的32 bytes的entry對(duì)象。

8) 請(qǐng)注意代碼“抽象”

通常,開(kāi)發(fā)者使用抽象作為"好的編程實(shí)踐",因?yàn)槌橄竽軌蛱嵘a的靈活性與可維護(hù)性。然而,抽象會(huì)導(dǎo)致一個(gè)顯著的開(kāi)銷(xiāo):通常他們需要同等量的代碼用于可執(zhí)行。那些代碼會(huì)被map到內(nèi)存中。因此如果你的抽象沒(méi)有顯著的提升效率,應(yīng)該盡量避免他們。

9) 為序列化的數(shù)據(jù)使用nano protobufs

Protocol buffers是由Google為序列化結(jié)構(gòu)數(shù)據(jù)而設(shè)計(jì)的,一種語(yǔ)言無(wú)關(guān),平臺(tái)無(wú)關(guān),具有良好擴(kuò)展性的協(xié)議。類(lèi)似XML,卻比XML更加輕量,快速,簡(jiǎn)單。如果你需要為你的數(shù)據(jù)實(shí)現(xiàn)協(xié)議化,你應(yīng)該在客戶端的代碼中總是使用nano protobufs。通常的協(xié)議化操作會(huì)生成大量繁瑣的代碼,這容易給你的app帶來(lái)許多問(wèn)題:增加RAM的使用量,顯著增加APK的大小,更慢的執(zhí)行速度,更容易達(dá)到DEX的字符限制。

關(guān)于更多細(xì)節(jié),請(qǐng)參考protobuf readme的"Nano version"章節(jié)。

10) 避免使用依賴注入框架

使用類(lèi)似Guice或者RoboGuice等f(wàn)ramework injection包是很有效的,因?yàn)樗麄兡軌蚝?jiǎn)化你的代碼。

Notes:RoboGuice 2 通過(guò)依賴注入改變代碼風(fēng)格,讓Android開(kāi)發(fā)時(shí)的體驗(yàn)更好。你在調(diào)用 getIntent().getExtras() 時(shí)經(jīng)常忘記檢查 null 嗎?RoboGuice 2 可以幫你做。你認(rèn)為將 findViewById() 的返回值強(qiáng)制轉(zhuǎn)換成 TextView 是本不必要的工作嗎? RoboGuice 2 可以幫你。RoboGuice 把這些需要猜測(cè)性的工作移到Android開(kāi)發(fā)以外去了。RoboGuice 2 會(huì)負(fù)責(zé)注入你的 View, Resource, System Service或者其他對(duì)象等等類(lèi)似的細(xì)節(jié)。

然而,那些框架會(huì)通過(guò)掃描你的代碼執(zhí)行許多初始化的操作,這會(huì)導(dǎo)致你的代碼需要大量的RAM來(lái)mapping代碼,而且mapped pages會(huì)長(zhǎng)時(shí)間的被保留在RAM中。

11) 謹(jǐn)慎使用第三方libraries

很多開(kāi)源的library代碼都不是為移動(dòng)網(wǎng)絡(luò)環(huán)境而編寫(xiě)的,如果運(yùn)用在移動(dòng)設(shè)備上,,這樣的效率并不高。當(dāng)你決定使用一個(gè)第三方library的時(shí)候,你應(yīng)該針對(duì)移動(dòng)網(wǎng)絡(luò)做繁瑣的遷移與維護(hù)的工作。

即使是針對(duì)Android而設(shè)計(jì)的library,也可能是很危險(xiǎn)的,因?yàn)槊恳粋€(gè)library所做的事情都是不一樣的。例如,其中一個(gè)lib使用的是nano protobufs, 而另外一個(gè)使用的是micro protobufs。那么這樣,在你的app里面就有2種protobuf的實(shí)現(xiàn)方式。這樣的沖突同樣可能發(fā)生在輸出日志,加載圖片,緩存等等模塊里面。

同樣不要陷入為了1個(gè)或者2個(gè)功能而導(dǎo)入整個(gè)library的陷阱。如果沒(méi)有一個(gè)合適的庫(kù)與你的需求相吻合,你應(yīng)該考慮自己去實(shí)現(xiàn),而不是導(dǎo)入一個(gè)大而全的解決方案。

12) 優(yōu)化整體性能

官方有列出許多優(yōu)化整個(gè)app性能的文章:Best Practices for Performance。這篇文章就是其中之一。有些文章是講解如何優(yōu)化app的CPU使用效率,有些是如何優(yōu)化app的內(nèi)存使用效率。

你還應(yīng)該閱讀optimizing your UI來(lái)為layout進(jìn)行優(yōu)化。同樣還應(yīng)該關(guān)注lint工具所提出的建議,進(jìn)行優(yōu)化。

13) 使用ProGuard來(lái)剔除不需要的代碼

ProGuard能夠通過(guò)移除不需要的代碼,重命名類(lèi),域與方法等方對(duì)代碼進(jìn)行壓縮,優(yōu)化與混淆。使用ProGuard可以使得你的代碼更加緊湊,這樣能夠使用更少mapped代碼所需要的RAM。

14) 對(duì)最終的APK使用zipalign

在編寫(xiě)完所有代碼,并通過(guò)編譯系統(tǒng)生成APK之后,你需要使用zipalign對(duì)APK進(jìn)行重新校準(zhǔn)。如果你不做這個(gè)步驟,會(huì)導(dǎo)致你的APK需要更多的RAM,因?yàn)橐恍╊?lèi)似圖片資源的東西不能被mapped。

Notes: Google Play不接受沒(méi)有經(jīng)過(guò)zipalign的APK。

15) 分析你的RAM使用情況

一旦你獲取到一個(gè)相對(duì)穩(wěn)定的版本后,需要分析你的app整個(gè)生命周期內(nèi)使用的內(nèi)存情況,并進(jìn)行優(yōu)化,更多細(xì)節(jié)請(qǐng)參考Investigating Your RAM Usage.

16) 使用多進(jìn)程

如果合適的話,有一個(gè)更高級(jí)的技術(shù)可以幫助你的app管理內(nèi)存使用:通過(guò)把你的app組件切分成多個(gè)組件,運(yùn)行在不同的進(jìn)程中。這個(gè)技術(shù)必須謹(jǐn)慎使用,大多數(shù)app都不應(yīng)該運(yùn)行在多個(gè)進(jìn)程中。因?yàn)槿绻褂貌划?dāng),它會(huì)顯著增加內(nèi)存的使用,而不是減少。當(dāng)你的app需要在后臺(tái)運(yùn)行與前臺(tái)一樣的大量的任務(wù)的時(shí)候,可以考慮使用這個(gè)技術(shù)。

一個(gè)典型的例子是創(chuàng)建一個(gè)可以長(zhǎng)時(shí)間后臺(tái)播放的Music Player。如果整個(gè)app運(yùn)行在一個(gè)進(jìn)程中,當(dāng)后臺(tái)播放的時(shí)候,前臺(tái)的那些UI資源也沒(méi)有辦法得到釋放。類(lèi)似這樣的app可以切分成2個(gè)進(jìn)程:一個(gè)用來(lái)操作UI,另外一個(gè)用來(lái)后臺(tái)的Service.

你可以通過(guò)在manifest文件中聲明'android:process'屬性來(lái)實(shí)現(xiàn)某個(gè)組件運(yùn)行在另外一個(gè)進(jìn)程的操作。

<service android:name=".PlaybackService"
         android:process=":background" />

更多關(guān)于使用這個(gè)技術(shù)的細(xì)節(jié),請(qǐng)參考原文,鏈接如下。 http://developer.android.com/training/articles/memory.html