5個Android開發中比較常見的記憶體泄漏問題及解決辦法

来源:http://www.cnblogs.com/sjm19910902/archive/2017/02/16/6404700.html
-Advertisement-
Play Games

android中一個對象已經不需要了,但是其他對象還持有他的引用,導致他不能回收,導致這個對象暫存在記憶體中,這樣記憶體泄漏就出現了。 記憶體泄漏出現多了,會是應用占用過多的沒存,當占用的記憶體超過了系統分配的記憶體容量,就會出現記憶體溢出了導致應用Crash. 瞭解了記憶體泄漏的原因及影響後,我們需要做的就是掌 ...


android中一個對象已經不需要了,但是其他對象還持有他的引用,導致他不能回收,導致這個對象暫存在記憶體中,這樣記憶體泄漏就出現了。   記憶體泄漏出現多了,會是應用占用過多的沒存,當占用的記憶體超過了系統分配的記憶體容量,就會出現記憶體溢出了導致應用Crash.   瞭解了記憶體泄漏的原因及影響後,我們需要做的就是掌握常見的記憶體泄漏,併在以後的Android程式開發中,儘量避免它。下麵搜羅了5個Android開發中比較常見的記憶體泄漏問題及解決辦法,分享給大家,一起來看看吧。   1.單例造成的記憶體泄漏   android的單列大家都喜歡使用。但單例模式的靜態特征使得他的生命周期和應用的生命周期一樣長,這就說明瞭一個對象不需要使用了,單例對象還持有某個對象,那麼這個對象就不能釋放了,這就記憶體泄漏了。 典例:   public class AppManager {     private static AppManager instance;     private Context context;     private AppManager(Context context) {         this.context = context;     }     public static AppManager getInstance(Context context) {         if (instance != null) {             instance = new AppManager(context);         }         return instance;     } }   這個單例需要傳入Context對象,所以這個Context的生命周期的長短至關重要: 1、傳入的是Application的Context:這將沒有任何問題,因為單例的生命周期和Application的一樣長 ; 2、傳入的是Activity的Context:當這個Context所對應的Activity退出時,由於該Context和Activity的生命周期一樣長(Activity間接繼承於Context),所以當前Activity退出時它的記憶體並不會被回收,因為單例對象持有該Activity的引用。 所以正確的單例應該修改為下麵這種方式: public class AppManager {     private static AppManager instance;     private Context context;     private AppManager(Context context) {         this.context = context.getApplicationContext();     }     public static AppManager getInstance(Context context) {         if (instance != null) {             instance = new AppManager(context);         }         return instance;     } }   這樣不管傳入什麼Context最終將使用Application的Context,而單例的生命周期和應用的一樣長,這樣就防止了記憶體泄漏。   二、非靜態內部類創建靜態實例造成的記憶體泄漏     有的時候我們可能會在啟動頻繁的Activity中,為了避免重覆創建相同的數據資源,會出現這種寫法: public class MainActivity extends AppCompatActivity {     private static TestResource mResource = null;     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         if(mManager == null){             mManager = new TestResource();         }         //...     }     class TestResource {         //...     } }   這樣就在Activity內部創建了一個非靜態內部類的單例,每次啟動Activity時都會使用該單例的數據,這樣雖然避免了資源的重覆創建,不過這種寫法卻會造成記憶體泄漏,因為非靜態內部類預設會持有外部類的引用,而又使用了該非靜態內部類創建了一個靜態的實例,該實例的生命周期和應用的一樣長,這就導致了該靜態實例一直會持有該Activity的引用,導致Activity的記憶體資源不能正常回收。正確的做法為:   將該內部類設為靜態內部類或將該內部類抽取出來封裝成一個單例,如果需要使用Context,請使用ApplicationContext 。     三、Handler造成的記憶體泄漏   Handler的使用造成的記憶體泄漏問題應該說最為常見了,平時在處理網路任務或者封裝一些請求回調等api都應該會藉助Handler來處理,對於Handler的使用代碼編寫一不規範即有可能造成記憶體泄漏,如下示例:   public class MainActivity extends AppCompatActivity {     private Handler mHandler = new Handler() {         @Override         public void handleMessage(Message msg) {             //...         }     };     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         loadData();     }     private void loadData(){         //...request         Message message = Message.obtain();         mHandler.sendMessage(message);     } }   這種創建Handler的方式會造成記憶體泄漏,由於mHandler是Handler的非靜態匿名內部類的實例,所以它持有外部類Activity的引用,我們知道消息隊列是在一個Looper線程中不斷輪詢處理消息,那麼當這個Activity退出時消息隊列中還有未處理的消息或者正在處理消息,而消息隊列中的Message持有mHandler實例的引用,mHandler又持有Activity的引用,所以導致該Activity的記憶體資源無法及時回收,引發記憶體泄漏,所以另外一種做法為:   public class MainActivity extends AppCompatActivity {     private MyHandler mHandler = new MyHandler(this);     private TextView mTextView ;     private static class MyHandler extends Handler {         private WeakReference<Context> reference;         public MyHandler(Context context) {             reference = new WeakReference<>(context);         }         @Override         public void handleMessage(Message msg) {             MainActivity activity = (MainActivity) reference.get();             if(activity != null){                 activity.mTextView.setText("");             }         }     }        @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         mTextView = (TextView)findViewById(R.id.textview);         loadData();     }        private void loadData() {         //...request         Message message = Message.obtain();         mHandler.sendMessage(message);     } }   創建一個靜態Handler內部類,然後對Handler持有的對象使用弱引用,這樣在回收時也可以回收Handler持有的對象,這樣雖然避免了Activity泄漏,不過Looper線程的消息隊列中還是可能會有待處理的消息,所以我們在Activity的Destroy時或者Stop時應該移除消息隊列中的消息,更準確的做法如下:   public class MainActivity extends AppCompatActivity {     private MyHandler mHandler = new MyHandler(this);     private TextView mTextView ;     private static class MyHandler extends Handler {         private WeakReference<Context> reference;         public MyHandler(Context context) {             reference = new WeakReference<>(context);         }         @Override         public void handleMessage(Message msg) {             MainActivity activity = (MainActivity) reference.get();             if(activity != null){                 activity.mTextView.setText("");             }         }     }        @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         mTextView = (TextView)findViewById(R.id.textview);         loadData();     }        private void loadData() {         //...request         Message message = Message.obtain();         mHandler.sendMessage(message);     }        @Override     protected void onDestroy() {         super.onDestroy();         mHandler.removeCallbacksAndMessages(null);     } }   使用mHandler.removeCallbacksAndMessages(null);是移除消息隊列中所有消息和所有的Runnable。當然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();來移除指定的Runnable和Message。   四、資源未關閉造成的記憶體泄漏   對於使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等資源的使用,應該在Activity銷毀時及時關閉或者註銷,否則這些資源將不會被回收,造成記憶體泄漏。   五、線程造成的記憶體泄漏   對於線程造成的記憶體泄漏,也是平時比較常見的,如下這兩個示例可能每個人都這樣寫過:   //——————test1         new AsyncTask<Void, Void, Void>() {             @Override             protected Void doInBackground(Void... params) {                 SystemClock.sleep(10000);                 return null;             }         }.execute(); //——————test2         new Thread(new Runnable() {             @Override             public void run() {                 SystemClock.sleep(10000);             }         }).start();     上面的非同步任務和Runnable都是一個匿名內部類,因此它們對當前Activity都有一個隱式引用。如果Activity在銷毀之前,任務還未完成, 那麼將導致Activity的記憶體資源無法回收,造成記憶體泄漏。正確的做法還是使用靜態內部類的方式,如下: static class MyAsyncTask extends AsyncTask<Void, Void, Void> {         private WeakReference<Context> weakReference;            public MyAsyncTask(Context context) {             weakReference = new WeakReference<>(context);         }            @Override         protected Void doInBackground(Void... params) {             SystemClock.sleep(10000);             return null;         }            @Override         protected void onPostExecute(Void aVoid) {             super.onPostExecute(aVoid);             MainActivity activity = (MainActivity) weakReference.get();             if (activity != null) {                 //...             }         }     }     static class MyRunnable implements Runnable{         @Override         public void run() {             SystemClock.sleep(10000);         }     } //——————     new Thread(new MyRunnable()).start();     new MyAsyncTask(this).execute();     這樣就避免了Activity的記憶體資源泄漏,當然在Activity銷毀時候也應該取消相應的任務AsyncTask::cancel(),避免任務在後臺執行浪費資源。     以上就是android編程中,常見的5大記憶體泄漏問題及相應的解決辦法,如果大家在編程中遇到了上述泄漏問題,不妨可以試試對應的方法。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 作者:Víctor Manuel Pineda 時間:Feb 14, 2017 原文鏈接:https://antonioleiva.com/kotlin-realm-extensions/ 當有人問我,最喜歡Kotlin什麼,我很難找出其突出特性。 數據類型、拉姆達(Lambda)表達式、類型推斷、 ...
  • 紅包互換軟體開發 互換紅包軟體定製開發,聯繫傑森團隊微電188-2624-7572. 1、精彩互換系統模式開發付出關鍵:統統的會員介入精彩互換系統模式付出的金額由平臺在後盾設定,能夠設定一個值,也能夠設定多個值。比如說付出5元的,平臺給你返的紅包額度在1-100元之間,付出20元的,平臺給你返的紅包 ...
  • 之前上傳圖片都是直接將圖片轉化為io流傳給伺服器,沒有用框架傳圖片。 最近做項目,打算換個方法上傳圖片。 Android發展到現在,Okhttp顯得越來越重要,所以,這次我選擇用Okhttp上傳圖片。 Okhttp目前已經更新到Okhttp3版本了,用法跟之前相比,也有一些差別。在網上找了很多資料, ...
  • 今天在項目中碰到一個問題,在一個頁面的頂部的標題欄顯示公司的名字,但由於公司名稱較長,顯示不開,影響美觀。故在網上查閱資料,在此做個小的總結。 TextView中有個ellipsize屬性,作用是當文字過長時,該控制項該如何顯示,解釋如下: 1.Android:ellipsize=”start”—–省 ...
  • 由OpenDigg 出品的iOS開源項目周報第八期來啦。我們的iOS開源周報集合了OpenDigg一周來新收錄的優質的iOS開源項目,方便iOS開發人員便捷的找到自己需要的項目工具等。 ...
  • UIDocumentInteractionController UIActivityViewController Shared Keychain Access Custom URL Scheme Web Service iCloud API UIPasteboard 參考 http://enharm ...
  • iOS UISearchController 的使用方法 UISearchController 讓用戶在 UISearchBar 上輸入搜索關鍵詞,展示搜索結果或者進行其他操作。UISearchController 把兩個控制器(UIViewController)連在一起。父控制器放置 UISear ...
  • 二碼公益軟體開發,二碼公益app開發,二碼公益開發聯繫微電188-2624-7572. 隨著社會的發展、經濟的進步,人口老齡化問題不可避免地日益凸顯,“尊老、敬老、扶老”,成為了每個中國人越發不能忽視的社會責任。 在這樣的大環境下,“二碼公益”應運而生,它將取代傳統的商業模式,無論是作為商家還是消費 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...