Android Handler的優化使用

看到一遍寫的不錯的文字,引用一下!

Handler:

普通用法:

Handler用於處理和從隊列MessageQueue中得到Message。一般我們要重寫Handler的handleMessage(Message msg){}方法來處理,如下代碼:

public class MainActivity extends Activity {
    private TextView textView;
    
	Handler normalHandler = new Handler(){
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case 1:
				Log.i("test",textView.getText().toString());
				break;

			default:
				break;
			}
		};
	};
}



問題:

這個時候Handler會被Android SDK中Lint工具檢查警告你(左邊那個黃色燈泡+歎號):This Handler class should be static or leaks might occur 。

原因:

This Handler class should be static:

(知識點一)爲什麼靜態內部類可以解決這個問題呢?或者說靜態內部類和非靜態內部類的區別是什麼?

舉例:class A{int a; static int b class B{}  static class C{} }  (A是外部類,B非靜態內部類,C靜態內部類,a普通字段,b靜態字段)

1)B非靜態內部類:

可以訪問A.a和A.b,也就是外部的屬性都能方位。因爲B隱式的持有A類對象的引用,相當於A的屬性

2)C靜態內部類:

C只可以訪問A.b,不可以方位A.a。爲什麼?因爲C不含有A的引用,它和A類是同一個級別,只不過寫到了A類的內部。

本例原因:

Handler匿名內部類,隱式的持有了外部類Activity的引用(這就是爲什麼你能在handleMessage()中調用MainActivity中TextView等的屬性)。--->而以後調

Message message = normalHandler.obtainMessage();
normalHandler.sendMessageAtTime(message , 100*1000);

得到的message中又含有這個Handler的引用(可以看源碼)。

在100秒後message被執行,這期間message被放在MessageQueue中,MessageQueue在Looper中,Looper是線程的本地變量。

也就是說MainActivity即使生命週期走完了也不會垃圾回收,爲什麼?因爲Java的垃圾回收機制,就是看一個對象有沒有被引用(從線程中的主要對象開始,對象之間的引用形成網狀結構,如果有類的對象不在這張網上,就證明它沒被引用。這就是數據結構中圖的遍歷,什麼連通子圖,非連通子圖)。而本文中一個MainActivity被Handler持有引用,Handler被Message持有引用,Message被MessageQueue持有引用,MessageQueue被Looper持有引用,Looper爲線程本地變量,線程不被摧毀,它就不會被銷燬。

所以即便用戶已經切換、退出到別的Activity,MainActivity佔有的內存仍舊不會被釋放。


解決方案:

打破引用鏈:

1.Message在100秒後被處理,之後回收Message,然後回收MainActivity。(所以是實際上,你只要不發很長時間的Message也不會有什麼問題)

2.使Handler不持有MainActivity的引用,用弱引用WeakReference:(簡單講,就是隻有WeakReference引用的對象,垃圾回收將回收該對象,以後再另寫一篇引用的文章吧)


正常代碼:

	MyHandler handler = new MyHandler(this);

	public static class MyHandler extends Handler {
		private WeakReference<MainActivity> reference;

		public MyHandler(MainActivity activity) {
			reference = new WeakReference<MainActivity>(activity);
		}

		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case 1:
				Log.i("test",textView.getText().toString());
				break;

			default:
				break;
			}
		}
	}




爲何Android界面更新,Handler作爲匿名內部類來實現handlerMessage方法

最重要的不是線程安全問題,而是android組件的監聽方法中只能訪問final 的屬性,所以是無法修改的,只能把它交給handler處理。就是這個樣子
 

ANDROID中handler的問題

Handler不等於使用線程。
傳統的UI架構,如swing。將繪製展示、事件派發都放在主線程(UI)線程中進行。
UI線程的實現模型通常是一個死循環,不斷接受Message。組織派發
android中Handler-MessageQueue-Looper,三者構成了這種死循環+消息通信的模型。此處的postDelayed實際上是將一個Runnable任務投入了MessageQueue中,並期望在3000毫秒後執行。
另外不要誤以爲Runnable是線程。在java.util.concurrent中,Dogn大神已經明確將執行過程與任務分割出來。Runnable接口只是表示一項任務,既可以同步執行,也可以在新線程中執行。
爲何需要Handler而不用Thread。除了消息模型是UI框架的經典模式外,還涉及到UI組件不允許跨線程訪問的限制。無論是.NET也好,swing也好,android也好,不允許在非UI線程中操作這一點都一樣。
Handler便是android框架中異步線程代碼到達同步線程的官方通道。從另一個角度說,這種基於消息模型的通信模式有時也很有用。相關的例子有IntentService,HandlerThread等。

發佈了23 篇原創文章 · 獲贊 7 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章