Android內存泄漏終極解決篇(下)
一、概述
在 Android內存泄漏終極解決篇(上)中我們介紹了如何檢查一個App是否存在內存泄漏的問題,本篇將總結典型的內存泄漏的代碼,并給出對應的解決方案。內存泄漏的主要問題可以分為以下幾種類型:
- 靜態(tài)變量引起的內存泄漏
- 非靜態(tài)內部類引起的內存泄漏
- 資源未關閉引起的內存泄漏
二、靜態(tài)變量引起的內存泄漏
在java中靜態(tài)變量的生命周期是在類加載時開始,類卸載時結束。換句話說,在android中其生命周期是在進程啟動時開始,進程死亡時結束。所以在程序的運行期間,如果進程沒有被殺死,靜態(tài)變量就會一直存在,不會被回收掉。如果靜態(tài)變量強引用了某個Activity中變量,那么這個Activity就同樣也不會被釋放,即便是該Activity執(zhí)行了onDestroy(不要將執(zhí)行onDestroy和被回收劃等號)。這類問題的解決方案為:1.尋找與該靜態(tài)變量生命周期差不多的替代對象。2.若找不到,將強引用方式改成弱引用。比較典型的例子如下:
單例引起的Context內存泄漏
public class IMManager { private Context context; private static IMManager mInstance; public static IMManager getInstance(Context context) { if (mInstance == null) { synchronized (IMManager.class) { if (mInstance == null) mInstance = new IMManager(context); } } return mInstance; } private IMManager(Context context) { this.context = context; } }
當調用getInstance時,如果傳入的context是Activity的context。只要這個單例沒有被釋放,這個Activity也不會被釋放。
解決方案
傳入Application的context,因為Application的context的生命周期比Activity長,可以理解為Application的context與單例的生命周期一樣長,傳入它是最合適的。
public class IMManager { private Context context; private static IMManager mInstance; public static IMManager getInstance(Context context) { if (mInstance == null) { synchronized (IMManager.class) { if (mInstance == null) //將傳入的context轉換成Application的context mInstance = new IMManager(context.getApplicationContext()); } } return mInstance; } private IMManager(Context context) { this.context = context; } }
三、非靜態(tài)內部類引起的內存泄漏
在java中,創(chuàng)建一個非靜態(tài)的內部類實例,就會引用它的外圍實例。如果這個非靜態(tài)內部類實例做了一些耗時的操作,就會造成外圍對象不會被回收,從而導致內存泄漏。這類問題的解決方案為:1.將內部類變成靜態(tài)內部類 2.如果有強引用Activity中的屬性,則將該屬性的引用方式改為弱引用。3.在業(yè)務允許的情況下,當Activity執(zhí)行onDestory時,結束這些耗時任務。
內部線程造成的內存泄漏
public class LeakAty extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.aty_leak); test(); } public void test() { //匿名內部類會引用其外圍實例LeakAty.this,所以會導致內存泄漏 new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
解決方案
將非靜態(tài)匿名內部類修改為靜態(tài)匿名內部類
public class LeakAty extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.aty_leak); test(); } //加上static,變成靜態(tài)匿名內部類 public static void test() { new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
Handler引起的內存泄漏
public class LeakAty extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.aty_leak); fetchData(); } private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case 0: // 刷新數(shù)據(jù) break; default: break; } }; }; private void fetchData() { //獲取數(shù)據(jù) mHandler.sendEmptyMessage(0); } }
mHandler 為匿名內部類實例,會引用外圍對象LeakAty.this,如果該Handler在Activity退出時依然還有消息需要處理,那么這個Activity就不會被回收。
解決方案
public class LeakAty extends Activity { private TextView tvResult; private MyHandler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.aty_leak); tvResult = (TextView) findViewById(R.id.tvResult); handler = new MyHandler(this); fetchData(); } //第一步,將Handler改成靜態(tài)內部類。 private static class MyHandler extends Handler { //第二步,將需要引用Activity的地方,改成弱引用。 private WeakReference<LeakAty> atyInstance; public MyHandler(LeakAty aty) { this.atyInstance = new WeakReference<LeakAty>(aty); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); LeakAty aty = atyInstance == null ? null : atyInstance.get(); //如果Activity被釋放回收了,則不處理這些消息 if (aty == null||aty.isFinishing()) { return; } aty.tvResult.setText("fetch data success"); } } private void fetchData() { // 獲取數(shù)據(jù) handler.sendEmptyMessage(0); } @Override protected void onDestroy() { //第三步,在Activity退出的時候移除回調 super.onDestroy(); handler.removeCallbacksAndMessages(null); } }
四、資源未關閉引起的內存泄漏
當使用了BraodcastReceiver、Cursor、Bitmap等資源時,當不需要使用時,需要及時釋放掉,若沒有釋放,則會引起內存泄漏。
綜上所述,內存泄漏的主要情況為上面的三大類型,最終歸結為一點,就是資源在不需要的時候沒有被釋放掉。所以在編碼的過程中要注意這些細節(jié),提高程序的性能。
相關文章
Android應用開發(fā)之簡易、大氣音樂播放器實現(xiàn)專輯倒影效果
這篇文章主要介紹了Android應用開發(fā)之簡單、大氣音樂播放器實現(xiàn)專輯倒影效果,對android音樂播放器感興趣的朋友可以參考下2015-10-10Android Studio中一套代碼多渠道打包的實現(xiàn)方法
這篇文章主要介紹了Android Studio中一套代碼多渠道打包的實現(xiàn)方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-05-05Android底部導航組件BottomNavigationView
這篇文章主要介紹了Android底部導航組件BottomNavigationView,BottomNavigationView是相當于一個導航的標簽,但是它的形式就是像QQ微信之類的界面,至于寫出后怎樣綁定這三個界面,就得用Fragment,寫這三個頁面的布局2023-03-03為Android系統(tǒng)添加config.xml 新配置的設置
這篇文章主要介紹了為Android系統(tǒng)添加config.xml 新配置的設置,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03