1. 程式人生 > >Handle & Inner Classes 如何避免記憶體洩漏

Handle & Inner Classes 如何避免記憶體洩漏

檢視下面一段程式碼

public class SampleActivity extends Activity {

  private final Handler mLeakyHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ... 
    }
  };
}

上面一段程式碼看起來很正常,但這段程式碼確實能造成大量的記憶體洩漏。並且,Android Lint(Android studio 編譯器的提示) 將會給你下面的警告:

In Android, Handler classes should be static or leaks might occur.

但洩漏到底在哪裡,何時發生? 我們就可以用我們所知道的來確定問題的根源:

1.Android 應用程式首次啟動時,framework將會為application’s main thread 建立一個looper物件。looper實現了一個簡單的message佇列,迴圈的處理message物件。所有的主要application framework事件(例如 activity生命週期方法的呼叫, button的點選事件, 等等) 都包含在Message物件中,message 被新增到looper的message佇列中並且逐個處理。

2.當一個handler在main thread中例項化,它將會和looper’s 訊息佇列關聯。當handler傳送一個message到訊息佇列中,該message物件將會持有handler的引用. 所以,當looper處理該訊息的時候,framework 可以呼叫handler#handler(message)方法

3.在Java中,非靜態內部和匿名類保留對其外部類的隱含引用。但是,靜態內部類將不會。

那麼記憶體洩漏究竟在哪裡呢?這很微妙,我們以下面的程式碼為例:

public class SampleActivity extends Activity {

  private final Handler mLeakyHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ...
    }
  };

  @Override
  protected void
onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mLeakyHandler.postDelayed(new Runnable() { @Override public void run() { /* ... */ } }, 1000 * 60 * 10); // Go back to the previous Activity. finish(); } }

當這個activity被銷燬,延遲訊息在被處理之前會繼續在主執行緒的佇列中存活10分鐘。訊息物件將會持有handler引用,而且handler也持有一個隱含的引用外部類(在這個例子中是SampleActivity)。這個引用直到訊息被處理之前將一直保留。注意,在匿名 Runnable 類中的15行也是如此。非靜態的匿名類例項持有一個他們的外部類的隱含引用,所以,context將會洩漏。

要解決這個問題,需要將繼承handler的子類建立成一個新的檔案, 或者使用靜態內部類來代替。靜態內部類不會持有他們的外部類的隱含引用,所以,activity將不會洩漏。如果你需要在handler內部呼叫外部activity的方法,可以在handler持有一個WeakReference 的activity,那麼你講不會意外地洩漏context。為了修復當我們例項化一個匿名Runnable 類時發生的記憶體洩漏,我們使變數成為該類的一個靜態欄位(由於匿名類的靜態例項不包含對外部類的隱含引用):

