Handler,Looper,MessageQueue的實現

所要用到的android源碼路徑:

ActivityThread源碼路徑:android-4.0.3_r1\frameworks\base\core\java\android\app\ActivityThread

Activity源碼路徑:android-4.0.3_r1\frameworks\base\core\java\android\app\Activity

Handler源碼路徑:android-4.0.3_r1\frameworks\base\core\java\android\os\Handler

Looper源碼路徑:android-4.0.3_r1\frameworks\base\core\java\android\os\Looper

Message源碼路徑:android-4.0.3_r1\frameworks\base\core\java\android\os\Message

MessageQueue源碼路徑:android-4.0.3_r1\frameworks\base\core\java\android\os\MessageQueue


我們從使用handler發送一條message到接收進行處理的這個過程來理解Handler,Looper,MessageQueue的實現,在Activity主線程中,會默認

創建一個Looper對象,在這個Looper類中會創建一個MessageQueue的消息隊列,然後主線程會調用Looper的的loop()方法循環遍歷這個消息隊

列,如果有消息則會通過Handler對象將消息傳遞到handlerMessage方法中進行處理,如果Hanlder對象爲空也就是沒有創建Hanlder對象則會將

這條消息丟掉。那麼消息的發送是怎樣的呢?我們創建一個Handler對象,使用這個對象發送Message,其實是使用Looper對象傳遞進來的

MessageQueue,通過這個消息隊列的引用將Message放進消息隊列中,由於Looper會遍歷這個隊列,所以Handler對象發送msg,將其放進消

息隊列,Looper遍歷消息隊列將消息取出,然後傳遞給Hanlder進行處理,這樣一個消息的發送與接收就完成了。

首先我們根據Handler的構造函數來了解Handler的幾種使用方法,從API中我們知道Handler有四個構造函數,分別是:

		Handler()
		Handler(callback)
		Handler(looper)
		Handler(looper, callback)

一、不帶參數的Handler()構造函數,其使用的是主線程默認的Looper對象以及MessageQueue

	Handler handler2 = new Handler(){
		
		public void handleMessage(Message msg) {
			
		};
	};
	
	handler2.sendEmptyMessage(30);
直接在Activity中new一個Handler對象,並且重載handlerMessage方法就能夠實現Message的發送與接收。

二、Handler(callback)構造函數,帶了一個Hanlder類中的一個Callable接口對象作爲參數,看源碼就能夠很清楚知道,他的作用其實是將handlerMessage

方法給獨立出去,也就是在Activity中如果handlerMessage中的代碼量太多,使用這個構造函數去創建對象,就能夠將handlerMessage方法不放在

Activity中。

public class MyHandler implements Handler.Callback{
	
	@Override
	public boolean handleMessage(Message arg0) {
		
		return false;
	}
}
先實現Hanlder.Callback接口,叢雜handlerMessage方法,在這裏進行消息的處理

		MyHandler callback = new MyHandler();		
		Handler handler3 = new Handler(callback);
		
		handler3.sendEmptyMessage(20);	
在Activity中創建Handler對象,將Callback對象作爲參數傳遞進去,然後使用handler發送Message,就能夠在MyHandler中接收消息進行處理。

三、Handler(looper)構造函數,帶了一個自定義的Looper對象作爲參數,所以我們只要自定義一個Looper,然後將該Looper對象傳遞進去就能夠正常

的進行消息發送與接收。但是我們不必要真的去自定義一個Looper類,我們只需要繼承android已經實現好的HandlerThread線程,就能夠自定義一個

Looper類

public class MyThread extends HandlerThread{

	//HandlerThread類是android中已經繼承Thread的類,他已經幫你實現了Looper對象的創建
	//所以如果我們在實現Handler時,需要自定義Looper對象,則直接繼承此類就行。
	public MyThread(String name) {
		super(name);
	}
	
	@Override
	protected void onLooperPrepared() {
		//這個方法是在Looper.loop()被調用前在線程HandlerThread的run方法中被調用
		//所以如果要在這之前做什麼操作,就需要重載這個方法
		Log.i("MyThread", "onLooperPrepared");
		super.onLooperPrepared();
	}
}

		MyThread thread = new MyThread("thread");
		thread.start();		
		Looper looper = thread.getLooper();
		
		Handler handler1 = new Handler(looper){			
			public void handleMessage(Message msg) {
				
			}
		};
		
		handler1.sendEmptyMessage(10);

