我理解的Hanlder--android消息传递机制
每一個學習Android的同學都會覺得Handler是一個神奇的東西,我也一樣,開始我以為我懂了Handler的機制,后來發現自己是一知半解,昨天想想,我能否自己實現一個Handler,讓子線程與ActivityUI線程通信,如果能夠自己實現一個Handler,那必然是對Handler的消息傳遞機制理解滲透了。
一、引入
Android的UI是單線程控制的,實際上,成功的UI框架都是基于單線程的,多線程的UI框架往往因為解決并發和死鎖的艱難而胎死腹中。只有UI線程能控制界面控件,但我們總是希望子線程的數據能更新到UI上,于是就有了Handler,它幫助我們實現了非UI線程向UI線程之間的通信,Handler的使用非常簡單。
一個簡單的示例,讓一個TextView動態顯示秒數:
1 public class MainActivity extends Activity { 2 private static final int UPDATE_TAG = 0x01; 3 private TextView textView; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.activity_main); 9 textView = (TextView) findViewById(R.id.textview); 10 // 啟動一個線程 11 new Thread(new Runnable() { 12 13 @Override 14 public void run() { 15 int count = 1; 16 while (true) { 17 // 發送計數值到handler 18 mHandler.obtainMessage(UPDATE_TAG, String.valueOf(count)) 19 .sendToTarget(); 20 try { 21 TimeUnit.SECONDS.sleep(1); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 } 27 28 }).start(); 29 } 30 31 public Handler mHandler = new Handler() { 32 public void handleMessage(Message msg) { 33 switch (msg.what) { 34 case UPDATE_TAG: 35 // 顯示計數值 36 textView.setText((String)msg.obj); 37 break; 38 } 39 } 40 }; 41 }通過開啟一個線程,線程每隔一秒發送一次計數給handler,讓handler來更新TextView的內容,新開的子線程里面不能直接操控TextView,因為這違反了UI線程了單線程控制規范,如果你真要這么做,一運行就會得到CalledFromWrongThreadException異常。
二、這不就是一個觀察著模式么?
最初自己理解的Handler就是一個典型的觀察著模式。sendToTarget()這個方法一定以某種方式調用了Handler的?handleMessage(Message msg)方法,從而完成了我們指定的任務,于是我寫了下面這樣一個Handler和Message
/**** 模擬Android的Handler,申明一個供子類覆蓋的方法和多個生成消息的方法*/ public class Handler {/**** 子類覆蓋本方法,實現想要的操作* * @param msg*/protected void handleMessage(Message msg) {}public Message obtainMessage(int what) {Message msg = new Message(this, what, -1, -1, null);return msg;}public Message obtainMessage(int what, Object obj) {Message msg = new Message(this, what, -1, -1, obj);return msg;}public Message obtainMessage(int what, int arg1, int arg2, Object obj) {Message msg = new Message(this, what, arg1, arg2, obj);return msg;} }下來就是Message類:
/*** * 模擬Android的Message,這里為方便演示,只寫了一個構造方法。構造方法中綁定了一個Handler*/ public class Message {private final Handler target;public final int what;public final int arg1;public final int arg2;public final Object obj;public Message(Handler target, int what, int arg1, int arg2, Object obj) {this.target = target;this.arg1 = arg1;this.arg2 = arg2;this.what = what;this.obj = obj;}/**** 利用OOP的多態,調用子類的覆蓋方法*/public void sendToTarget() {target.handleMessage(this);} }測試代碼如下:
public class HandlerTest {public static void main(String[] args) {new Thread(new MyTask(Myhandler)).start();}static class MyTask implements Runnable {private Handler handler;public MyTask(Handler handler) {this.handler = handler;}@Overridepublic void run() {while (true) {handler.obtainMessage(0, this.toString()).sendToTarget();}}}static Handler Myhandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 0:System.out.println("I am zero " + (String) msg.obj);break;}}};} View Code真想說上面的代碼體現了一個簡單的觀察著模式,上面的測試代碼能運行。但是,但是這樣的Handler能代替Android的Handler在Activity中使用嗎,用上面的Handler去替代Android的handler,結果得到是CalledFromWrongThreadException。
這很顯然的,上面的Handler可以只模仿到Android Handler的形,沒有模仿到其神,它只是通過一個回調模仿了Handler的工作外表,利用多態的伎倆去調用子類的handleMessage方法,想法是好的,但這最終相當于讓子線程直接調用了handleMessage,從而讓子線程對界面控件進行了操控,違背了UI界面的單線程控制原則,必須會報CalledFromWrongThreadException的異常。
?
三、子線程是如何把消息傳遞給UI線程
前面仿造的Handler讓子線程通過Message操縱了UI控件,因此會報錯,那Android是如何將子進程的消息發送給UI進程。實際上Handler的工作離不開以下幾個組件:
- Message: Handler接收和處理的對象
- MessageQueue:消息隊列,以FIFO的方式管理Message對象
- Looper:管理MessageQueue的對象,不斷地從MessageQueue中取消息,并發送該消息對應的Handler進行處理。每個線程只有一個Looper,UI線程在創建時就已經默認創建了一個Looper,因此在Activity中可以直接創建Handler即可發送和處理消息;如果在子線程中創建Handler,必須在創建前調用Looper.prepare();這樣Looper在才能做好準備,并為當前線程創建MessageQueue,以便接受Handler的Message。可以看一下Looper.prepare()的源碼 public static void prepare() {prepare(true);}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}private Looper(boolean quitAllowed) {//私有的構造方法mQueue = new MessageQueue(quitAllowed);mRun = true;mThread = Thread.currentThread();}
在子進程創建好Handler后,調用Looper.loop()處理消息。Looper.loop()是一個死循環,如果不執行quit(),其后的代碼將不會執行。現在看一個例子,我們通過點擊Button給子線程的Hanlder傳遞消息。
public class MainActivity extends Activity {private Mythread thread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);thread = new Mythread();thread .start();}class Mythread extends Thread {public Handler handler;@Overridepublic void run() {Looper.prepare();// 在子線程中定義Handlerhandler = new Handler() {public void handleMessage(Message msg) {Log.i("handleMessage", "子線程handler.handleMessage(...)");// 接到消息讓Looper退出死循環 handler.getLooper().quit();}};Toast.makeText(MainActivity.this, "before Looper.loop()",Toast.LENGTH_LONG).show();// 死循環處理消息 Looper.loop();Toast.makeText(MainActivity.this, "after Looper.loop()",Toast.LENGTH_LONG).show();}};public void onClickButton(View v) {thread.handler.obtainMessage().sendToTarget();} }
可以看到線程啟動后,會Toast"before Looper.loop()",但不會立刻Toast"after?Looper.loop()",只有當我們按下Button后才會顯示"after?Looper.loop()",因為按下Button,會給子線程的Handler發送一個空消息,而我們定義Handler對消息的處理是調用looper的quit(),從而結束Looper.loop()。注意quit()并不是簡單的終止Looper.loop(),而是設置MessageQueue的一個標志,讓MessageQueue的next()方法返回null,從而結束Looper.loop(),詳情請看MessageQueue源碼,這里我們來看看loop()的源碼:
- public static void loop() {...for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}...msg.target.dispatchMessage(msg);...msg.recycle();}}
另外跟蹤sendToTarget()發現最終調用的方法是MessageQueue的enqueueMessage(Message msg, long when),這個方法只是把message放入隊列中,并沒有調用消息消息方法,消息的分發處理是在Looper的loop()中進行的。
?
四、是什么東西再幫UI線程跑Looper.loop()
現在理清了前面的問題又來了新的問題,是誰在幫UI線程跑Looper.loop():
- 如果是其他線程幫它Looper.loop(),那么這明顯違背了UI線程的單線程控制規范;
- 如果是UI線程自己在跑,那它的死循環在那里跑著,怎么用工夫去執行onCreate(),onResume()...以及我們綁定的各種listener。
如果既要滿足UI線程的單線程控制規范,又要讓死循環Looper.loop()跑著不漏掉一個消息,還要響應onCreate()、listener,只有一種辦法,那就是對界面的所有的操作都是由loop來驅動分發的,onCreate(),onResume()...各種listener都是Looper.loop()來調用。
實際上Android就是這么干的,要理清onCreate等各種方法綁定的Message的過程不容易,但是我們可以很簡單地驗證我們的上面的假設,那就是在onCreate(),onResume(),以及listener的響應方法里面引發異常,然后看一下棧信息,我們在onCreate發放中加一句int a = 1/0,看看異常的棧信息:
當發現神奇的Handler原來就是靠一個死循環來維持的,一切就變得豁然開朗了。
如果再深入,理解Activity的啟動機制,我們最終找到UI線程的Looper.loop()是在ActivityThread中的main方法中執行的,讓我們再看看源碼:
public final class ActivityThread {...public static void main(String[] args) {SamplingProfilerIntegration.start();// CloseGuard defaults to true and can be quite spammy. We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);Environment.initForCurrentUser();// Set the reporter for event logging in libcoreEventLogger.setReporter(new EventLoggingReporter());Security.addProvider(new AndroidKeyStoreProvider());Process.setArgV0("<pre-initialized>");//為主線程開啟Looper Looper.prepareMainLooper();ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}AsyncTask.init();if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}//UI線程的死循環在這里 Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");} }??
五、真的能自己寫一個Handler供Activity調用嗎
現在回到我最初的問題,我們可以自己寫一個Handler供Activity調用嗎?答案已經很明顯了,如果你足夠強,你真實現了一個功能一樣的Handler,那么恭喜你,你是在重寫了整個Activity組件。
?
以上是我對Handler的理解,希望對你有所幫助,如果有總結得不恰當的地方,歡迎指正。
?
?感謝閱讀,轉載請注明出處:http://www.cnblogs.com/fengfenggirl/?
轉載于:https://www.cnblogs.com/fengfenggirl/p/android_handler.html
總結
以上是生活随笔為你收集整理的我理解的Hanlder--android消息传递机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 导航栏动画的效果
- 下一篇: 重新组织函数--《重构》阅读笔记