public class SampleActivity extends Activity {

  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   */
  private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;

    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
        // ...
      }
    }
  }

  private final MyHandler mHandler = new MyHandler(this);

  /**
   * Instances of anonymous classes do not hold an implicit
   * reference to their outer class when they are "static".
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Post a message and delay its execution for 10 minutes.
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

    // Go back to the pevious Activity.
    finish();
  }
}

靜態類和非靜態類的區別是微妙的,但是,這是每一個android開發人員需要知道的。
最關鍵的是什麼?如果一個內部類能夠存活的比activity的生命週期還長,應該避免在activity使用非靜態內部類。因此,寧可選擇靜態內部類並在裡面持有一個弱引用的activity。

相關推薦

Handle & Inner Classes 如何避免記憶體洩漏

檢視下面一段程式碼 public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override

在 POSIX 執行緒程式設計中避免記憶體洩漏

POSIX 執行緒簡介 使用執行緒的主要原因是要提高程式效能。執行緒的建立和管理只需要較小的作業系統開銷和較少的系統資源。一個程序內的所有執行緒共享相同的地址空間,使得執行緒間的通訊更高效,且比程序間通訊更易於實現。例如,如果一個執行緒在等待一個輸入/輸出系統呼叫完成,其他執行緒可以處理 CPU 密集型任務

在JNI程式設計中避免記憶體洩漏(二)

JAVA 程式設計中的記憶體洩漏,從洩漏的記憶體位置角度可以分為兩種:JVM 中 Java Heap 的記憶體洩漏;JVM 記憶體中native memory 的記憶體洩漏。 Java 物件儲存在 JVM 程序空間中的 Java Heap 中,Java Heap 可以在 JVM 執行過程中動態變化。如果

記憶體優化(二)如何避免記憶體洩漏

文章目錄 一、不同生命週期導致的記憶體洩漏 解決辦法 二、非靜態內部類持有物件導致的記憶體洩漏 1. 非靜態內部類呼叫外部類的方法的 2. 內部類是如

在 JNI 程式設計中避免記憶體洩漏與崩潰

JNI 程式設計簡介 JNI,Java Native Interface,是 native code 的程式設計介面。JNI 使 Java 程式碼程式可以與 native code 互動——在 Java 程式中呼叫 native code;在 native code 中

c++避免記憶體洩漏

在c/c++語言對於程式記憶體的管理不像java語言一樣有自己的垃圾回收機制,而c/c++卻要程式設計師手動的釋放用關鍵字new或者 malloc系統函式申請的記憶體空間,然而由於程式設計師的疏忽可能

詳解java記憶體洩露和如何避免記憶體洩漏

源地址:http://www.xttblog.com/?p=518 一直以來java都佔據著語言排行榜的頭把交椅。這是與java的設計密不可分的,其中最令大家喜歡的不是面向物件,而是垃圾回收機制。你只需要簡單的建立物件而不需要負責釋放空間,因為Java的垃圾回收器會負責記憶

Android Handler 避免記憶體洩漏之清空佇列

Android開發經常會用到handler,但是我們發現每次使用Handler都會出現:This Handler class should be static or leaks might occur(null)這樣的提示。Android lint就是為了提示我們,這樣使

Android Weak Handler:可以避免記憶體洩漏的Handler庫

這是一個針對技術開發者的一個應用,你可以在掘金上獲取最新最優質的技術乾貨,不僅僅是Android知識、前端、後端以至於產品和設計都有涉獵,想成為全棧工程師的朋友不要錯過! android使用java作為其開發環境。java的跨平臺和垃圾回收機制已經幫助我們解決了底層的一些問題。但是儘管有了垃圾回收機

Kotlin中handler避免記憶體洩漏

前言: Handler在Android開發中經常使用,一不小心就會陷入記憶體洩漏的問題,最近在開發一款Kotlin軟體,針對Handler記憶體洩漏的問題做出瞭解決方案 問題分析: 在fini

JAVA 內部靜態類-避免記憶體洩漏的原因

publicclass PrefixTrie {         // supports 7-bit chars.       privatestaticfinalint SIZE = 128;         Node root = new Node();         publicvoid p

Rxjava2定時器週期執行任務,避免記憶體洩漏

private CompositeDisposable mCompositeDisposable = new CompositeDisposable(); private void test() { mCompositeDi

你不必使用弱引用以避免記憶體洩漏

我的一位同事最近提到,他們看到了一個演講,說:“如果你是一個Android開發者,你不使用使用弱引用,將會有問題。” 我個人認為,這不僅是一個錯誤的論點,而且完全是一個誤導。WeakReference應該是修復記憶體洩漏的最後手段。 下面是谷歌開發專家E

如何避免記憶體洩漏、溢位的幾種常用方法

儘早釋放無用物件的引用。 好的辦法是使用臨時變數的時候,讓引用變數在退出活動域後自動設定為null,暗示垃圾收集器來收集該物件,防止發生記憶體洩露。 程式進行字串處理時,儘量避免使用String,而應使用StringBuffer。 因為每一個String物

android 使用handle警告,,存在記憶體洩漏的危險,使用靜態內部類和弱引用的方式解決。

Handle警告的原因:handle定義為內部類,會持有外部類的引用,如果外部類結束,handle因為執行耗時操作沒有結束,並持有外部類的引用,導致外部類佔用的記憶體不能釋放。 解決辦法:handle定義為靜態內部類,handle對於外部類的元件和方法的操作藉助弱引用來實現。 public

JavaScript中4種常見的記憶體洩漏避免方法

垃圾回收演算法        常用垃圾回收演算法叫做**標記清除 (Mark-and-sweep) **,演算法由以下幾步組成: 1、垃圾回收器建立了一個“roots”列表。roots 通常是程式碼中全域性變數的引用。JavaScrip

安卓專案實戰之:最實用的Retrofit2+RxJava2+MVP框架搭建,避免各種記憶體洩漏

工程目錄結構 目前網上的mvp框架大多存在以下問題: 1,Presenter持有View的引用,容易導致出現記憶體洩漏 MvpPresenter mvpPresenter = new MvpPresenter(this); // 不推薦這樣寫,持有activty引用,容易出現

java記憶體洩漏分類及避免

要點 記憶體洩露是指程式中間動態分配了記憶體,但在程式結束時沒有釋放這部分記憶體,從而造成那部分記憶體不可用的情況,重啟計算機可以解決,但也有可能再次發生記憶體洩露,記憶體洩露和硬體沒有關係,它是由軟體設計缺陷引起的。 記憶體洩漏可以分為4類: 1) 常發性記憶體洩漏。發生

如何避免java程式記憶體洩漏

雖然jvm有垃圾回收機制,如果程式編寫不注意某些特定規則,仍然會導致java程式記憶體洩漏,最終可能出現OutOfMemory異常。 1.Java記憶體洩漏的原因 java中的物件從使用上分為2種類型,被引用(referenced)的和不被引用(unre

記憶體溢位和記憶體洩漏的區別和如何避免記憶體溢位

記憶體溢位 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是記憶體溢位。 記憶體洩露 memory leak,是指程式在申請記憶體後,無