這樣我們就能夠使用自定義的Looper進行消息的發送與接收,而不必使用Activity幫我們已經創建好的默認的Looper對象。


四、Handler(looper, callback)構造函數,知道了前面三種構造函數之後,這種就能夠很容易理解了:使用自定義Looper,將handlerMessage方法

從創建Handler對象的類中獨立出去。

Handler的詳細使用方法就介紹完了,接下來我們就來分析相關類的源碼,來徹底瞭解其運作過程。

首先我們來看看Looper的實現過程,我們知道在創建一個Activity時,其主UI主線程ActivityThread中會默認創建一個Looper對象,那麼這個對象是在

什麼地方創建的呢?

    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);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

step1:從上面ActivityThread源碼中我們可以看見,這個main方法是主線程的啓動的地方,首先他調用了Looper的靜態prepareMainLooper()方法:

    public static void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        myLooper().mQueue.mQuitAllowed = false;
    }

    public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

    private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }
在上面源碼中我們可以看出,prepareMainLooper方法中調用prepare()方法創建了一個Looper對象,並將這個Looper對象放進了ThreadLocal中,將

Looper對象放進ThreadLocal中的作用是,ThreadLocal會維護Looper對象中變量的副本。具體可以參考ThreadLocal的使用方法。然後調用myLooper

方法從ThreadLocal中獲取Looper對象,並將其賦值給主線程Looper對象的引用。在Looper構造函數中我們可以看見,他創建了一個MessageQueue

對象,同時獲取了Looper對象所在當前線程的引用。這樣一個Looper對象便創建成功。

step2: 然後判斷主線程的Handler是否爲空,如果爲空則默認創建一個Handler對象,但是在源碼中這個默認的Handler時候從來就沒使用過,
估計是既然已經默認創建了Looper,那麼也默認創建一個Handler對象。
step3: 創建主線程ActivityThread對象,然後調用loop()方法

    public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me.mQueue;

        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        
        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }

                long wallStart = 0;
                long threadStart = 0;

                // 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);
                    wallStart = SystemClock.currentTimeMicro();
                    threadStart = SystemClock.currentThreadTimeMicro();
                }

                msg.target.dispatchMessage(msg);

                if (logging != null) {
                    long wallTime = SystemClock.currentTimeMicro() - wallStart;
                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;

                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                    if (logging instanceof Profiler) {
                        ((Profiler) logging).profile(msg, wallStart, wallTime,
                                threadStart, threadTime);
                    }
                }

                // 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.recycle();
            }
        }
    }

在loop方法中,首先獲取之前創建的Looper對象,MessageQueue對象,清除所有之前的訪問標誌,然後下面就是一個while死循環,在這個while循環

中,通過Message msg = queue.next()不停的從消息隊列中獲取Message,然後判斷獲取的Message是否爲空,不爲空,則判斷這個Message的target

是否爲空,通過了解Message源碼我們知道,這個target其實就是一個Handler對象,也就是如果在沒有創建Handler對象,則這個Message不進行處理

直接返回,繼續去下一個Message。如果已經創建了Handler對象,則通過msg.target.dispatchMessage(msg)將從消息隊列中獲取的Message傳遞

到Handler中。

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

在dispatchMessage中將消息傳遞到handlerMessage(msg)中,這樣handler便接收到了一個Message。

通過上面三步,當一個Activity啓動時,其主線程ActivityThread創建成功後,會默認創建一個Looper對象,一個消息隊列。同時會循環遍歷這個消息

隊列,而不會管Handler對象有沒有創建。

下面我們來看Handler是怎樣發送一個消息進消息隊列的,首先了解Hanlder的初始化過程:

    public Handler() {
        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());
            }
        }

        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 = null;
    }

從Handler的構造函數中我們可以看到,通過Looper.myLooper()獲取Looper對象的引用,通過mLooper.mQueue獲取消息隊列的引用。

接下來我們看發送消息的方法:

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    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) {
            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;
    }
從上面源碼可以看到,調用sendMessage其實是調用sendMessageDelayed方法,在sendMessageDelayed方法中調用了sendMessageAtTime方法

sendMessageAtTime纔是最終發送Message的地方,在sendMessageAtTime中我們可以看到,首先判斷消息隊列的引用是否爲空,不爲空則調用

