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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...