Android垃圾回收機(jī)制解決內(nèi)存泄露問題
在android編碼中,會(huì)有一些簡(jiǎn)便的寫法和編碼習(xí)慣,會(huì)導(dǎo)致我們的代碼有很多內(nèi)存泄露的問題,在這里做一個(gè)已知錯(cuò)誤的總結(jié):
1、編寫單例的時(shí)候常出現(xiàn)的錯(cuò)誤。
錯(cuò)誤方式:
public class Foo{ private static Foo foo; private Context mContext; private Foo(Context mContext){ this.mContext = mContext; } // 普通單例,非線程安全 public static Foo getInstance(Context mContext){ if(foo == null) foo = new Foo(mContext); return foo; } public void otherAction(){ mContext.xxxx(); …. } }
錯(cuò)誤原因:
如果我們?cè)贏ctivity A中或者其他地方使用Foo.getInstance()時(shí),我們總是會(huì)順手寫一個(gè)『this』或者『mContext』(這個(gè)變量也是指向this)。試想一下,當(dāng)前我們所用的Foo是單例,意味著被初始化后會(huì)一直存在與內(nèi)存中,以方便我們以后調(diào)用的時(shí)候不會(huì)在此次創(chuàng)建Foo對(duì)象。但Foo中的『mContext』變量一直都會(huì)持有Activity A中的『Context』,導(dǎo)致Activity A即使執(zhí)行了onDestroy方法,也不能夠?qū)⒆约轰N毀。但『applicationContext』就不同了,它一直伴隨著我們應(yīng)用存在(中途也可能會(huì)被銷毀,但也會(huì)自動(dòng)reCreate),所以就不用擔(dān)心Foo中的『mContext』會(huì)持有某Activity的引用,讓其無(wú)法銷毀。
正確方式:
public class Foo{ private static Foo foo; private Context mContext; private Foo(Context mContext){ this.mContext = mContext; } // 普通單例,非線程安全 public static Foo getInstance(Context mContext){ if(foo == null) foo = new Foo(mContext.getApplicationContext()); return foo; } public void otherAction(){ mContext.xxxx(); …. } }
2、使用匿名內(nèi)部類的時(shí)候經(jīng)常出現(xiàn)的錯(cuò)誤
錯(cuò)誤方式:
public class FooActivity extends Activity{ private TextView textView; private Handler handler = new Handler(){ @override public void handlerMessage(Message msg){ } }; @override public void onCreate(Bundle bundle){ super.onCreate(bundle); setContextView(R.layout.activity_foo_layout); textView = (TextView)findViewById(R.id.textView); handler.postDelayed(new Runnable(){ @override public void run(){ textView.setText(“ok”); }; },1000 * 60 * 10); } }
錯(cuò)誤原因:
當(dāng)我們執(zhí)行了FooActivity的finish方法,被延遲的消息會(huì)在被處理之前存在于主線程消息隊(duì)列中10分鐘,而這個(gè)消息中又包含了Handler的引用,而Handler是一個(gè)匿名內(nèi)部類的實(shí)例,其持有外面的FooActivity的引用,所以這導(dǎo)致了FooActivity無(wú)法回收,進(jìn)而導(dǎo)致FooActivity持有的很多資源都無(wú)法回收,所以產(chǎn)生了內(nèi)存泄露。
注意上面的new Runnable這里也是匿名內(nèi)部類實(shí)現(xiàn)的,同樣也會(huì)持有FooActivity的引用,也會(huì)阻止FooActivity被回收。
一個(gè)靜態(tài)的匿名內(nèi)部類實(shí)例不會(huì)持有外部類的引用。
正確方式:
public class FooActivity extends Activity{ private TextView textView; private static class MyHandler extends Handler { private final WeakReference<FooActivity> mActivity; public MyHandler(FooActivity activity) { mActivity = new WeakReference<FooActivity>(activity); } @Override public void handleMessage(Message msg) { FooActivity activity = mActivity.get(); if (activity != null) { // ... } } } private final MyHandler handler = new MyHandler(this); @override public void onCreate(Bundle bundle){ super.onCreate(bundle); setContextView(R.layout.activity_foo_layout); textView = (TextView)findViewById(R.id.textView); handler.postDelayed(new MyRunnable(textView),1000 * 60 * 10); } private static class MyRunnable implements Runnable{ private WeakReference<TextView> textViewWeakReference; public MyRunnable(TextView textView){ textViewWeakReference = new WeakReference<TextView>(textView); } @override public void run(){ final TextView textView = textViewWeakReference.get(); if(textView != null){ textView.setText("OK"); } }; } }
3、在使用handler后,記得在onDestroy里面handler.removeCallbacksAndMessages(object token);
handler.removeCallbacksAndMessages(null); // removeCallbacksAndMessages,當(dāng)參數(shù)為null的時(shí)候,可以清除掉所有跟次handler相關(guān)的Runnable和Message,我們?cè)趏nDestroy中調(diào)用次方法也就不會(huì)發(fā)生內(nèi)存泄漏了。
開發(fā)中需要注意的點(diǎn)以免內(nèi)存泄漏:
- 1.不要讓生命周期長(zhǎng)于Activity的對(duì)象持有到Activity的引用
- 2.盡量使用Application的Context而不是Activity的Context
- 3.盡量不要在Activity中使用非靜態(tài)內(nèi)部類,因?yàn)榉庆o態(tài)內(nèi)部類會(huì)隱式持有外部類實(shí)例的引用(具體可以查看細(xì)話Java:”失效”的private修飾符了解)。如果使用靜態(tài)內(nèi)部類,將外部實(shí)例引用作為弱引用持有。
- 4.垃圾回收不能解決內(nèi)存泄露,了解Android中垃圾回收機(jī)制
獲取context的方法,以及使用上context和applicationContext的區(qū)別:
- 1.View.getContext,返回當(dāng)前View對(duì)象的Context對(duì)象,通常是當(dāng)前正在展示的Activity對(duì)象。
- 2.Activity.getApplicationContext,獲取當(dāng)前Activity所在的(應(yīng)用)進(jìn)程的Context對(duì)象,通常我們使用Context對(duì)象時(shí),要優(yōu)先考慮這個(gè)全局的進(jìn)程Context。
- 3,ContextWrapper.getBaseContext():用來(lái)獲取一個(gè)ContextWrapper進(jìn)行裝飾之前的Context,可以使用這個(gè)方法,這個(gè)方法在實(shí)際開發(fā)中使用并不多,也不建議使用。
- 4.Activity.this 返回當(dāng)前的Activity實(shí)例,如果是UI控件需要使用Activity作為Context對(duì)象,但是默認(rèn)的Toast實(shí)際上使用ApplicationContext也可以。
大家注意看到有一些NO上添加了一些數(shù)字,其實(shí)這些從能力上來(lái)說(shuō)是YES,但是為什么說(shuō)是NO呢?下面一個(gè)一個(gè)解釋:
數(shù)字1:啟動(dòng)Activity在這些類中是可以的,但是需要?jiǎng)?chuàng)建一個(gè)新的task。一般情況不推薦。
數(shù)字2:在這些類中去layout inflate是合法的,但是會(huì)使用系統(tǒng)默認(rèn)的主題樣式,如果你自定義了某些樣式可能不會(huì)被使用。
數(shù)字3:在receiver為null時(shí)允許,在4.2或以上的版本中,用于獲取黏性廣播的當(dāng)前值。(可以無(wú)視)
注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因?yàn)樵谄鋬?nèi)部方法中都有一個(gè)context用于使用。
好了,這里我們看下表格,重點(diǎn)看Activity和Application,可以看到,和UI相關(guān)的方法基本都不建議或者不可使用Application,并且,前三個(gè)操作基本不可能在Application中出現(xiàn)。實(shí)際上,只要把握住一點(diǎn),凡是跟UI相關(guān)的,都應(yīng)該使用Activity做為Context來(lái)處理;其他的一些操作,Service,Activity,Application等實(shí)例都可以,當(dāng)然了,注意Context引用的持有,防止內(nèi)存泄漏。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
- Android 優(yōu)化Handler防止內(nèi)存泄露
- 解決Android使用Handler造成內(nèi)存泄露問題
- 使用Android Studio檢測(cè)內(nèi)存泄露(LeakCanary)
- Android 消息機(jī)制以及handler的內(nèi)存泄露
- 避免 Android中Context引起的內(nèi)存泄露
- Android 中Handler引起的內(nèi)存泄露
- Android中Handler引起的內(nèi)存泄露問題解決辦法
- Android編程中避免內(nèi)存泄露的方法總結(jié)
- Android App調(diào)試內(nèi)存泄露之Cursor篇
- 分析Android常見的內(nèi)存泄露和解決方案
相關(guān)文章
Android 第三方庫(kù)lottie、mmkv的使用詳解
lottie是Airbnb開源的一個(gè)面向 iOS、Android、React Native 的動(dòng)畫庫(kù),能實(shí)現(xiàn)精美、復(fù)雜的動(dòng)畫效果,本文通過實(shí)例代碼給大家介紹Android 第三方庫(kù)lottie、mmkv的使用,感興趣的的朋友一起看看吧2021-04-04Android無(wú)需權(quán)限調(diào)起系統(tǒng)相機(jī)
在進(jìn)行一些小型APP的開發(fā),或者是對(duì)拍照界面沒有自定義要求時(shí),我們可以用調(diào)起系統(tǒng)相機(jī)的方式快速完成拍照需求2023-03-03Android?Gradle?插件自定義Plugin實(shí)現(xiàn)注意事項(xiàng)
這篇文章主要介紹了Android?Gradle?插件自定義Plugin實(shí)現(xiàn)注意事項(xiàng),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-06-06Android NDK開發(fā)的環(huán)境搭建與簡(jiǎn)單示例
本文主要介紹Android NDK的知識(shí),這里整理了相關(guān)資料,來(lái)說(shuō)明如何搭建相應(yīng)環(huán)境和簡(jiǎn)單實(shí)例,幫助大家理解,有興趣的小伙伴可以參考下2016-09-09Android 實(shí)現(xiàn)旋轉(zhuǎn)木馬的音樂效果
大家一定在百度音樂上在線聽過歌,有沒有注意到那個(gè)旋轉(zhuǎn)的唱片,本篇文章主要介紹在Android上如何實(shí)現(xiàn)這樣的功能,有需要的小伙伴可以參考下2016-07-07Android實(shí)現(xiàn)背景顏色滑動(dòng)漸變效果的全過程
在Android開發(fā)中,經(jīng)常需要設(shè)置控件的背景顏色或者圖片的src顏色,下面這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)背景顏色滑動(dòng)漸變效果的相關(guān)資料,本文通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09Android ProgressBar 模擬進(jìn)度條效果的實(shí)現(xiàn)
這篇文章主要介紹了Android ProgressBar 模擬進(jìn)度條效果的實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04Android View的事件分發(fā)機(jī)制深入分析講解
事件分發(fā)從手指觸摸屏幕開始,即產(chǎn)生了觸摸信息,被底層系統(tǒng)捕獲后會(huì)傳遞給Android的輸入系統(tǒng)服務(wù)IMS,通過Binder把消息發(fā)送到activity,activity會(huì)通過phoneWindow、DecorView最終發(fā)送給ViewGroup。這里就直接分析ViewGroup的事件分發(fā)2023-01-01