2012-07-01 30 views
42

請看看下面的代碼:處理程序和內存泄漏的Android

public class MyGridFragment extends Fragment{ 

    Handler myhandler = new Handler() { 
    @Override 
    public void handleMessage(Message message) { 
     switch (message.what) { 
     case 2: { 

      ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj; 
      urls.addAll(theurls); 
      theimageAdapter.notifyDataSetChanged(); 
      dismissBusyDialog(); 
      break; 
     }}}}; 
     } 

當我使用的處理程序是這樣,我得到一個警告「處理程序應該是靜態的,否則很容易出現內存泄漏。」有人能告訴我什麼是最好的辦法嗎?

+2

我不相信你正確地使用了手柄。看看這個指南:http://www.vogella.com/articles/AndroidPerformance/article.html。它沒有在示例代碼中聲明爲靜態。 :/ –

+0

即使像這樣使用它也會給我同樣的錯誤。這從來沒有發生過,直到我昨晚升級我的android sdk。只要將一個處理程序聲明爲一個類變量,現在會彈出這個lint警告 – Rasmus

+0

那麼如何聲明你的處理程序是靜態的呢? –

回答

96

我最近在自己的代碼中更新了類似的東西。我只是將匿名處理程序類設爲受保護的內部類,並且Lint警告消失了。看看下面的代碼是否適用於您:

public class MyGridFragment extends Fragment{ 

    static class MyInnerHandler extends Handler{ 
     WeakReference<MyGridFragment> mFrag; 

     MyInnerHandler(MyGridFragment aFragment) { 
      mFrag = new WeakReference<MyGridFragment>(aFragment); 
     } 

     @Override 
     public void handleMessage(Message message) { 
      MyGridFragment theFrag = mFrag.get(); 
      switch (message.what) { 
      case 2: 
       ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj; 
       theFrag.urls.addAll(theurls); 
       theFrag.theimageAdapter.notifyDataSetChanged(); 
       theFrag.dismissBusyDialog(); 
       break; 
      }//end switch 
     } 
    } 
    MyInnerHandler myHandler = new MyInnerHandler(this); 
} 

您可能需要更改放置「theFrag」的位置。因爲我只能猜測那些引用的內容。

+0

這似乎正是我想要的。我沒有測試過,但似乎沒有理由不工作..謝謝! – Rasmus

+12

不應該在mFrag.get()之後進行檢查,以檢查是否因爲它可能會收集垃圾,而看到theFrag!= null。 –

+1

取決於你的用例。我的個人代碼在大多數情況下使內部靜態類定義變爲私有的,因爲匿名類定義不應該在別處使用(即使是後代)。當僅限於它定義的類時,無論何時父Fragment被破壞,我們的內部類引用都應該在Fragment變爲NULL之前銷燬。我們使用WeakReference,因此我們的內部類不會影響Fragment何時收集垃圾。如果你公開你的內部類,或者不確定,請在使用之前檢查看看是否爲空!= null。 –

5

根據ADT 20 Changes,它看起來應該是靜態的。

新林特檢查:

檢查,以確保片段類實例化的。如果您不小心使 片段內部類非靜態,或者忘記具有默認構造函數,那麼當系統嘗試在配置更改後重新實例化片段時,可能會遇到運行時 錯誤。

查找處理程序泄漏:此檢查可確保處理程序內部類不保留對其外部類的隱式引用 。

+0

所以你認爲我應該如何處理上述問題..你需要看到更多的代碼..如果是這樣,請讓我知道 – Rasmus

+0

如果你不能讓Handler靜態,最好的可能是將其移動到父Activity並將引用傳遞給Fragment。 – Geobits

+0

感謝您的回覆Geobits ...但將它移動到父活動仍然不會使警告go.Though在我的情況下,內部類將不會比外部類生活更長,但擺脫警告是我更關心的東西。 – Rasmus

3

如果您閱讀有關AccountManager或PendingIntent的文檔,您將看到有些方法將Handler作爲參數之一。

For example

  • onFinished - 回電話時發送已完成,或者爲null,沒有回調的對象。
  • 處理程序 - 處理程序標識應該發生回調的線程。如果爲null,則回調將從進程的線程池中發生。

