第一次接觸Android應(yīng)用程序(這里指的是Java層的UI程序,,也難怪了,,Google放出的API就只支持JAVA應(yīng)用程序了),,很難搞明白內(nèi)部是如何實現(xiàn)的。但是,,從原理上分析,,應(yīng)該是有一個消息循環(huán),一個消息隊列,,然后主線程不斷得從消息隊列中取得消息并處理之,。
然而,google封裝得太厲害了,,所以一時半會還是搞不清楚到底是怎么做的,。本文將分析android內(nèi)的looper,這個是用來封裝消息循環(huán)和消息隊列的一個類,,handler其實可以看做是一個工具類,,用來向消息隊列中插入消息的。好比是Windows API的SendMessage中的HANDLE,,這個handle是窗口句柄,。
- //Looper類分析
- //沒找到合適的分析代碼的辦法,只能這么來了,。每個重要行的上面都會加上注釋
- //功能方面的代碼會在代碼前加上一段分析
- public class Looper {
- //static變量,,判斷是否打印調(diào)試信息。
- private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
-
- // sThreadLocal.get() will return null unless you've called prepare().
- //線程本地存儲功能的封裝,,TLS,,thread local storage,什么意思呢?因為存儲要么在棧上,,例如函數(shù)內(nèi)定義的內(nèi)部變量,。要么在堆上,例如new或者malloc出來的東西
- //但是現(xiàn)在的系統(tǒng)比如Linux和windows都提供了線程本地存儲空間,,也就是這個存儲空間是和線程相關(guān)的,,一個線程內(nèi)有一個內(nèi)部存儲空間,這樣的話我把線程相關(guān)的東西就存儲到
- //這個線程的TLS中,,就不用放在堆上而進(jìn)行同步操作了,。
- private static final ThreadLocal sThreadLocal = new ThreadLocal();
- //消息隊列,MessageQueue,,看名字就知道是個queue..
- final MessageQueue mQueue;
- volatile boolean mRun;
- //和本looper相關(guān)的那個線程,,初始化為null
- Thread mThread;
- private Printer mLogging = null;
- //static變量,代表一個UI Process(也可能是service吧,,這里默認(rèn)就是UI)的主線程
- private static Looper mMainLooper = null;
-
- /** 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()}.
- */
- //往TLS中設(shè)上這個Looper對象的,,如果這個線程已經(jīng)設(shè)過了looper的話就會報錯
- //這說明,一個線程只能設(shè)一個looper
- public static final void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper());
- }
-
- /** 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.
- * {@link #prepare()}
- */
- //由framework設(shè)置的UI程序的主消息循環(huán),,注意,,這個主消息循環(huán)是不會主動退出的
- //
- public static final void prepareMainLooper() {
- prepare();
- setMainLooper(myLooper());
- //判斷主消息循環(huán)是否能退出....
- //通過quit函數(shù)向looper發(fā)出退出申請
- if (Process.supportsProcesses()) {
- myLooper().mQueue.mQuitAllowed = false;
- }
- }
-
- private synchronized static void setMainLooper(Looper looper) {
- mMainLooper = looper;
- }
-
- /** Returns the application's main looper, which lives in the main thread of the application.
- */
- public synchronized static final Looper getMainLooper() {
- return mMainLooper;
- }
-
- /**
- * Run the message queue in this thread. Be sure to call
- * {@link #quit()} to end the loop.
- */
- //消息循環(huán),,整個程序就在這里while了。
- //這個是static函數(shù)喔,!
- public static final void loop() {
- Looper me = myLooper();//從該線程中取出對應(yīng)的looper對象
- MessageQueue queue = me.mQueue;//取消息隊列對象...
- while (true) {
- Message msg = queue.next(); // might block取消息隊列中的一個待處理消息..
- //if (!me.mRun) {//是否需要退出,?mRun是個volatile變量,跨線程同步的,,應(yīng)該是有地方設(shè)置它,。
- // break;
- //}
- if (msg != null) {
- if (msg.target == null) {
- // No target is a magic identifier for the quit message.
- return;
- }
- if (me.mLogging!= null) me.mLogging.println(
- ">>>>> Dispatching to " + msg.target + " "
- + msg.callback + ": " + msg.what
- );
- msg.target.dispatchMessage(msg);
- if (me.mLogging!= null) me.mLogging.println(
- "<<<<< Finished to " + msg.target + " "
- + msg.callback);
- msg.recycle();
- }
- }
- }
-
- /**
- * Return the Looper object associated with the current thread. Returns
- * null if the calling thread is not associated with a Looper.
- */
- //返回和線程相關(guān)的looper
- public static final Looper myLooper() {
- return (Looper)sThreadLocal.get();
- }
-
- /**
- * Control logging of messages as they are processed by this Looper. If
- * enabled, a log message will be written to <var>printer</var>
- * at the beginning and ending of each message dispatch, identifying the
- * target Handler and message contents.
- *
- * @param printer A Printer object that will receive log messages, or
- * null to disable message logging.
- */
- //設(shè)置調(diào)試輸出對象,looper循環(huán)的時候會打印相關(guān)信息,,用來調(diào)試用最好了,。
- public void setMessageLogging(Printer printer) {
- mLogging = printer;
- }
-
- /**
- * Return the {@link MessageQueue} object associated with the current
- * thread. This must be called from a thread running a Looper, or a
- * NullPointerException will be thrown.
- */
- public static final MessageQueue myQueue() {
- return myLooper().mQueue;
- }
- //創(chuàng)建一個新的looper對象,
- //內(nèi)部分配一個消息隊列,,設(shè)置mRun為true
- private Looper() {
- mQueue = new MessageQueue();
- mRun = true;
- mThread = Thread.currentThread();
- }
-
- public void quit() {
- Message msg = Message.obtain();
- // NOTE: By enqueueing directly into the message queue, the
- // message is left with a null target. This is how we know it is
- // a quit message.
- mQueue.enqueueMessage(msg, 0);
- }
-
- /**
- * Return the Thread associated with this Looper.
- */
- public Thread getThread() {
- return mThread;
- }
- //后面就簡單了,打印,,異常定義等,。
- public void dump(Printer pw, String prefix) {
- pw.println(prefix + this);
- pw.println(prefix + "mRun=" + mRun);
- pw.println(prefix + "mThread=" + mThread);
- pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
- if (mQueue != null) {
- synchronized (mQueue) {
- Message msg = mQueue.mMessages;
- int n = 0;
- while (msg != null) {
- pw.println(prefix + " Message " + n + ": " + msg);
- n++;
- msg = msg.next;
- }
- pw.println(prefix + "(Total messages: " + n + ")");
- }
- }
- }
-
- public String toString() {
- return "Looper{"
- + Integer.toHexString(System.identityHashCode(this))
- + "}";
- }
-
- static class HandlerException extends Exception {
-
- HandlerException(Message message, Throwable cause) {
- super(createMessage(cause), cause);
- }
-
- static String createMessage(Throwable cause) {
- String causeMsg = cause.getMessage();
- if (causeMsg == null) {
- causeMsg = cause.toString();
- }
- return causeMsg;
- }
- }
- }
那怎么往這個消息隊列中發(fā)送消息呢?,?調(diào)用looper的static函數(shù)myQueue可以獲得消息隊列,,這樣你就可用自己往里邊插入消息了。不過這種方法比較麻煩,,這個時候handler類就發(fā)揮作用了,。先來看看handler的代碼,就明白了,。
- class Handler{
- ..........
- //handler默認(rèn)構(gòu)造函數(shù)
- public Handler() {
- //這個if是干嘛用的暫時還不明白,,涉及到j(luò)ava的深層次的內(nèi)容了應(yīng)該
- if (FIND_POTENTIAL_LEAKS) {
- final Class<? extends Handler> 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對象
- //如果本線程還沒有設(shè)置looper,這回拋異常
- mLooper = Looper.myLooper();
- if (mLooper == null) {
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- //無恥啊,,直接把looper的queue和自己的queue搞成一個了
- //這樣的話,,我通過handler的封裝機(jī)制加消息的話,就相當(dāng)于直接加到了looper的消息隊列中去了
- mQueue = mLooper.mQueue;
- mCallback = null;
- }
- //還有好幾種構(gòu)造函數(shù),,一個是帶callback的,,一個是帶looper的
- //由外部設(shè)置looper
- public Handler(Looper looper) {
- mLooper = looper;
- mQueue = looper.mQueue;
- mCallback = null;
- }
- // 帶callback的,一個handler可以設(shè)置一個callback,。如果有callback的話,,
- //凡是發(fā)到通過這個handler發(fā)送的消息,都有callback處理,,相當(dāng)于一個總的集中處理
- //待會看dispatchMessage的時候再分析
- public Handler(Looper looper, Callback callback) {
- mLooper = looper;
- mQueue = looper.mQueue;
- mCallback = callback;
- }
- //
- //通過handler發(fā)送消息
- //調(diào)用了內(nèi)部的一個sendMessageDelayed
- public final boolean sendMessage(Message msg)
- {
- return sendMessageDelayed(msg, 0);
- }
- //FT,,又封裝了一層,這回是調(diào)用sendMessageAtTime了
- //因為延時時間是基于當(dāng)前調(diào)用時間的,,所以需要獲得絕對時間傳遞給sendMessageAtTime
- public final boolean sendMessageDelayed(Message msg, long delayMillis)
- {
- if (delayMillis < 0) {
- delayMillis = 0;
- }
- return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
- }
-
-
- public boolean sendMessageAtTime(Message msg, long uptimeMillis)
- {
- boolean sent = false;
- MessageQueue queue = mQueue;
- if (queue != null) {
- //把消息的target設(shè)置為自己,,然后加入到消息隊列中
- //對于隊列這種數(shù)據(jù)結(jié)構(gòu)來說,,操作比較簡單了
- msg.target = this;
- sent = queue.enqueueMessage(msg, uptimeMillis);
- }
- else {
- RuntimeException e = new RuntimeException(
- this + " sendMessageAtTime() called with no mQueue");
- Log.w("Looper", e.getMessage(), e);
- }
- return sent;
- }
- //還記得looper中的那個消息循環(huán)處理嗎
- //從消息隊列中得到一個消息后,會調(diào)用它的target的dispatchMesage函數(shù)
- //message的target已經(jīng)設(shè)置為handler了,,所以
- //最后會轉(zhuǎn)到handler的msg處理上來
- //這里有個處理流程的問題
- public void dispatchMessage(Message msg) {
- //如果msg本身設(shè)置了callback,,則直接交給這個callback處理了
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- //如果該handler的callback有的話,則交給這個callback處理了---相當(dāng)于集中處理
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- //否則交給派生處理,基類默認(rèn)處理是什么都不干
- handleMessage(msg);
- }
- }
- ..........
- }
講了這么多,,該怎么創(chuàng)建和使用一個帶消息循環(huán)的線程呢,?
- //假設(shè)在onCreate中創(chuàng)建一個線程
- //不花時間考慮代碼的完整和嚴(yán)謹(jǐn)性了,以講述原理為主,。
- ....
-
- ... onCreate(...){
-
- //難點(diǎn)是如何把a(bǔ)ndroid中的looper和java的thread弄到一起去,。
- //而且還要把隨時取得這個looper用來創(chuàng)建handler
- //最簡單的辦法就是從Thread派生一個
- class ThreadWithMessageHandle extends Thread{
- //重載run函數(shù)
- Looper myLooper = null;
- run(){
- Looper.prepare();//將Looper設(shè)置到這個線程中
- myLooper = Looper.myLooper();
- Looper.loop();開啟消息循環(huán)
- }
-
- ThreadWithMessageHandle threadWithMgs = new ThreadWithMessageHandle();
- threadWithMsg.start();
- Looper looper = threadWithMsg.myLooper;//
- //這里有個問題.threadWithMgs中的myLooper可能此時為空
- //需要同步處理一下
- //或者像API文檔中的那樣,把handler定義到ThreadWithMessageHandle到去,。
- //外線程獲得這個handler的時候仍然要注意同步的問題,,因為handler的創(chuàng)建是在run中的
- Handler threadHandler = new Handler(looper);
- threadHandler.sendMessage(...)
- }
-
-
- }
-
-
-
- ...
好了,handler和looper的分析就都這了,,其實原理挺簡單的,。
|