作者:@JohnTsai
本文為作者原創(chuàng),轉(zhuǎn)載請注明出處:http://www.cnblogs.com/JohnTsai/p/5259869.html
在學(xué)習(xí)Handler之前,首先要學(xué)習(xí)一些基本概念,這將對之后的學(xué)習(xí)有所幫助。
下面用代碼來解釋什么是主線程和ANR:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); //查看當(dāng)前線程, 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(); }}
參考:
2.What is the Android UiThread (UI thread)
首先我們通過一個Demo學(xué)習(xí)Handler的兩種基本用途:
第一種用途比較好理解,比如延時finish掉啟動Activity。第二種是我們在經(jīng)常需要子線程中執(zhí)行耗時操作,可能是讀取文件或訪問網(wǎng)絡(luò),但是我們只能在主線程中更新UI。所以使用Handler來將更新UI的操作從子線程切換到主線程中執(zhí)行。
1.繼承Handler類,實(shí)現(xiàn)handleMessage方法。一般不推薦使用非靜態(tài)內(nèi)部類的方式實(shí)現(xiàn),會導(dǎo)致內(nèi)存泄露。具體原因可參看我上一篇文章,Android開發(fā)——避免內(nèi)存泄露。推薦使用靜態(tài)內(nèi)部類和弱引用結(jié)合的方式來實(shí)現(xiàn)。如下所示。
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方法發(fā)送Runnable對象,sendMessage方法來發(fā)送Message。
下面的代碼通過postDelayed執(zhí)行1秒后將Button顯示或隱藏的操作。在下載完圖片后使用sendMessage通知UI線程更新ImageView。
//使用Handler進(jìn)行延時操作 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();
運(yùn)行效果:
說起Handler,就不得不提Message、MessageQueue以及Looper。搞清楚這四者之間的關(guān)系,Android開發(fā)中的Handler消息處理機(jī)制也掌握了個大概了。
Message:包含描述和任意數(shù)據(jù)對象的消息,用于發(fā)送給Handler。
//獲取Message實(shí)例的方式Message msg1 = Message.obtain();//或Message msg2 = Handler.obtainMessage();
學(xué)到這里,可能有人會有疑問。不是可通過Handler發(fā)送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()方法內(nèi)部,將Runnable對象通過getPostMessage()方法包裝成了一個Message對象。這樣就回答了我們之前的疑問。對于Handler,不管是使用的是post類方法還是send類方法,發(fā)送出去的都是Message對象。
public final class Message implements Parcelable { public int what; public int arg1; public int arg2; public Object obj; ... }
Message類中有這幾個成員變量描述消息,其中what是我們定義的消息碼,為了讓接收者能知道消息是關(guān)于什么的。arg1和arg2用于發(fā)送一些integer類型的值。obj用于傳輸任意類型的值。
MessageQueue:顧名思義,消息隊(duì)列。內(nèi)部存儲著一組消息。對外提供了插入和刪除的操作。MessageQueue內(nèi)部是以單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲消息列表的。
獲取MessageQueue實(shí)例使用Looper.myQueue()方法。
查看源碼中的enqueueMessage()方法能看出MessageQueue是用單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲消息的。
//插入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內(nèi)部是以單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲消息的 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:主要用于給一個線程輪詢消息的。線程默認(rèn)沒有Looper,在創(chuàng)建Handler對象前,我們需要為線程創(chuàng)建Looper。
使用Looper.prepare()方法創(chuàng)建Looper,使用Looper.loop()方法運(yùn)行消息隊(duì)列。
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(); }
這里就有疑問了,之前我們在主線程中,創(chuàng)建Handler之前并沒有創(chuàng)建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。這就是為什么我們在主線程中不需要創(chuàng)建Looper的原因。
好,今天的學(xué)習(xí)就到這。下次將繼續(xù)學(xué)習(xí)Handler、Message和Message之間的關(guān)系。
如果本文對你的開發(fā)有所幫助,并且你手頭恰好有零錢。
不如打賞我一杯咖啡,鼓勵我繼續(xù)分享優(yōu)秀的文章。