避免 Android中Context引起的內(nèi)存泄露
Context是我們?cè)诰帉?xiě)Android程序經(jīng)常使用到的對(duì)象,意思為上下文對(duì)象。 常用的有Activity的Context還是有Application的Context。Activity用來(lái)展示活動(dòng)界面,包含了很多的視圖,而視圖又含有圖片,文字等資源。在A(yíng)ndroid中內(nèi)存泄露很容易出現(xiàn),而持有很多對(duì)象內(nèi)存占用的Activity更加容易出現(xiàn)內(nèi)存泄露,開(kāi)發(fā)者需要特別注意這個(gè)問(wèn)題。
本文講介紹Android中Context,更具體的說(shuō)是Activity內(nèi)存泄露的情況,以及如何避免Activity內(nèi)存泄露,加速應(yīng)用性能。
Drawable引起的內(nèi)存泄露
Drawable引起內(nèi)存泄露這個(gè)問(wèn)題是比較隱晦,難以察覺(jué)的。在閱讀了Romain Guy的Avoiding memory leaks,結(jié)合grepcode查看源碼才明白了。
在A(yíng)ndroid系統(tǒng)中,當(dāng)我們進(jìn)行了屏幕旋轉(zhuǎn),默認(rèn)情況下,會(huì)銷(xiāo)毀掉當(dāng)前的Activity,并創(chuàng)建一個(gè)新的Activity并保持之前的狀態(tài)。在這個(gè)過(guò)程中,Android系統(tǒng)會(huì)重新加載程序的UI視圖和資源。假設(shè)我們有一個(gè)程序用到了一個(gè)很大的Bitmap圖像,我們不想每次屏幕旋轉(zhuǎn)時(shí)都重新加載這個(gè)Bitmap對(duì)象,最簡(jiǎn)單的辦法就是將這個(gè)Bitmap對(duì)象使用static修飾。
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
但是上面的方法在屏幕旋轉(zhuǎn)時(shí)有可能引起內(nèi)存泄露,無(wú)論是咋一看還是仔細(xì)看這段代碼,都很難發(fā)現(xiàn)哪里引起了內(nèi)存泄露。
當(dāng)一個(gè)Drawable綁定到了View上,實(shí)際上這個(gè)View對(duì)象就會(huì)成為這個(gè)Drawable的一個(gè)callback成員變量,上面的例子中靜態(tài)的sBackground持有TextView對(duì)象lable的引用,而lable只有Activity的引用,而Activity會(huì)持有其他更多對(duì)象的引用。sBackground生命周期要長(zhǎng)于A(yíng)ctivity。當(dāng)屏幕旋轉(zhuǎn)時(shí),Activity無(wú)法被銷(xiāo)毀,這樣就產(chǎn)生了內(nèi)存泄露問(wèn)題。
2.3.7及以下版本Drawable的setCallback方法的實(shí)現(xiàn)
public final void setCallback(Callback cb) {
mCallback = cb;
}
好在從4.0.1開(kāi)始,引入了弱引用處理這個(gè)問(wèn)題,弱引用在GC回收時(shí),不會(huì)阻止GC回收其指向的對(duì)象,避免了內(nèi)存泄露問(wèn)題。
public final void setCallback(Callback cb) {
mCallback = new WeakReference<Callback>(cb);
}
單例引起的內(nèi)存泄露
單例是我們比較簡(jiǎn)單常用的一種設(shè)計(jì)模式,然而如果單例使用不當(dāng)也會(huì)導(dǎo)致內(nèi)存泄露。 比如這樣一個(gè)例子,我們使用餓漢式初始化單例,AppSettings我們需要持有一個(gè)Context作為成員變量,如果我們按照下面的實(shí)現(xiàn)其實(shí)是有問(wèn)題。
public class AppSettings {
private Context mAppContext;
private static AppSettings sInstance = new AppSettings();
//some other codes
public static AppSettings getInstance() {
return sInstance;
}
public final void setup(Context context) {
mAppContext = context;
}
}
sInstance作為靜態(tài)對(duì)象,其生命周期要長(zhǎng)于普通的對(duì)象,其中也包含Activity,當(dāng)我們進(jìn)行屏幕旋轉(zhuǎn),默認(rèn)情況下,系統(tǒng)會(huì)銷(xiāo)毀當(dāng)前Activity,然后當(dāng)前的Activity被一個(gè)單例持有,導(dǎo)致垃圾回收器無(wú)法進(jìn)行回收,進(jìn)而產(chǎn)生了內(nèi)存泄露。
解決的方法就是不持有Activity的引用,而是持有Application的Context引用。代碼如下修改
public final void setup(Context context) {
mAppContext = context.getApplicationContext();
}
訪(fǎng)問(wèn)這里了解更多關(guān)于單例模式的問(wèn)題
條條方法返回Context
通常我們想要獲取Context對(duì)象,主要有以下四種方法
- View.getContext,返回當(dāng)前View對(duì)象的Context對(duì)象,通常是當(dāng)前正在展示的Activity對(duì)象。
- Activity.getApplicationContext,獲取當(dāng)前Activity所在的(應(yīng)用)進(jìn)程的Context對(duì)象,通常我們使用Context對(duì)象時(shí),要優(yōu)先考慮這個(gè)全局的進(jìn)程Context。
- ContextWrapper.getBaseContext():用來(lái)獲取一個(gè)ContextWrapper進(jìn)行裝飾之前的Context,可以使用這個(gè)方法,這個(gè)方法在實(shí)際開(kāi)發(fā)中使用并不多,也不建議使用。
- Activity.this 返回當(dāng)前的Activity實(shí)例,如果是UI控件需要使用Activity作為Context對(duì)象,但是默認(rèn)的Toast實(shí)際上使用ApplicationContext也可以。
其他內(nèi)存泄露問(wèn)題
OnSharedPreferenceChangeListener詳解及出現(xiàn)不觸發(fā)解決辦法
避免內(nèi)存泄露須謹(jǐn)記
- 不要讓生命周期長(zhǎng)于A(yíng)ctivity的對(duì)象持有到Activity的引用
- 盡量使用Application的Context而不是Activity的Context
- 盡量不要在A(yíng)ctivity中使用非靜態(tài)內(nèi)部類(lèi),因?yàn)榉庆o態(tài)內(nèi)部類(lèi)會(huì)隱式持有外部類(lèi)實(shí)例的引用(具體可以查看細(xì)話(huà)Java:”失效”的private修飾符了解)。如果使用靜態(tài)內(nèi)部類(lèi),將外部實(shí)例引用作為弱引用持有。
- 垃圾回收不能解決內(nèi)存泄露,了解Android中垃圾回收機(jī)制
參考文章
Avoiding memory leaks
Difference between getContext() , getApplicationContext() , getBaseContext() and “this”
Android – what's the difference between the various methods to get a Context?
以上就是對(duì)Android Context 內(nèi)存泄漏的資料整理,后續(xù)繼續(xù)添加相關(guān)資料,謝謝大家的支持!
- Android 優(yōu)化Handler防止內(nèi)存泄露
- 解決Android使用Handler造成內(nèi)存泄露問(wèn)題
- 使用Android Studio檢測(cè)內(nèi)存泄露(LeakCanary)
- Android 消息機(jī)制以及handler的內(nèi)存泄露
- Android 中Handler引起的內(nèi)存泄露
- Android垃圾回收機(jī)制解決內(nèi)存泄露問(wèn)題
- Android中Handler引起的內(nèi)存泄露問(wèn)題解決辦法
- Android編程中避免內(nèi)存泄露的方法總結(jié)
- Android App調(diào)試內(nèi)存泄露之Cursor篇
- 分析Android常見(jiàn)的內(nèi)存泄露和解決方案
相關(guān)文章
Android 對(duì)話(huà)框(Dialog)大全詳解及示例代碼
本文主要介紹Android 對(duì)話(huà)框的知識(shí),這里整理了詳細(xì)資料及實(shí)現(xiàn)示例代碼及實(shí)現(xiàn)效果圖,有興趣的小伙伴可以參考下2016-09-09
Android AsyncTask 后監(jiān)聽(tīng)異步加載完畢的動(dòng)作詳解
這篇文章主要介紹了Android 使用AsyncTask 后監(jiān)聽(tīng)異步加載完畢的動(dòng)作的相關(guān)資料,需要的朋友可以參考下2016-11-11
Android實(shí)現(xiàn)調(diào)用震動(dòng)的方法
這篇文章主要介紹了Android實(shí)現(xiàn)調(diào)用震動(dòng)的方法,實(shí)例分析了Android中Vibrator類(lèi)的調(diào)用與使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
Cocos2d-x 3.0多線(xiàn)程異步加載資源實(shí)例
手機(jī)游戲,即便是休閑類(lèi)的小游戲,往往也涉及大量紋理資源、音視頻資源、文件讀寫(xiě)以及網(wǎng)絡(luò)通信,處理的稍有不甚就會(huì)出現(xiàn)畫(huà)面卡頓,交互不暢的情況2014-04-04
Android數(shù)據(jù)加密之Base64編碼算法的簡(jiǎn)單實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇Android數(shù)據(jù)加密之Base64編碼算法的簡(jiǎn)單實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
Android實(shí)現(xiàn)儀表盤(pán)控件開(kāi)發(fā)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)儀表盤(pán)控件開(kāi)發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05
Compose自定義View實(shí)現(xiàn)宇智波斑寫(xiě)輪眼
這篇文章主要為大家介紹了Compose自定義View實(shí)現(xiàn)宇智波斑寫(xiě)輪眼示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
Android webview 內(nèi)存泄露的解決方法
這篇文章主要介紹了Android webview 內(nèi)存泄露的解決方法的相關(guān)資料,需要的朋友可以參考下2017-07-07
Android ViewPager實(shí)現(xiàn)圖片輪播效果
這篇文章主要為大家詳細(xì)介紹了Android ViewPager實(shí)現(xiàn)圖片輪播效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
Android實(shí)現(xiàn)清除單個(gè)域名的cookie
這篇文章主要介紹了Android實(shí)現(xiàn)清除單個(gè)域名的cookie,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03