想象一下情況。某些Activity調用PendingIntent.send(...)並放置Handler的非靜態內部子類。然後活動被破壞。但內心階層的生活。

內部類仍然存在一個被破壞活動的鏈接,它不能被垃圾收集。

如果你不打算髮送你的處理程序到這種方法,你沒有什麼可擔心的。

+2

感謝QuickNick ......當你說「如果你不打算把你的處理程序發送到這樣的方法,你沒有什麼可擔心的,我同意你的意見。」但我需要擺脫那個警告 – Rasmus

0

我遇到了同樣的問題,我發現它是這個主題有很多問題和答案很少。我的解決方法很簡單,我希望它可以幫助別人:

/* BEFORE */ 
private Handler mHandler= new Handler() { 
     @Override public void handleMessage(Message msg) { 
     this.doSomething(); 
    }; 
}; 

我們可以創建一個僅運行一個Runnable靜態處理程序的子類。實際的處理程序實例將通過可以訪問實例變量的runnable知道該怎麼做。

/* AFTER */ 
static class RunnableHandler extends Handler { 
    private Runnable mRunnable; 
    public RunnableHandler(Runnable runnable) { 
     mRunnable = runnable; 
    } 
    @Override public void handleMessage(Message msg) { 
     mRunnable.run(); 
    }; 
} 
private RunnableHandler mHandler = new RunnableHandler(new Runnable() { 
    @Override public void run() { 
     this.doSomething(); 
    } }); 

警告消失,而功能性是相同的。

+1

感謝您回覆Urkidi,但由於某種原因,我覺得這更像是一種黑客攻擊。我不是任何方面的專家,可能是非常錯誤的 – Rasmus

+0

我喜歡這種方式,但我希望有人有更多的知識讓我知道如果這只是一個黑客周圍。 – SatanEnglish

+0

這隱藏了lint警告,但並未解決潛在的問題。 runnable將引用'this',因爲RunnableHandler保持對可運行引用的引用,所以只有更復雜的鏈纔會出現相同的錯誤。 – Sogger

11

這是我可以使用的一個有點有用的小班。可悲的是它仍然很冗長,因爲你不能擁有匿名的靜態內部類。

import java.lang.ref.WeakReference; 
import android.os.Handler; 
import android.os.Message; 

/** A handler which keeps a weak reference to a fragment. According to 
* Android's lint, references to Handlers can be kept around for a long 
* time - longer than Fragments for example. So we should use handlers 
* that don't have strong references to the things they are handling for. 
* 
* You can use this class to more or less forget about that requirement. 
* Unfortunately you can have anonymous static inner classes, so it is a 
* little more verbose. 
* 
* Example use: 
* 
* private static class MsgHandler extends WeakReferenceHandler<MyFragment> 
* { 
*  public MsgHandler(MyFragment fragment) { super(fragment); } 
* 
*  @Override 
*  public void handleMessage(MyFragment fragment, Message msg) 
*  { 
*   fragment.doStuff(msg.arg1); 
*  } 
* } 
* 
* // ... 
* MsgHandler handler = new MsgHandler(this); 
*/ 
public abstract class WeakReferenceHandler<T> extends Handler 
{ 
    private WeakReference<T> mReference; 

    public WeakReferenceHandler(T reference) 
    { 
     mReference = new WeakReference<T>(reference); 
    } 

    @Override 
    public void handleMessage(Message msg) 
    { 
     if (mReference.get() == null) 
      return; 
     handleMessage(mReference.get(), msg); 
    } 

    protected abstract void handleMessage(T reference, Message msg); 
} 
+0

如果我不需要重寫handleMessage,只需使用一個處理程序來發布Runnable,如果(mReference.get()== null) return,是否可以使用新的Handler() – virsir

+2

; handleMessage(mReference。get(),msg);是一個糟糕的方法,因爲在null檢查和handleMessage之間,引用仍然可能被引發,從而導致空指針異常,因此將返回的值首先存儲在T reference = mReference.get()中是一種更好的方法。然後檢查null並將本地變量傳遞給handleMessage方法。 –

+0

好點Kerem。隨意編輯代碼,如果你喜歡! – Timmmm