編輯:關於Android編程
在早些年剛開始接觸Android開發時,就曾遇到過這樣一個異常,當我們在子線程中更新UI界面時會出現:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views,翻譯過來就是只有創建這個控件的線程才能去更新該控件的內容。可見當年真的是too young too simple。
既然UI更新只能在創建它的UI線程中更新,那麼Handler機制的出現也就理所當然了,它就是用來處理子線程更新UI線程的問題的。Handler負責與子線程進行通訊,從而讓子線程與UI線程之間建立起聯系,達到可以異步更新UI的效果。
說到Handler機制,又不得不提這麼幾個類:Message、Looper、MessageQueue、Handler。
看Message類的介紹:
/** * * Defines a message containing a description and arbitrary data object that can be * sent to a {@link Handler}. This object contains two extra int fields and an * extra object field that allow you to not do allocations in many cases. * *
While the constructor of Message is public, the best way to get * one of these is to call {@link #obtain Message.obtain()} or one of the * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull * them from a pool of recycled objects.
*/ public final class Message implements Parcelable { ...... }android.os.Message的主要功能是進行消息的封裝,同時可以指定消息的操作形式。使用它時有幾點要注意:
- 盡管Message有public的默認構造方法,但是你應該通過Message.obtain()來從消息池中獲得空消息對象,以節省資源;
- 如果你的message只需要攜帶簡單的int信息,請優先使用Message.arg1和Message.arg2來傳遞信息,這比用Bundle更省內存;
- 擅用message.what來標識信息,以便用不同方式處理message。
Android中對Looper的定義:
/** * Class used to run a message loop for a thread. Threads by default do * not have a message loop associated with them; to create one, call * {@link #prepare} in the thread that is to run the loop, and then * {@link #loop} to have it process messages until the loop is stopped. * *
Most interaction with a message loop is through the * {@link Handler} class. * *
This is a typical example of the implementation of a Looper thread, * using the separation of {@link #prepare} and {@link #loop} to create an * initial Handler to communicate with the Looper. */ public final class Looper { ...... }
由定義可知,Looper就是用來在一個Thread中進行消息的輪詢,使得一個普通的線程變成Looper線程(循環工作的線程)。
通過Looper類創建一個循環線程實例:
public class LooperThread extends Thread { @Override public void run() { // 將當前線程初始化為Looper線程 Looper.prepare(); // ...其他處理,如實例化handler // 開始循環處理消息隊列 Looper.loop(); } }
關鍵的代碼就是Looper.prepare()和Looper.loop(),那麼這兩行代碼到底做了什麼呢?讓我們看源碼:
1. Looper.prepare();
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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)); }
從Looper.prepare()可以看出,它創建了一個Looper的實例,Looper的構造函數是私有的,外部不能訪問。
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocalsThreadLocal = new ThreadLocal (); final MessageQueue mQueue; final Thread mThread; private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
可以看到Looper的內部維護了一個消息隊列MessageQueue以及一個線程對象。從以上源碼可看出,一個Thread只能有一個Looper實例。這也說明Looper.prepare()方法不能被調用兩次,同時一個Looper實例也只有一個MessageQueue。背後的核心就是將Looper對象定義為ThreadLocal。
2. Looper.loop();
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
可以看到,當調用Looper.loop()方法後,Looper線程就開始真正工作了,會一直輪循獲取消息,直到沒有消息返回退出。
我們也可以從裡面看到消息分發的調用msg.target.dispatchMessage(msg)。該調用表示將消息交給msg的target的dispatchMessage方法去處理,而msg的target就是我們之後要分析的Handler對象。
Looper中的myLooper()方法用於獲取當前線程的Looper對象:
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }
這裡可能有人有疑問,為何我們沒有主動執行Looper.prepare()方法,卻可以在UI線程中獲取到Looper.myLooper()對象呢?其實系統在啟動時已經初始化了UI線程的Looper對象了,具體源碼在SystemServer的run()方法及ActivityThread的main()方法中,通過執行Looper類的prepareMainLooper()來實現的:
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
而且Looper類也提供了獲取UI線程Looper實例的方法了。
/** Returns the application's main looper, which lives in the main thread of the application. */ public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }
這裡再次重申每個線程有且只能有一個Looper對象,每個線程的Looper對象都由ThreadLocal來維持。
先看下Handler類的定義:
/** * A Handler allows you to send and process {@link Message} and Runnable * objects associated with a thread's {@link MessageQueue}. Each Handler * instance is associated with a single thread and that thread's message * queue. When you create a new Handler, it is bound to the thread / * message queue of the thread that is creating it -- from that point on, * it will deliver messages and runnables to that message queue and execute * them as they come out of the message queue. * *
There are two main uses for a Handler: (1) to schedule messages and * runnables to be executed as some point in the future; and (2) to enqueue * an action to be performed on a different thread than your own. * *
Scheduling messages is accomplished with the * {@link #post}, {@link #postAtTime(Runnable, long)}, * {@link #postDelayed}, {@link #sendEmptyMessage}, * {@link #sendMessage}, {@link #sendMessageAtTime}, and * {@link #sendMessageDelayed} methods. The post versions allow * you to enqueue Runnable objects to be called by the message queue when * they are received; the sendMessage versions allow you to enqueue * a {@link Message} object containing a bundle of data that will be * processed by the Handler's {@link #handleMessage} method (requiring that * you implement a subclass of Handler). * *
When posting or sending to a Handler, you can either * allow the item to be processed as soon as the message queue is ready * to do so, or specify a delay before it gets processed or absolute time for * it to be processed. The latter two allow you to implement timeouts, * ticks, and other timing-based behavior. * *
When a * process is created for your application, its main thread is dedicated to * running a message queue that takes care of managing the top-level * application objects (activities, broadcast receivers, etc) and any windows * they create. You can create your own threads, and communicate back with * the main application thread through a Handler. This is done by calling * the same post or sendMessage methods as before, but from * your new thread. The given Runnable or Message will then be scheduled * in the Handler's message queue and processed when appropriate. */ public class Handler { ...... }
簡單的說,Handler就是用來和一個線程對應的MessageQueue進行打交道的,起到對Message及Runnable對象進行發送和處理(只處理自己發出的消息)的作用。每個Handler實例關聯了單個的線程和線程裡的消息隊列。
Handler創建時默認會關聯一個當前線程的Looper對象,通過構造函數我們也可以將其關聯到其它線程的Looper對象,先看下Handler默認的構造函數:
/** * Use the {@link Looper} for the current thread with the specified callback interface * and set whether the handler should be asynchronous. * * Handlers are synchronous by default unless this constructor is used to make * one that is strictly asynchronous. * * Asynchronous messages represent interrupts or events that do not require global ordering * with respect to synchronous messages. Asynchronous messages are not subject to * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. * * @param callback The callback interface in which to handle messages, or null. * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. * * @hide */ public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } //默認將關聯當前線程的looper mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
可以預知,當我們在主線程中創建默認的Hanler時不會有問題,而在子線程中用默認構造函數創建時一定會報錯,除非我們在Handler構造前執行Looper.prepare(),生成一個子線程對應的Looper實例才行,比如:
public class LooperThread extends Thread { private Handler handler1; private Handler handler2; @Override public void run() { // 將當前線程初始化為Looper線程 Looper.prepare(); // 實例化兩個handler handler1 = new Handler(); handler2 = new Handler(); // 開始循環處理消息隊列 Looper.loop(); } }
可以看到,一個線程是可以有多個Handler的,但是只能有一個Looper的實例。
Handler的發送消息主要通過sendMessage()方法,最終調用的就是sendMessageAtTime()方法:
/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) uptimeMillis. * The time-base is {@link android.os.SystemClock#uptimeMillis}. * Time spent in deep sleep will add an additional delay to execution. * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
其中msg.target = this就是將當前的Handler作為msg的target屬性,以確保確looper執行到該message時能找到處理它的Handler對象,對應Looper.loop()方法中的
msg.target.dispatchMessage(msg);
Handler可以在任意線程發送消息,而且消息的發送方式除了有sendMessage()還有post()方法。其實post發出的Runnable對象最後都被封裝成message對象了,看源碼:
/** * Causes the Runnable r to be added to the message queue. * The runnable will be run on the thread to which this handler is * attached. * * @param r The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
在開發中,我們常使用的
mHandler.post(new Runnable() { @Override public void run() { //更新UI操作 } });
其實並沒有創建線程,而只是發送了一條消息而已,最後通過Message的callback方法進行了處理而已。這裡就設計到Handler處理消息的機制了。
那麼Handler是如何處理消息的呢?其實在前面的Looper.loop()方法中我們已經可以看到了,就是通過msg.target.dispatchMessage(msg);來實現的,通過找到發送消息的Handler,然後由Handler自己的dispatchMessage()方法進行消息的分發處理。
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
也即Handler是在它關聯的Looper線程中進行處理消息的。Handler在收到消息後,先判斷消息內的回調Runnable是否為空(post(Runnable)等方法),為空的話,看是否實現內部回調接口,實現了的話由回調接口回調給用戶進行處理,否則由自身的handlerMessage()方法進行處理。
private static void handleCallback(Message message) { message.callback.run(); } /** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public interface Callback { public boolean handleMessage(Message msg); } /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }
通過這樣一種機制,就能解決在其他非主線程中更新UI的問題,通常的做法是:在activity中創建Handler實例並將其引用傳遞給worker thread,worker thread執行完任務後使用handler發送消息通知activity更新UI。
Demo實現的效果圖如下:工程目錄如下圖:一個Application,一個實體類,一個Activity,另一個是自定義的AutoPlayingViewPager繼承Fra
1、什麼是popWindow? popWindow就是對話框的一種方式!此文講解的android中對話框的一種使用方式,它叫popWindow。 2、popWindow的
.xml代碼如下: .java程序代碼如下: package org.lxh.demo; import java.util.Date;
Android調用系統相機和自定義相機實例 本博文主要是介紹了android上使用相機進行拍照並顯示的兩種方式,並且由於涉及到要把拍到的照片顯示出來,該例子也會涉及到An