前言
Android應(yīng)用程序是基礎(chǔ)Java語(yǔ)言,內(nèi)部封裝了消息隊(duì)列红符,然后在主線程開啟了死循環(huán)不斷獲取消息并處理來(lái)實(shí)現(xiàn)不間斷的界面變化與各種操作的執(zhí)行。這個(gè)過程主要由:Looper、Handler、Message三者來(lái)共同完成默刚,Message作為消息載體,Looper用于封裝消息隊(duì)列逃魄,Handler用于插入與處理消息荤西。
Looper簡(jiǎn)析
首先,看看Looper類的幾個(gè)成員變量
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
sThreadLoca并非是一個(gè)線程的實(shí)現(xiàn)版本伍俘,它并不是一個(gè)Thread邪锌,而是線程局部變量。簡(jiǎn)單地說(shuō)癌瘾,它是一種較為特殊的線程綁定機(jī)制觅丰。很顯然,這里已經(jīng)暴露了Looper與線程之間肯定有一些“特殊的聯(lián)系”妨退。
sMainLooper是個(gè)Looper對(duì)象妇萄,很顯然Looper不可能是單例實(shí)現(xiàn)蜕企,并且從命名上我們也可以很容易看出來(lái)該對(duì)象,它可能是主線程/UI線程的Looper對(duì)象冠句,咱們看到prepareMainLooper方法中初始化了這個(gè)對(duì)象
/**
* 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();
}
}
從方法注釋可以得知轻掩,該方法是實(shí)例化一個(gè)屬于該應(yīng)用主Looper(暫且這么理解),它是由系統(tǒng)自動(dòng)調(diào)用懦底,不需要你手動(dòng)調(diào)用該方法
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
getMainLooper方法是提供獲取主線程Looper對(duì)象唇牧,也就是說(shuō)sMainLooper確實(shí)是屬于主線程的Looper對(duì)象,并且它在應(yīng)用開始時(shí)就被初始化了
具體的初始化位置于ActivityThread#main()方法中基茵,有興趣可自行了解
mQueue 是一個(gè)MessageQueue對(duì)象奋构,咱們來(lái)看看官方對(duì)它的解釋
/**
* Low-level class holding the list of messages to be dispatched by a
* {@link Looper}. Messages are not added directly to a MessageQueue,
* but rather through {@link Handler} objects associated with the Looper.
*
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
public final class MessageQueue {...}
我粗俗的翻譯下,MessageQueue是一個(gè)低級(jí)類拱层,通過Looper提供消息集合的派發(fā)弥臼,消息不會(huì)直接被添加到MessageQueue,而是通過Handler對(duì)象與其關(guān)聯(lián)Looper對(duì)象根灯【睹澹總結(jié)下,就是MessageQueue的作用只是簡(jiǎn)單是存儲(chǔ)消息烙肺,具體的消息存取是由Handler與Looper來(lái)觸發(fā)與管理纳猪。
之前咱們提到,Looper很可能與線程間存在密切聯(lián)系桃笙。mThread成員變量再次證實(shí)了咱們的想法氏堤,怎么說(shuō)?不急搏明,咱們現(xiàn)在先分析下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));
}
初始化生成當(dāng)前線程的Looper對(duì)象,只要當(dāng)prepare方法執(zhí)行了之后對(duì)應(yīng)線程中才可以創(chuàng)建Handler對(duì)象星著,并且必須確保在loop執(zhí)行前調(diào)用购笆。從代碼中可以看出來(lái),此時(shí)sThreadLocal對(duì)象被實(shí)例化并與當(dāng)前線程綁定虚循,What同欠?你是想說(shuō)sThreadLocal.set(new Looper(quitAllowed));就完成了綁定?從哪得出線程信息了横缔?好铺遂,我們?cè)賮?lái)看看構(gòu)造器。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
OK剪廉,mThread = Thread.currentThread()娃循,mThread現(xiàn)身了,引用了當(dāng)前線程
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();
}
}
注釋已經(jīng)給了明確的意思斗蒋,它是開始循環(huán)派發(fā)當(dāng)前線程的消息隊(duì)列捌斧,可以調(diào)用quit停止笛质。咱們?cè)倏纯捶椒▋?nèi)部細(xì)節(jié),在開始真正之前調(diào)用myLooper方法獲取了sThreadLocal.get()判斷若為空捞蚂,表示當(dāng)前線程并沒有實(shí)例化過Looper妇押,即并沒有調(diào)用prepare方法,于是拋出異常姓迅。
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
...
for (;;) {
...
final long newIdent = Binder.clearCallingIdentity();
}
這里涉及到IPC通信中的UID與PIC識(shí)別敲霍,存儲(chǔ)于IPCThreadState中的mCallingPid與mCallingUid,簡(jiǎn)單的解釋下丁存。假設(shè)processA對(duì)processB進(jìn)行了遠(yuǎn)程調(diào)用肩杈,processB必須攜帶processA的pid與uid,用于A端的權(quán)限校驗(yàn)解寝。因此上述代碼塊的意思很明了:在每次派發(fā)消息前清除IPC身份標(biāo)識(shí)扩然,并判斷身份是否變化
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);
}
獲取隊(duì)列中的下一個(gè)消息,獲取Looper的日志輸出對(duì)象聋伦。OK夫偶,開始派發(fā)消息,在處理消息的前后輸出日志
需要注意的觉增,loop方法中若成功執(zhí)行兵拢,那里面執(zhí)行for死循環(huán),將會(huì)阻塞后續(xù)代碼逾礁。即在loop調(diào)用后说铃,后續(xù)的代碼將永遠(yuǎn)執(zhí)行不到,不信你看~
ActivityThread#main(String[] args)
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Message淺析
Message嘹履,消息載體截汪。包括:身份標(biāo)識(shí)what、數(shù)值變量arg1與arg2(用于額外數(shù)據(jù)較簡(jiǎn)單的情況下使用)植捎、數(shù)據(jù)obj(Object類型)、replyTo(用于IPC阳柔,與本篇內(nèi)容)焰枢、數(shù)據(jù)data(Bunlde類型)、target對(duì)象(存儲(chǔ)目標(biāo)Handler對(duì)象)
/**
*
* 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.
*
* <p class="note">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.</p>
*/
public final class Message implements Parcelable {...}
obtain方法
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
...
}
/**
* Same as {@link #obtain()}, but copies the values of an existing
* message (including its target) into the new one.
* @param orig Original message to copy.
* @return A Message object from the global pool.
*/
public static Message obtain(Message orig) {
...
}
/**
* Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
* @param h Handler to assign to the returned Message object's <em>target</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
實(shí)現(xiàn)Message的復(fù)用舌剂,內(nèi)部緩存是用一個(gè)鏈表形式的全局緩存池實(shí)現(xiàn)济锄,一共有八個(gè)重載方法。各個(gè)方法的差異只是對(duì)于獲取到的Message對(duì)象各信息賦值霍转。
既然使用了緩存荐绝,那么肯定有回收消息的方法。對(duì)于被回收了消息避消,清除了對(duì)象信息后低滩,若鏈表緩存池未滿召夹,會(huì)把該消息放到鏈表的首部。結(jié)合recycleUnchecked與obtain方法可以看出恕沫,這是個(gè)后進(jìn)先出的鏈表监憎。
public void recycle() {
// check ...
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
總結(jié),Message對(duì)象作為消息內(nèi)容的載體婶溯,它的主要核心就是實(shí)現(xiàn)了鏈表緩存與對(duì)于消息內(nèi)容的存取與維護(hù)
Handler淺析
Handler鲸阔,簡(jiǎn)單總結(jié)下源碼注釋
1.Handler用于處理與其自身關(guān)聯(lián)的線程中的消息隊(duì)列
2.一個(gè)Handler只對(duì)應(yīng)一個(gè)Looper與一個(gè)線程
3.Handler的主要兩個(gè)作用:消息的調(diào)度與處理;在不同線程間執(zhí)行操作
/**
* 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.
*
* <p>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.
*
* <p>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 <em>post</em> versions allow
* you to enqueue Runnable objects to be called by the message queue when
* they are received; the <em>sendMessage</em> 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).
*
* <p>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.
*
* <p>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 <em>post</em> or <em>sendMessage</em> 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 {...}
咱們主要看兩方面內(nèi)容
1.Handler與Looper之間的關(guān)聯(lián)
2.Handler與Message之間的關(guān)聯(lián)
/**
* 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 represent 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) {
...
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;
}
在Handler實(shí)例化的時(shí)候迄委,判斷了當(dāng)前線程對(duì)應(yīng)的Looper對(duì)象是否已創(chuàng)建褐筛。這也證明了在分析Looper時(shí)對(duì)Looper與線程之間存在聯(lián)系的判斷,以及Looper與線程是一一對(duì)應(yīng)的叙身。
public final Message obtainMessage(...)
{
...
}
obtainMessage的四個(gè)重載方法渔扎,分別調(diào)用了相關(guān)的Message.obtain(…)
發(fā)送消息的兩種方式:Message與Runnbale,這里我就拿delayed舉例
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
從上面的代碼可以看出曲梗,發(fā)送Runnable實(shí)質(zhì)上也是Message對(duì)象赞警,只不過Runnable方式會(huì)為Message對(duì)象的callback對(duì)象賦值
消息處理
/**
* 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);
}
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
消息處理時(shí)首先會(huì)判斷callback對(duì)象,若不為空則執(zhí)行對(duì)應(yīng)的回調(diào)代碼虏两。否則將空方法handleMessage進(jìn)行處理愧旦,如果想要接收到消息后進(jìn)行對(duì)應(yīng)的事件操作,那么handleMessage是子類必須重寫的方法定罢。
三者關(guān)系
用一張圖來(lái)解釋他們?nèi)咧g的聯(lián)系