一、处理者Handler类里的主要方法:
Handler():构造方法
sendMessage(Message msg):将消息发送到消息队列
post(Runnable r):将消息发送到消息队列
dispatchMessage(Message msg):将消息发送给对应的Handler
handleMessage(Message msg):根据某一个消息进行相关的处理和操作
(1)Handler构造方法
Hander具备多种构造函数:
public Handler()
public Handler(Callback callback)
public Handler(Looper looper)
public Handler(Looper looper ,CallBack callback)
等等。
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
//Looper.myLooper()作用:指定Looper对象,获取当前线程的Looper对象;
//若线程无Looper对象则抛出异常,也就无法创建Handler对象,需先创建Looper对象,也可以通过Loop.getMainLooper()获得当前进程的主线程的Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
// 绑定消息队列对象(MessageQueue),获取该Looper对象中保存的消息队列对象(MessageQueue)
mQueue = mLooper.mQueue;
}
Handler对象需要绑定线程才能使用,绑定方式需要指定Looper对象,因为会通过构造方法自动关联当前线程的Looper对象与对应的消息队列对象(MessageQueue),同时Looper对象本身已经绑定了对应线程,这样当绑定了 Looper对象所绑定的线程,从而就自动绑定了实现创建Handler对象操作的线程,绑定后Handler的消息处理会在绑定的线程中执行。 也就是说指定了Handler对象的 Looper对象 = 绑定到了Looper对象所在的线程。
至此,保证了handler对象关联上Looper对象中MessageQueue。
(2)发送消息
public void dispatchMessage(Message msg) {
// 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则执行handleCallback(msg),回调Runnable对象里复写的run()
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
//在mCallback不为空的情况下调用mCallback.handleMessage(msg)
//除了通过handlermessage来改变行为,还可以通过重载dispathMessage来解决
if (mCallback.handleMessage(msg)) {
return;
}
}
//若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息
//则执行handleMessage(msg),即回调复写的handleMessage(msg)
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
作用:属于处理者类(Handler)中的方法,派发消息到对应的Handler实例并根据传入的msg作出对应的操作,出队消息的归属者通过dispatchMessage(msg)进行分发,最终回调复写的handleMessage(Message msg),从而实现消息处理的操作,进行消息分发时会进行1次发送方式的判断:
当msg中有callback时,即msg.callback属性不为空,则调用message.callback.run(),其中的callback指的是Runnable,代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run()。
若msg.callback属性为空,那么看一下成员变量的mCallback是否为空,这个是Handler的构造方法传入的,不为空则代表使用了sendMessage(Message msg)发送消息,则回调复写的handleMessage(msg),如果mCallback也为空,则调用handleMessage方法,这个一般在Handler的子类中重写。
Handler 里藏着的 Callback 能干什么?
Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截,那么就意味着一个消息可以同时被 Callback 以及 Handler 处理。也就是说可以利用 Callback 这个拦截机制来拦截 Handler 的消息
(3)消息处理
public void handleMessage(Message msg) {
//是一个空方法
}
作用:handleMessage(msg)该方法为空方法
public interface Callback {
public boolean handleMessage(Message msg);
}
作用:在创建Handler实例时复写相当于自定义消息处理方式,Message对象的callback属性为传入的Runnable对象,即回调Runnable对象里复写的run()
(4)sendMessage发送消息
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);//调用逻辑
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
//调用逻辑,该方法会调用sendMessageAtTime()方法。
//其中第二个参数是执行消息的时间,是通过从开机到现在的毫秒数(手机睡眠的时间不包括在内)+ 延迟执行的时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 获取对应的消息队列对象(MessageQueue)
MessageQueue queue = mQueue;
// 调用了enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);//调用逻辑
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 将msg.target赋值为this,把当前的Handler实例对象作为msg的target属性当Looper的loop()中消息循环时
//会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息,就是将该消息派发给对应的Handler实例
msg.target = this;
//调用消息队列的enqueueMessage(), Handler发送的消息,最终是保存到消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
作用:属于处理器类(Handler)的方法,将消息发送到消息队列中(Message ->> MessageQueue),Handler发送消息的本质就是为该消息定义target属性(即本身实例对象)并将消息入队到绑定线程的消息队列中。
(5)post发送消息
// 在工作线程中 发送消息到消息队列中并指定操作UI内容需传入1个Runnable对象
mHandler.post(new Runnable() {
@Override
public void run() {
... // 需执行的UI操作
}
});
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
// 创建1个消息对象(Message),创建Message对象可用关键字new 或 Message.obtain(),
//使用Message.obtain()创建,因为Message内部维护了1个Message池,用于Message的复用
//使用obtain()直接从池内获取,从而避免使用new重新分配内存
Message m = Message.obtain();
// 将 Runable对象 赋值给消息对象(message)的callback属性
m.callback = r;
// 返回该消息对象
return m;
}
作用:getPostMessage(r)将传入的Runable对象封装成1个消息对象
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) {
// 获取对应的消息队列对象(MessageQueue)
MessageQueue queue = mQueue;
// 调用了enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 将msg.target赋值为this,把当前的Handler实例对象作为msg的target属性, Looper的loop()中消息循环时,会从消息队列中取出每个消息msg
//然后执行msg.target.dispatchMessage(msg)去处理消息,实际上则是将该消息派发给对应的Handler实例
msg.target = this;
// 调用消息队列的enqueueMessage(),Handler发送的消息,最终是保存到消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
作用:实际上从此处开始,则类似sendMessage(Message msg)将消息入队到消息队列,即最终是调用MessageQueue.enqueueMessage()
作用:与Handler.sendMessage()类似,区别在于不需外部创建消息对象,而是内部根据传入的Runnable对象 封装消息对象,同时回调的消息处理方法是通过复写Runnable对象的run()。所以不管是调用sendMessage,sendEmptyMessage,sendMessageDelayed还是其他发送一系列方法。最终都会调用sendMessageDelayed(Message msg, long delayMillis)方法。并调用enqueueMessage()方法。
二、消息队列类MessageQueue类的主要方法:
enqueueMessage(Message msg,long when):入队,将消息根据时间放入到消息队列中
next():出队,从消息队列中移出该信息
removeMessage():删除元素
nativeDestory():销毁一个MessageQueue
(1)消息入队
boolean enqueueMessage(Message msg, long when) {
...
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;
// 判断消息队列里有无消息,如果p==null表示消息队列为空
//当队列中没有消息或者当前进入的消息比消息队列中的其他消息等待时间短,那么就放在消息队列的头部
//如果此时消息队列处于阻塞状态则唤醒队列
if (p == null || when == 0 || when < p.when) {
//新的头部,如果阻塞就唤醒事件队列
msg.next = p;
mMessages = msg;
needWake = mBlocked;//这个mBlocked指示在nativepollOnce()中使用非零超时是否阻塞了next()等待
} else {
//按照时间顺序插入到队列中,不需要唤醒事件队列,除非在队列的顶部有一个屏障,并且消息是队列中最早的异步消息
//判断唤醒条件,当前消息队列头部消息是屏障消息,且当前插入的消息为异步消息,且当前消息队列处于无消息可处理的状态
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//循环遍历消息队列, 如果判断消息队列里有消息,则根据消息创建的时间 ,把当前进入的消息插入到队列中合适的位置(比较等待时间)
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//将消息插入合适的位置
msg.next = p;
prev.next = msg;
}
//调用nativeWake,以触发nativePollOnce函数结束等待,消息唤醒机制受到needWake这个变量影响
if (needWake) {
nativeWake(mPtr);//此处的mptr是Native中的MessageQueue的地址
}
}
return true;
}
作用:属于消息队列类(MessageQueue)的方法,入队即将消息根据时间放入到消息队列中(Message ->> MessageQueue) 采用单链表实现:提高插入消息、删除消息的效率,随着Looper对象的无限消息循环,不断从消息队列中取出Handler发送的消息并分发到对应Handler,最终回调Handler.handleMessage()处理消息
在将消息加入到消息队列中时,已经将消息按照等待时间进行了排序。排序分为两种情况
第一种:如果队列中没有消息,或者当前进入的消息比消息队列中头部的消息等待时间短,那么就放在消息队列的头部
第二种:如果大于头部的时间,就会循环遍历消息队列,把当前进入的消息放入合适的位置,依然是通过比较等待时间的大小
第一个:如果当前消息按照等待时间排序是在消息队列的头部, needWake = mBlocked,且mBlocked会在当前消息队列中没有消息可以处理,且没有空闲任务的条件下为true(mBlocked变量的赋值会在下文讲解)。
第二个:如果当前mBlocked=true(第一个条件判断),且消息队列头部消息是屏障消息,同时当前插入的消息为异步消息的条件。needWake = true。
(2)消息出队
Message next() {
//如果消息循环已经退出就直接返回null
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // 只在第一次迭代期间为-1
// 该参数用于确定消息队列中是否还有消息,从而决定消息队列应处于出队消息状态 or 等待状态
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,等待nextPollTimeoutMillis时长,或者被唤醒都会返回
// nativePollOnce方法在native层,nextPollTimeoutMillis参数为超时等待时间
//若是nextPollTimeoutMillis为-1,此时消息队列处于无限等待,直到有事件发生为止,如果值为0,则无需等待立即返回。该方法可能会阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//获取系统开机到现在的时间,如果使用System.currentMillis()会有误差
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;//头部消息
//判断是否是栅栏,同时获取消息队列最近的异步消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 出队消息,即 从消息队列中取出消息:判断创建Message对象的时间与等待时间,如果还需要等待则等待相应时间后唤醒
if (msg != null) {
if (now < msg.when) {//判断当前消息时间,是不是比当前时间大,计算时间差重新设置阻塞的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 如果消息队列不为空,不需要等待时间或者等待时间已经到了,那么直接返回取出了消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1,下次循环时,消息队列则处于等待状态
nextPollTimeoutMillis = -1;
}
//判断是否已经退出了
if (mQuitting) {
dispose();
return null;
}
//如果第一次空闲,获取空闲时处理任务的handler的数量,空闲handler仅在队列为空或队列中的第一个消息(可能是一个障碍)将在将来处理时运行。
//用于发现线程何时阻塞等待更多消息的回调接口。
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();//mIdleHandlers是一个IdleHandler类型的数组
}
//如果空闲时处理任务的handler个数为0,继续让线程阻塞
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
//判断当前空闲时处理任务的handler是否是为空
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//只有第一次迭代的时候,才会执行下面代码
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(TAG, "IdleHandler threw exception", t);
}
//如果不保存空闲任务,执行完成后直接删除
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 重置空闲的handler个数,因为不需要重复执行
pendingIdleHandlerCount = 0;
//当执行完空闲的handler的时候,新的native消息可能会进入,所以唤醒Native消息机制层
nextPollTimeoutMillis = 0;
}
}
作用:该方法属于消息队列类中的出队消息方法,即从消息队列中移出该消息,messageQueue头部是一个叫mMessages的待处理消息,这个mMessages会被next方法取出,之后messageQueue检测到mMessages是空的,就会按时间排序把别的message补上来作为mMessages,否则就进入阻塞状态。
首先进入先判断是否已经退出,退出直接返回,不退出进行下一步,之后再判断当前的MessageQueue是否为空,为空则赋值阻塞时间 nextPollTimeoutMillis = -1,如果不为空,则判断当前时间是否大于等于消息的触发时间,如果小于触发时间,则赋值阻塞时间 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE)。如果当前时间大于等于触发时间,则直接取出消息返回,并且把此消息移除队列
其中涉及一个方法 nativePollOnce(ptr, nextPollTimeoutMillis);这是一个native方法,主要作用是阻塞,nextPollTimeoutMillis代表阻塞时间
其中nextPollTimeoutMillis=-1表示,一直阻塞,直到被唤醒
其中nextPollTimeoutMillis=0表示,不阻塞,立即返回
其中nextPollTimeoutMillis>0表示,阻塞nextPollTimeoutMillis毫秒,如果期间唤醒也会立即返回
这里涉及到了IdleHandler,它的作用是在当前线程消息队列空闲时,去做一些我们想要做的操作,但是IdleHandler只会执行一次。
三、循环器Looper类的主要方法:
prepareMainLooper():创建一个属于当前主线程的循环器对象与消息队列对象
prepare():创建一个属于当前子线程的循环器对象与消息队列对象
loop():消息循环,即循环从消息队列中获取Message并分发给相应的Handler,无限循环无消息时则阻塞
myLooper():获取当前线程的Looper对象
mQueue:获取Looper实例里的消息队列队列对象
quit():最终调用的是MessageQueue的quit方法,mQueue.quit(false);,会直接退出
quitSafely():最终调用的是MessageQueue的quit方法。mQueue.quit(true);,会执行完剩余的消息退出
(1)Looper的创建
public static final void prepare() {
// 判断sThreadLocal是否为null,否则抛出异常, sThreadLocal代表一个ThreadLocal对象,用于存储线程的变量
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 若为初次Looper.prepare(),因为Looper对象是存放在Thread线程里的,所以创建Looper对象并存放在ThreadLocal变量中
sThreadLocal.set(new Looper(true));
}
(2)Looper的构造方法
private Looper(boolean quitAllowed) {
// 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue),所以在构造函数内创建1个消息队列实例
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
作用:prepare方法为当前子线程 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue),该方法需在子线程中手动调用,同时 Looper.prepare()方法不能被调用两次 ,因为 1个线程中只能对应1个Looper实例
(3)主线程Looper的创建
public static void main(String[] args) {
...
// 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue),逻辑类似 Looper.prepare()
Looper.prepareMainLooper();
// 创建主线程
ActivityThread thread = new ActivityThread();
//建立Binder通道 (创建新线程),通过创建新线程的方式,在死循环中处理其他事物,死循环同时可以保证主线程能一直运行
thread.attach(false);
// 自动开启 消息循环
Looper.loop();
}
作用:在Android应用进程启动时,会默认创建1个主线程ActivityThread,它的入口方法为main(),创建时会自动调用ActivityThread的静态main()方法作为应用程序的入口,,在该方法中会通过prepareMainLooper()创建1个循环器对象(Looper)和1个消息队列对象(MessageQueue),该方法在主线程(UI线程)创建时自动调用,即主线程的Looper对象自动生成,不需手动生成,并开启主线程的消息循环。
(4)消息循环
public static void loop() {
...
// 获取本线程的Looper,myLooper()作用是返回sThreadLocal存储的Looper实例,即loop()执行前必须执行prepare()创建1个Looper实例
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 获取Looper实例中的消息队列对象(MessageQueue)
final MessageQueue queue = me.mQueue;
// 消息循环,通过for循环
for (;;) {
// 通过next()取出消息队列里的消息,若取出的消息为空即没有消息就会阻塞,则线程阻塞
Message msg = queue.next();
if (msg == null) {
//一般情况msg不会为null,只有messageQueue退出,msg才会返回null
return;
}
// 派发消息到对应的Handler,把消息Message派发给消息对象msg的target属性,target属性实际代表一个handler对象
msg.target.dispatchMessage(msg);
// 释放消息占据的资源
msg.recycle();
}
}
作用:消息循环,loop()方法进入无限循环不断重复以下操作,从MessageQueue中取出Message即从消息队列中获取消息、把Message分发给对应的Handler,把分发后的Message回收到消息池,以便重新利用。主线程的消息循环不允许退出无限循环,子线程的消息循环允许退出通过调用消息队列MessageQueue的quit()
四、消息Message类的主要方法:
obtain():实例化消息对象
recycle():回收消息
(1)创建消息对象
public static Message obtain() {
// Message内部维护了1个Message池,用于Message消息对象的复用,使用obtain()则是直接从池内获取
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
// 如果池内无消息对象可复用,结果还是使用用关键字new创建
return new Message();
}
作用:创建Message对象可用关键字new 或 Message.obtain(),使用obtain()获得消息对象,避免每次都使用new重新分配内存
(2)回收消息
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "+ "is still in use.");
}
return;
}
recycleUnchecked()
}
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++;
}
}
}
作用:Message维护了一个消息池,recycle()方法可以把用过的消息加入到消息池中,这样做的好处是,当消息池不为空时,可以直接从中取出Message使用,而不是重新创建,提高效率。静态变量sPool的数据类型是Message,其实是一个链表,维护这个消息池,MAX_POOL_SIZE代表容量,默认50。其实就是一个链表的插入,把信息清除,然后插入
五、线程数据ThreadLocal的主要方法:
set(T value):通过当前线程获取一个ThreadLocalMap
createMap(Thread t, T firstValue):创建ThreadLocalMap
(1)获取一个ThreadLocalMap
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
作用:set方法就是通过当前线程获取一个ThreadLocalMap ,然后通过ThreadLocalMap 去储存数据,如果ThreadLocalMap 是null那么就通过当前thread去创建一个ThreadLocalMap ,再去存储。
(2)创建ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
每一个Thread内部都有一个ThreadLocalMap对象,如果这个对象为null,就为它重新赋值。
ThreadLocalMap类的set():首先用key计算出数组下标,然后从Entry[]中取出值,如果有数据则重新赋值,如果没有数据,则创建一个新的Entry添加到Entry[]数组中。
ThreadLocalMap类的get():首先获取此线程的ThreadLocalMap,如果不为Null,就用key计算出Entry[]数组下标,然后取出Entry,然后再取出具体的值,如果ThreadLocalMap为Null或者取出的Entry为Null,就重新赋值
每一个线程中都会有一个 ThreadLocal.ThreadLocalMap threadLocals = null;成员变量,操作ThreadLocal的set个get方法时,都是操作的单个线程中ThreadLocalMap对象,而ThreadLocalMap中是以Entry[]数组来储存数据,所以就实现了每个线程都会有不同的值。
如果想要了解关于Looper.loop()源码中的死循环为什么不会影响到线程正常运行,请看Android:Handler消息机制(四)——为什么主线程不会因Looper.loop()里的死循环卡死