enqueueMessage方法將Message放進消息隊列中。在這個方法中我們還可以看到通過msg.target = this,Message獲取到了當前Handler對象的引用

final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        final boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

要理解enqueueMessage源碼,就必須結合Looper獲取消息的方法來理解:

    final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }

                // If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount == 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

在enqueueMessage中我們看到,首先判斷Message的target即所屬的Handler對象是否爲空,爲空則拋出RuntimeException異常,然後定義了一個

final boolean needWake變量,注意他沒有進行初始化,而且爲final類型,表明一旦其被初始化後就無法再次進行更改了,然後就是一個同步塊,在

這個同步塊中,首先獲取一個全局Message對象mMessages的引用,如果這個Message不是延遲性或者空的消息,那麼將這個msg賦值給這個全局

的Message對象mMessages,並且將mBlocked賦值給needWake,在MessageQueue源碼中,可以看見mBlocked定義時是沒有初始化的,那麼

在哪裏初始化呢?這就要看next()方法了。

我們知道在主UI線程創建時,Looper對象就會創建,就會調用loop()方法,就會調用next()方法循環遍歷消息隊列,由於一開始消息隊列爲空,所以

msg一定爲空,執行的只是下面這段代碼:

                if (pendingIdleHandlerCount < 0) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount == 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
從這裏就能夠看見,mBlocked一開始是初始化爲true的,所以在同步方法塊執行完畢後,會調用nativeWake方法。在next()方法中可以看見,其消息

操作的邏輯也是一個同步方法塊,而且注意到了沒有?他操作的Message不是從什麼隊列中獲取的,其實操作的就是那個全局的Message對象

mMessages,並且從MessageQueue的其他removeMessages等方法可以看到,所有操作全局對象mMessages都是在方法同步塊中的,所以我們

就應該能夠明白了MessageQueue不是用什麼消息隊列來保存Message的,而只是使用一個全局的mMessages來保存的,每次Looper循環遍歷的

都是mMessages對象,獲取到之後然後將這個對象置爲空,如果next()在調用,則其他方法調用處於阻塞狀態,如果enqueueMessage處於調用中

則其他調用的方法處於阻塞中,所以當有多個線程同時調用同一個Handler對象發送Message時,其中只有一個線程調用的方法是處於運行狀態的,

其他線程的調用是處於阻塞狀態的,爲什麼android會這樣進行處理呢?我覺得這麼處理的原因是以時間換取空間,Handler發送消息的邏輯是相對

簡單的,沒有什麼業務邏輯,也就是所需獲取執行的時間是非常短的,而且不會出現很多線程同時使用同一個Handler來發送Message。

所以到這裏我們就明白了MessageQueue的實現機制了:

1、MessageQueue不是什麼消息隊列,沒有使用到隊列,其使用的是一個全局對象來進行消息接收與發送

2、MessageQueue中所有的消息操作方法都是同步塊的,當有一個方法被執行時,其他方法是處於阻塞狀態的。

3、如果我們同時開啓非常多的線程使用同一Handler發送消息或者移除消息,那麼sendMessage(msg)不會立即返回結果的。

4、在多個線程中不要大量使用Handler來操作Message。他只適合於少量線程中進行使用。

這樣Handler的消息發送以及Looper的消息獲取就都剖析完了。Handler,Looper,MessageQueue的實現也就完全講完了。

總結:

在我們創建一個Activity時,會自動創建一個Looper對象,並且在Looper的構造方法中會創建一個MessageQueue的對象,當Activity的主線程啓動

時,會調用Looper的loop()方法循環檢查MessageQueue中的全局對象mMessages是否爲空,如果不爲空則會調用相應Handler的

dispatchMessage方法,將這個消息傳遞到Handler中的handleMessage方法中進行處理。當我們創建一個Handler對象,使用這個Handler對象

發送消息時,最終調用的是MessageQueue中的enqueueMessage,消息的所有的操作,如:發送,移除,遍歷都是在MessageQueue中進行

操作的,這也對應了面向對象的及迪米特法則,也就是所有該類的職責應該就在該類中完成,儘量少於外界進行聯繫。消息的處理是單個操作的

當一個消息在發送時,其他消息的發送是處於阻塞狀態,由於消息操作的業務邏輯簡單,所以不必過於關注消息操作的延時性,但是不應在大量

線程中使用Handler進行消息傳遞。










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章