在學習Handler以前,首先要學習一些基本概念,這將對以後的學習有所幫助。html
下面用代碼來解釋什麼是主線程和ANR:java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); //查看當前線程, Log.d("TAG", "current Thread's name:" + Thread.currentThread().getName()); //休眠UI線程5秒,製造ANR Thread thread = Thread.currentThread(); try { thread.sleep(5*1000); } catch (InterruptedException e) { e.printStackTrace(); } }
參考:android
首先咱們經過一個Demo學習Handler的兩種基本用途:網絡
第一種用途比較好理解,好比延時finish掉啓動Activity。第二種是咱們在常常須要子線程中執行耗時操做,多是讀取文件或訪問網絡,可是咱們只能在主線程中更新UI。因此使用Handler來將更新UI的操做從子線程切換到主線程中執行。數據結構
1.繼承Handler類,實現handleMessage方法。通常不推薦使用非靜態內部類的方式實現,會致使內存泄露。具體緣由可參看我上一篇文章,Android開發——避免內存泄露。推薦使用靜態內部類和弱引用結合的方式來實現。以下所示。app
private static class MyHandler extends Handler{ //對Activity的弱引用 private final WeakReference<HandlerActivity> mActivity; public MyHandler(HandlerActivity activity){ mActivity = new WeakReference<HandlerActivity>(activity); } @Override public void handleMessage(Message msg) { HandlerActivity activity = mActivity.get(); if(activity==null){ super.handleMessage(msg); return; } switch (msg.what) { case DOWNLOAD_FAILED: Toast.makeText(activity, "下載失敗", Toast.LENGTH_SHORT).show(); break; case DOWNLOAD_SUCCESS: Toast.makeText(activity, "下載成功", Toast.LENGTH_SHORT).show(); Bitmap bitmap = (Bitmap) msg.obj; activity.imageView.setVisibility(View.VISIBLE); activity.imageView.setImageBitmap(bitmap); break; default: super.handleMessage(msg); break; } } } private final MyHandler mHandler = new MyHandler(this);
2.使用post方法發送Runnable對象,sendMessage方法來發送Message。less
下面的代碼經過postDelayed執行1秒後將Button顯示或隱藏的操做。在下載完圖片後使用sendMessage通知UI線程更新ImageView。async
//使用Handler進行延時操做 mHandler.postDelayed(new Runnable() { @Override public void run() { imageView.setVisibility(imageView.getVisibility()==View.VISIBLE?View.GONE:View.VISIBLE); } },1000); //使用子線程下載圖片 new Thread(new Runnable() { @Override public void run() { Bitmap bitmap = downloadImage(); Message msg = Message.obtain(); msg.obj = bitmap; msg.what = bitmap==null?DOWNLOAD_FAILED:DOWNLOAD_SUCCESS; handler.sendMessage(msg); } }).start();
運行效果:
提及Handler,就不得不提Message、MessageQueue以及Looper。搞清楚這四者之間的關係,Android開發中的Handler消息處理機制也掌握了個大概了。
Message:包含描述和任意數據對象的消息,用於發送給Handler。
//獲取Message實例的方式 Message msg1 = Message.obtain(); //或 Message msg2 = Handler.obtainMessage();
學到這裏,可能有人會有疑問。不是可經過Handler發送Runnable對象的嗎?爲何這裏只提Message了呢?
好問題,讓咱們來查看post類方法的源碼。
public final boolean postDelayed(Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
在postDelayed()方法內部,將Runnable對象經過getPostMessage()方法包裝成了一個Message對象。這樣就回答了咱們以前的疑問。對於Handler,無論是使用的是post類方法仍是send類方法,發送出去的都是Message對象。
public final class Message implements Parcelable { public int what; public int arg1; public int arg2; public Object obj; ... }
Message類中有這幾個成員變量描述消息,其中what是咱們定義的消息碼,爲了讓接收者能知道消息是關於什麼的。arg1和arg2用於發送一些integer類型的值。obj用於傳輸任意類型的值。
MessageQueue:顧名思義,消息隊列。內部存儲着一組消息。對外提供了插入和刪除的操做。MessageQueue內部是以單鏈表的數據結構來存儲消息列表的。
獲取MessageQueue實例使用Looper.myQueue()方法。
查看源碼中的enqueueMessage()方法能看出MessageQueue是用單鏈表的數據結構來存儲消息的。
//插入Message操做 boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { //從這裏可看出MessageQueue內部是以單鏈表的數據結構來存儲消息的 prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
Looper:主要用於給一個線程輪詢消息的。線程默認沒有Looper,在建立Handler對象前,咱們須要爲線程建立Looper。
使用Looper.prepare()方法建立Looper,使用Looper.loop()方法運行消息隊列。
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); }
這裏就有疑問了,以前咱們在主線程中,建立Handler以前並無建立Looper。這是爲何呢?查看Main Thread源碼(也就是ActivityThread)。
public static void main(String[] args) { ... //初始化Looper Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } }
在ActivityThread的main方法中,初始化了Looper。這就是爲何咱們在主線程中不須要建立Looper的緣由。
好,今天的學習就到這。下次將繼續學習Handler、Message和Message之間的關係。
若是本文對你的開發有所幫助,而且你手頭剛好有零錢。
不如打賞我一杯咖啡,鼓勵我繼續分享優秀的文章。