天天看點

This Handler class should be static or leaks might occur Android警告處理

        更新到ADT2.0的開發者們可能會在handler上發現這麼一條警告:This Handler class should be static or leaks might occur 。首先在ADT 20 Changes我們可以找到這樣一個變化:New Lint Checks:Look for handler leaks: This check makes sure that a handler inner class does not hold an implicit reference to its outer class.首先解釋下這句話This Handler class should be static or leaks might occur,大緻意思就是說:Handler類應該定義成靜态類,否則可能導緻記憶體洩露。

具體如何解決,在國外有人提出,如下:

Issue: Ensures that Handler classes do not hold on to a reference to an outer class In Android, Handler classes should be static or leaks might occur. Messages enqueued on the application thread's MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class.

大體翻譯如下:

Handler 類應該應該為static類型,否則有可能造成洩露。在程式消息隊列中排隊的消息保持了對目标Handler類的應用。如果Handler是個内部類,那 麼它也會保持它所在的外部類的引用。為了避免洩露這個外部類,應該将Handler聲明為static嵌套類,并且使用對外部類的弱應用。

解決方式一  推薦使用,比較适合

這裡我們可以采用弱引用的方式來解決問題,我們先定義一個static的内部類CHandler ,然後讓它持有Activity的弱引用,這樣問題就得到了解決。

static class CHandler extends Handler{
	
	WeakReference<AsBindPhoneActivity> cactivity;
	CHandler(AsBindPhoneActivity activity) {
		cactivity = new WeakReference<AsBindPhoneActivity>(activity);
	}
	
	@Override
	public void handleMessage(Message msg) {
		// TODO Auto-generated method stub
		super.handleMessage(msg);
		switch (msg.what) {
		case 10011:
			if (btGet != null) {
				btGet.setText("重新擷取("+second+")");
			}
			break;
		case 10012:
			if (btGet != null) {
				btGet.setText("擷取短信驗證碼");
			}
			break;
		default:
			DebugUtil.d("msg.what沒有比對的項");
			break;
		}
	}
}
           

解決方式二 把Handler定義成static,然後用post方法把Runnable對象傳送到主線程,這種方式了解一下就可以了

這種方法将要發送的消息轉換成了對應的Runable對象,适用于隻有一個消息要發送的情形

static class MyHandler extends Handler {  
       
    WeakReference<PopupActivity> mActivity;  
  
    MyHandler(PopupActivity activity) {  
        mActivity =  new  WeakReference<PopupActivity>(activity);  
    }  
  
     @Override   
     public void handleMessage(Message msg) {  
        PopupActivity theActivity = mActivity.get();  
         switch  (msg.what) {  
         //此處可以根據what的值處理多條資訊   
         case 0x0001 :  
             //這裡可以改變activity中的UI控件的狀态   
            theActivity.textView.setText(R.string.hello_world);  
             break ; 
         case 0x0002 :  
             //這裡可以改變activity中的UI控件的狀态   
            theActivity.textView.setText(R.string.welcome);  
             break ;  
         /*這裡可以有多條要處理資訊的操作*/   
         /*... ...*/   
    	}  
    }  
};	  
	//執行個體化一個MyHandler對象   
	MyHandler testHandler =  new  MyHandler( this );  
	  
	private void test1() {  
	     //這裡發送了一個空消息,空消息的what值是0x0001   
	    testHandler.sendEmptyMessage( 0x0001 );  
	}  
  
	private void test2() {  
		//這裡發送了一個空消息,空消息的what值是0x0001   
		testHandler.sendEmptyMessage( 0x0002 );  
	} 
           

詳細的解釋一下

1.Android App啟動的時候,Android Framework 為主線程建立一個Looper對象,這個Looper對象将貫穿這個App的整個生命周期,它實作了一個消息隊列(Message  Queue),并且開啟一個循環來處理Message對象。而Framework的主要事件都包含着内部Message對象,當這些事件被觸發的時候,Message對象會被加到消息隊列中執行。

2.當一個Handler被執行個體化時(如上面那樣),它将和主線程Looper對象的消息隊列相關聯,被推到消息隊列中的Message對象将持有一個Handler的引用以便于當Looper處理到這個Message的時候,Framework執行Handler的handleMessage(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() { }
    }, 60 * 10 * 1000);
                  
    // Go back to the previous Activity.
    finish();
  }
}
           

當Activity被finish()掉,Message 将存在于消息隊列中長達10分鐘的時間才會被執行到。這個Message持有一個對Handler的引用,Handler也會持有一個對于外部類(SampleActivity)的隐式引用,這些引用在Message被執行前将一直保持,這樣會保證Activity的上下文不被垃圾回收機制回收,同時也會洩露應用程式的資源( views and resources )。

為解決這個問題,下面這段代碼中的Handler則是一個靜态匿名内部類。靜态匿名内部類不會持有一個對外部類的隐式引用,是以Activity将不會被洩露。如果你需要在Handler中調用外部Activity的方法,就讓Handler持有一個對Activity的WeakReference,這樣就不會洩露Activity的上下文了,如下所示:

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, 60 * 10 * 1000);
             
    // Go back to the previous Activity.
    finish();
  }
}
           

在實際開發中,如果内部類的生命周期和Activity的生命周期不一緻(比如上面那種,Activity finish()之後要等10分鐘,内部類的執行個體才會執行),則在Activity中要避免使用非靜态的内部類,這種情況,就使用一個靜态内部類,同時持有一個對Activity的 WeakReference 。

參考連結:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1106/1922.html

繼續閱讀