热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

javahandlerbase_Android消息机制1Handler(Java层)(转)

相关源码frameworkbasecorejavaandoridosHandler.javaframeworkbasecorejavaandoridosLooper.javafra

相关源码

framework/base/core/java/andorid/os/Handler.java

framework/base/core/java/andorid/os/Looper.java

framework/base/core/java/andorid/os/Message.java

framework/base/core/java/andorid/os/MessageQueue.java

libcore/luni/src/main/java/java/lang/ThreadLocal.java

一、概述

在整个Android的源码世界里,有两大利剑,其一是Binder IPC机制,,另一个便是消息机制(由Handler/Looper/MessageQueue等构成的)。关于Binder在Binder系列中详细讲解过,有兴趣看看。

Android有大量的消息驱动方式来进行交互,比如Android的四剑客Activity, Service, Broadcast, ContentProvider的启动过程的交互,都离不开消息机制,Android某种意义上也可以说成是一个以消息驱动的系统。消息机制涉及MessageQueue/Message/Looper/Handler这4个类。

1.1 模型

消息机制主要包含:

Message:消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息;

MessageQueue:消息队列的主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);

Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);

Looper:不断循环执行(Looper.loop),按分发机制将消息分发给目标处理者。

1.2 架构图

6b1613698c4ca958d003312abde18399.png

Looper有一个MessageQueue消息队列;

MessageQueue有一组待处理的Message;

Message中有一个用于处理消息的Handler;

Handler中有Looper和MessageQueue。

1.3 典型实例

先展示一个典型的关于Handler/Looper的线程

class LooperThread extends Thread {

public Handler mHandler;

public void run() {

Looper.prepare(); //【见 2.1】

mHandler = new Handler() { //【见 3.1】

public void handleMessage(Message msg) {

//TODO 定义消息处理逻辑. 【见 3.2】

}

};

Looper.loop(); //【见 2.2】

}

}

接下来,围绕着这个实例展开详细分析。

二、Looper

2.1 prepare()

对于无参的情况,默认调用prepare(true),表示的是这个Looper运行退出,而对于false的情况则表示当前Looper不运行退出。

private static void prepare(boolean quitAllowed) {

//每个线程只允许执行一次该方法,第二次执行时线程的TLS已有数据,则会抛出异常。

if (sThreadLocal.get() != null) {

throw new RuntimeException("Only one Looper may be created per thread");

}

//创建Looper对象,并保存到当前线程的TLS区域

sThreadLocal.set(new Looper(quitAllowed));

}

这里的sThreadLocal是ThreadLocal类型,下面,先说说ThreadLocal。

ThreadLocal: 线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。TLS常用的操作方法:

ThreadLocal.set(T value):将value存储到当前线程的TLS区域,源码如下:

public void set(T value) {

Thread currentThread = Thread.currentThread(); //获取当前线程

Values values = values(currentThread); //查找当前线程的本地储存区

if (values == null) {

//当线程本地存储区,尚未存储该线程相关信息时,则创建Values对象

values = initializeValues(currentThread);

}

//保存数据value到当前线程this

values.put(this, value);

}

ThreadLocal.get():获取当前线程TLS区域的数据,源码如下:

public T get() {

Thread currentThread = Thread.currentThread(); //获取当前线程

Values values = values(currentThread); //查找当前线程的本地储存区

if (values != null) {

Object[] table = values.table;

int index = hash & values.mask;

if (this.reference == table[index]) {

return (T) table[index + 1]; //返回当前线程储存区中的数据

}

} else {

//创建Values对象

values = initializeValues(currentThread);

}

return (T) values.getAfterMiss(this); //从目标线程存储区没有查询是则返回null

}

ThreadLocal的get()和set()方法操作的类型都是泛型,接着回到前面提到的sThreadLocal变量,其定义如下:

static final ThreadLocal sThreadLocal = new ThreadLocal()

可见sThreadLocal的get()和set()操作的类型都是Looper类型。

Looper.prepare()

Looper.prepare()在每个线程只允许执行一次,该方法会创建Looper对象,Looper的构造方法中会创建一个MessageQueue对象,再将Looper对象保存到当前线程TLS。

对于Looper类型的构造方法如下:

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed); //创建MessageQueue对象. 【见4.1】

mThread = Thread.currentThread(); //记录当前线程.

}

另外,与prepare()相近功能的,还有一个prepareMainLooper()方法,该方法主要在ActivityThread类中使用。

public static void prepareMainLooper() {

prepare(false); //设置不允许退出的Looper

synchronized (Looper.class) {

//将当前的Looper保存为主Looper,每个线程只允许执行一次。

if (sMainLooper != null) {

throw new IllegalStateException("The main Looper has already been prepared.");

}

sMainLooper = myLooper();

}

}

2.2 loop()

public static void loop() {

final Looper me = myLooper(); //获取TLS存储的Looper对象 【见2.4】

if (me == null) {

throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

}

final MessageQueue queue = me.mQueue; //获取Looper对象中的消息队列

Binder.clearCallingIdentity();

//确保在权限检查时基于本地进程,而不是基于最初调用进程。

final long ident = Binder.clearCallingIdentity();

for (;;) { //进入loop的主循环方法

Message msg = queue.next(); //可能会阻塞 【见4.2】

if (msg == null) { //没有消息,则退出循环

return;

}

Printer logging = me.mLogging; //默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能

if (logging != null) {

logging.println(">>>>> Dispatching to " + msg.target + " " +

msg.callback + ": " + msg.what);

}

msg.target.dispatchMessage(msg); //用于分发Message 【见3.2】

if (logging != null) {

logging.println("<<<<

}

final long newIdent &#61; Binder.clearCallingIdentity(); //确保分发过程中identity不会损坏

if (ident !&#61; newIdent) {

//打印identity改变的log&#xff0c;在分发消息过程中是不希望身份被改变的。

}

msg.recycleUnchecked(); //将Message放入消息池 【见5.2】

}

}

loop()进入循环模式&#xff0c;不断重复下面的操作&#xff0c;直到没有消息时退出循环

读取MessageQueue的下一条Message&#xff1b;

把Message分发给相应的target&#xff1b;

再把分发后的Message回收到消息池&#xff0c;以便重复利用。

这是这个消息处理的核心部分。另外&#xff0c;上面代码中可以看到有logging方法&#xff0c;这是用于debug的&#xff0c;默认情况下logging &#61;&#61; null&#xff0c;通过设置setMessageLogging()用来开启debug工作。

2.3 quit()

public void quit() {

mQueue.quit(false); //消息移除

}

public void quitSafely() {

mQueue.quit(true); //安全地消息移除

}

Looper.quit()方法的实现最终调用的是MessageQueue.quit()方法

MessageQueue.quit()

void quit(boolean safe) {

// 当mQuitAllowed为false&#xff0c;表示不运行退出&#xff0c;强行调用quit()会抛出异常

if (!mQuitAllowed) {

throw new IllegalStateException("Main thread not allowed to quit.");

}

synchronized (this) {

if (mQuitting) { //防止多次执行退出操作

return;

}

mQuitting &#61; true;

if (safe) {

removeAllFutureMessagesLocked(); //移除尚未触发的所有消息

} else {

removeAllMessagesLocked(); //移除所有的消息

}

//mQuitting&#61;false&#xff0c;那么认定为 mPtr !&#61; 0

nativeWake(mPtr);

}

}

消息退出的方式&#xff1a;

当safe &#61;true时&#xff0c;只移除尚未触发的所有消息&#xff0c;对于正在触发的消息并不移除&#xff1b;

当safe &#61;flase时&#xff0c;移除所有的消息

2.4 常用方法

2.4.1 myLooper

用于获取TLS存储的Looper对象

public static &#64;Nullable Looper myLooper() {

return sThreadLocal.get();

}

2.4.2 post

发送消息&#xff0c;并设置消息的callback&#xff0c;用于处理消息。

public final boolean post(Runnable r)

{

return sendMessageDelayed(getPostMessage(r), 0);

}

private static Message getPostMessage(Runnable r) {

Message m &#61; Message.obtain();

m.callback &#61; r;

return m;

}

三、Handler

3.1 创建Handler

3.1.1 无参构造

public Handler() {

this(null, false);

}

public Handler(Callback callback, boolean async) {

//匿名类、内部类或本地类都必须申明为static&#xff0c;否则会警告可能出现内存泄露

if (FIND_POTENTIAL_LEAKS) {

final Class extends Handler> klass &#61; getClass();

if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&

(klass.getModifiers() & Modifier.STATIC) &#61;&#61; 0) {

Log.w(TAG, "The following Handler class should be static or leaks might occur: " &#43;

klass.getCanonicalName());

}

}

//必须先执行Looper.prepare()&#xff0c;才能获取Looper对象&#xff0c;否则为null.

mLooper &#61; Looper.myLooper(); //从当前线程的TLS中获取Looper对象【见2.1】

if (mLooper &#61;&#61; null) {

throw new RuntimeException("");

}

mQueue &#61; mLooper.mQueue; //消息队列&#xff0c;来自Looper对象

mCallback &#61; callback; //回调方法

mAsynchronous &#61; async; //设置消息是否为异步处理方式

}

对于Handler的无参构造方法&#xff0c;默认采用当前线程TLS中的Looper对象&#xff0c;并且callback回调方法为null&#xff0c;且消息为同步处理方式。只要执行的Looper.prepare()方法&#xff0c;那么便可以获取有效的Looper对象。

3.1.2 有参构造

public Handler(Looper looper) {

this(looper, null, false);

}

public Handler(Looper looper, Callback callback, boolean async) {

mLooper &#61; looper;

mQueue &#61; looper.mQueue;

mCallback &#61; callback;

mAsynchronous &#61; async;

}

Handler类在构造方法中&#xff0c;可指定Looper&#xff0c;Callback回调方法以及消息的处理方式(同步或异步)&#xff0c;对于无参的handler&#xff0c;默认是当前线程的Looper。

3.2 消息分发机制

在Looper.loop()中&#xff0c;当发现有消息时&#xff0c;调用消息的目标handler&#xff0c;执行dispatchMessage()方法来分发消息。

public void dispatchMessage(Message msg) {

if (msg.callback !&#61; null) {

//当Message存在回调方法&#xff0c;回调msg.callback.run()方法&#xff1b;

handleCallback(msg);

} else {

if (mCallback !&#61; null) {

//当Handler存在Callback成员变量时&#xff0c;回调方法handleMessage()&#xff1b;

if (mCallback.handleMessage(msg)) {

return;

}

}

//Handler自身的回调方法handleMessage()

handleMessage(msg);

}

}

分发消息流程&#xff1a;

当Message的回调方法不为空时&#xff0c;则回调方法msg.callback.run()&#xff0c;其中callBack数据类型为Runnable,否则进入步骤2&#xff1b;

当Handler的mCallback成员变量不为空时&#xff0c;则回调方法mCallback.handleMessage(msg),否则进入步骤3&#xff1b;

调用Handler自身的回调方法handleMessage()&#xff0c;该方法默认为空&#xff0c;Handler子类通过覆写该方法来完成具体的逻辑。

对于很多情况下&#xff0c;消息分发后的处理方法是第3种情况&#xff0c;即Handler.handleMessage()&#xff0c;一般地往往通过覆写该方法从而实现自己的业务逻辑。

3.3 消息发送

发送消息调用链&#xff1a;

c3e37f7a3aefe91b63614eb82f5f8b2c.png

从上图&#xff0c;可以发现所有的发消息方式&#xff0c;最终都是调用MessageQueue.enqueueMessage();

3.3.1 sendEmptyMessage

public final boolean sendEmptyMessage(int what)

{

return sendEmptyMessageDelayed(what, 0);

}

3.3.2 sendEmptyMessageDelayed

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {

Message msg &#61; Message.obtain();

msg.what &#61; what;

return sendMessageDelayed(msg, delayMillis);

}

3.3.3 sendMessageDelayed

public final boolean sendMessageDelayed(Message msg, long delayMillis)

{

if (delayMillis <0) {

delayMillis &#61; 0;

}

return sendMessageAtTime(msg, SystemClock.uptimeMillis() &#43; delayMillis);

}

3.3.4 sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

MessageQueue queue &#61; mQueue;

if (queue &#61;&#61; null) {

return false;

}

return enqueueMessage(queue, msg, uptimeMillis);

}

3.3.5 sendMessageAtFrontOfQueue

public final boolean sendMessageAtFrontOfQueue(Message msg) {

MessageQueue queue &#61; mQueue;

if (queue &#61;&#61; null) {

return false;

}

return enqueueMessage(queue, msg, 0);

}

该方法通过设置消息的触发时间为0&#xff0c;从而使Message加入到消息队列的队头。

3.3.6 post

public final boolean post(Runnable r)

{

return sendMessageDelayed(getPostMessage(r), 0);

}

private static Message getPostMessage(Runnable r) {

Message m &#61; Message.obtain();

m.callback &#61; r;

return m;

}

3.3.7 postAtFrontOfQueue

public final boolean postAtFrontOfQueue(Runnable r)

{

return sendMessageAtFrontOfQueue(getPostMessage(r));

}

3.3.8 enqueueMessage

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target &#61; this;

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis); 【见4.3】

}

3.3.8 小节

Handler.sendEmptyMessage()等系列方法最终调用MessageQueue.enqueueMessage(msg, uptimeMillis)&#xff0c;将消息添加到消息队列中&#xff0c;其中uptimeMillis为系统当前的运行时间&#xff0c;不包括休眠时间。

3.4 其他方法

3.4.1 obtainMessage

获取消息

public final Message obtainMessage()

{

return Message.obtain(this); 【见5.2】

}

Handler.obtainMessage()方法&#xff0c;最终调用Message.obtainMessage(this)&#xff0c;其中this为当前的Handler对象。

3.4.2 removeMessages

public final void removeMessages(int what) {

mQueue.removeMessages(this, what, null); 【见 4.5】

}

Handler是消息机制中非常重要的辅助类&#xff0c;更多的实现都是MessageQueue, Message中的方法&#xff0c;Handler的目的是为了更加方便的使用消息机制。

四、MessageQueue

MessageQueue是消息机制的Java层和C&#43;&#43;层的连接纽带&#xff0c;大部分核心方法都交给native层来处理&#xff0c;其中MessageQueue类中涉及的native方法如下&#xff1a;

private native static long nativeInit();

private native static void nativeDestroy(long ptr);

private native void nativePollOnce(long ptr, int timeoutMillis);

private native static void nativeWake(long ptr);

private native static boolean nativeIsPolling(long ptr);

private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

4.1 创建MessageQueue

MessageQueue(boolean quitAllowed) {

mQuitAllowed &#61; quitAllowed;

//通过native方法初始化消息队列&#xff0c;其中mPtr是供native代码使用

mPtr &#61; nativeInit();

}

4.2 next()

提取下一条message

Message next() {

final long ptr &#61; mPtr;

if (ptr &#61;&#61; 0) { //当消息循环已经退出&#xff0c;则直接返回

return null;

}

int pendingIdleHandlerCount &#61; -1; // 循环迭代的首次为-1

int nextPollTimeoutMillis &#61; 0;

for (;;) {

if (nextPollTimeoutMillis !&#61; 0) {

Binder.flushPendingCommands();

}

//阻塞操作&#xff0c;当等待nextPollTimeoutMillis时长&#xff0c;或者消息队列被唤醒&#xff0c;都会返回

nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {

final long now &#61; SystemClock.uptimeMillis();

Message prevMsg &#61; null;

Message msg &#61; mMessages;

if (msg !&#61; null && msg.target &#61;&#61; null) {

//当消息Handler为空时&#xff0c;查询MessageQueue中的下一条异步消息msg&#xff0c;则退出循环。

do {

prevMsg &#61; msg;

msg &#61; msg.next;

} while (msg !&#61; null && !msg.isAsynchronous());

}

if (msg !&#61; null) {

if (now

//当异步消息触发时间大于当前时间&#xff0c;则设置下一次轮询的超时时长

nextPollTimeoutMillis &#61; (int) Math.min(msg.when - now, Integer.MAX_VALUE);

} else {

// 获取一条消息&#xff0c;并返回

mBlocked &#61; false;

if (prevMsg !&#61; null) {

prevMsg.next &#61; msg.next;

} else {

mMessages &#61; msg.next;

}

msg.next &#61; null;

//设置消息的使用状态&#xff0c;即flags |&#61; FLAG_IN_USE

msg.markInUse();

return msg; //成功地获取MessageQueue中的下一条即将要执行的消息

}

} else {

//没有消息

nextPollTimeoutMillis &#61; -1;

}

//消息正在退出&#xff0c;返回null

if (mQuitting) {

dispose();

return null;

}

//当消息队列为空&#xff0c;或者是消息队列的第一个消息时

if (pendingIdleHandlerCount <0 && (mMessages &#61;&#61; null || now

pendingIdleHandlerCount &#61; mIdleHandlers.size();

}

if (pendingIdleHandlerCount <&#61; 0) {

//没有idle handlers 需要运行&#xff0c;则循环并等待。

mBlocked &#61; true;

continue;

}

if (mPendingIdleHandlers &#61;&#61; null) {

mPendingIdleHandlers &#61; new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];

}

mPendingIdleHandlers &#61; mIdleHandlers.toArray(mPendingIdleHandlers);

}

//只有第一次循环时&#xff0c;会运行idle handlers&#xff0c;执行完成后&#xff0c;重置pendingIdleHandlerCount为0.

for (int i &#61; 0; i

final IdleHandler idler &#61; mPendingIdleHandlers[i];

mPendingIdleHandlers[i] &#61; null; //去掉handler的引用

boolean keep &#61; false;

try {

keep &#61; idler.queueIdle(); //idle时执行的方法

} catch (Throwable t) {

Log.wtf(TAG, "IdleHandler threw exception", t);

}

if (!keep) {

synchronized (this) {

mIdleHandlers.remove(idler);

}

}

}

//重置idle handler个数为0&#xff0c;以保证不会再次重复运行

pendingIdleHandlerCount &#61; 0;

//当调用一个空闲handler时&#xff0c;一个新message能够被分发&#xff0c;因此无需等待可以直接查询pending message.

nextPollTimeoutMillis &#61; 0;

}

}

nativePollOnce是阻塞操作&#xff0c;其中nextPollTimeoutMillis代表下一个消息到来前&#xff0c;还需要等待的时长&#xff1b;当nextPollTimeoutMillis &#61; -1时&#xff0c;表示消息队列中无消息&#xff0c;会一直等待下去。

当处于空闲时&#xff0c;往往会执行IdleHandler中的方法。当nativePollOnce()返回后&#xff0c;next()从mMessages中提取一个消息。

nativePollOnce()在native做了大量的工作&#xff0c;想进一步了解可查看 Android消息机制2-Handler(native篇)。

4.3 enqueueMessage

添加一条消息到消息队列

boolean enqueueMessage(Message msg, long when) {

// 每一个Message必须有一个target

if (msg.target &#61;&#61; null) {

throw new IllegalArgumentException("Message must have a target.");

}

if (msg.isInUse()) {

throw new IllegalStateException(msg &#43; " This message is already in use.");

}

synchronized (this) {

if (mQuitting) { //正在退出时&#xff0c;回收msg&#xff0c;加入到消息池

msg.recycle();

return false;

}

msg.markInUse();

msg.when &#61; when;

Message p &#61; mMessages;

boolean needWake;

if (p &#61;&#61; null || when &#61;&#61; 0 || when

//p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的&#xff0c; 则进入该该分支

msg.next &#61; p;

mMessages &#61; msg;

needWake &#61; mBlocked; //当阻塞时需要唤醒

} else {

//将消息按时间顺序插入到MessageQueue。一般地&#xff0c;不需要唤醒事件队列&#xff0c;除非

//消息队头存在barrier&#xff0c;并且同时Message是队列中最早的异步消息。

needWake &#61; mBlocked && p.target &#61;&#61; null && msg.isAsynchronous();

Message prev;

for (;;) {

prev &#61; p;

p &#61; p.next;

if (p &#61;&#61; null || when

break;

}

if (needWake && p.isAsynchronous()) {

needWake &#61; false;

}

}

msg.next &#61; p;

prev.next &#61; msg;

}

//消息没有退出&#xff0c;我们认为此时mPtr !&#61; 0

if (needWake) {

nativeWake(mPtr);

}

}

return true;

}

MessageQueue是按照Message触发时间的先后顺序排列的&#xff0c;队头的消息是将要最早触发的消息。当有消息需要加入消息队列时&#xff0c;会从队列头开始遍历&#xff0c;直到找到消息应该插入的合适位置&#xff0c;以保证所有消息的时间顺序。

4.4 removeMessages

void removeMessages(Handler h, int what, Object object) {

if (h &#61;&#61; null) {

return;

}

synchronized (this) {

Message p &#61; mMessages;

//从消息队列的头部开始&#xff0c;移除所有符合条件的消息

while (p !&#61; null && p.target &#61;&#61; h && p.what &#61;&#61; what

&& (object &#61;&#61; null || p.obj &#61;&#61; object)) {

Message n &#61; p.next;

mMessages &#61; n;

p.recycleUnchecked();

p &#61; n;

}

//移除剩余的符合要求的消息

while (p !&#61; null) {

Message n &#61; p.next;

if (n !&#61; null) {

if (n.target &#61;&#61; h && n.what &#61;&#61; what

&& (object &#61;&#61; null || n.obj &#61;&#61; object)) {

Message nn &#61; n.next;

n.recycleUnchecked();

p.next &#61; nn;

continue;

}

}

p &#61; n;

}

}

}

这个移除消息的方法&#xff0c;采用了两个while循环&#xff0c;第一个循环是从队头开始&#xff0c;移除符合条件的消息&#xff0c;第二个循环是从头部移除完连续的满足条件的消息之后&#xff0c;再从队列后面继续查询是否有满足条件的消息需要被移除。

五、 Message

5.1 创建消息

每个消息用Message表示&#xff0c;Message主要包含以下内容&#xff1a;

数据类型成员变量解释

int

what

消息类别

long

when

消息触发时间

int

arg1

参数1

int

arg2

参数2

Object

obj

消息内容

Handler

target

消息响应方

Runnable

callback

回调方法

创建消息的过程&#xff0c;就是填充消息的上述内容的一项或多项。

5.2 消息池

在代码中&#xff0c;可能经常看到recycle()方法&#xff0c;咋一看&#xff0c;可能是在做虚拟机的gc()相关的工作&#xff0c;其实不然&#xff0c;这是用于把消息加入到消息池的作用。这样的好处是&#xff0c;当消息池不为空时&#xff0c;可以直接从消息池中获取Message对象&#xff0c;而不是直接创建&#xff0c;提高效率。

静态变量sPool的数据类型为Message&#xff0c;通过next成员变量&#xff0c;维护一个消息池&#xff1b;静态变量MAX_POOL_SIZE代表消息池的可用大小&#xff1b;消息池的默认大小为50。

消息池常用的操作方法是obtain()和recycle()。

5.2.1 obtain

从消息池中获取消息

public static Message obtain() {

synchronized (sPoolSync) {

if (sPool !&#61; null) {

Message m &#61; sPool;

sPool &#61; m.next;

m.next &#61; null; //从sPool中取出一个Message对象&#xff0c;并消息链表断开

m.flags &#61; 0; // 清除in-use flag

sPoolSize--; //消息池的可用大小进行减1操作

return m;

}

}

return new Message(); // 当消息池为空时&#xff0c;直接创建Message对象

}

obtain()&#xff0c;从消息池取Message&#xff0c;都是把消息池表头的Message取走&#xff0c;再把表头指向next;

5.2.2 recycle

把不再使用的消息加入消息池

public void recycle() {

if (isInUse()) { //判断消息是否正在使用

if (gCheckRecycle) { //Android 5.0以后的版本默认为true,之前的版本默认为false.

throw new IllegalStateException("This message cannot be recycled because it is still in use.");

}

return;

}

recycleUnchecked();

}

//对于不再使用的消息&#xff0c;加入到消息池

void recycleUnchecked() {

//将消息标示位置为IN_USE&#xff0c;并清空消息所有的参数。

flags &#61; FLAG_IN_USE;

what &#61; 0;

arg1 &#61; 0;

arg2 &#61; 0;

obj &#61; null;

replyTo &#61; null;

sendingUid &#61; -1;

when &#61; 0;

target &#61; null;

callback &#61; null;

data &#61; null;

synchronized (sPoolSync) {

if (sPoolSize

next &#61; sPool;

sPool &#61; this;

sPoolSize&#43;&#43;; //消息池的可用大小进行加1操作

}

}

}

recycle()&#xff0c;将Message加入到消息池的过程&#xff0c;都是把Message加到链表的表头&#xff1b;

六、总结

最后用一张图&#xff0c;来表示整个消息机制

69c7792987525849246b7164843d22f3.png

图解&#xff1a;

Handler通过sendMessage()发送Message到MessageQueue队列&#xff1b;

Looper通过loop()&#xff0c;不断提取出达到触发条件的Message&#xff0c;并将Message交给target来处理&#xff1b;

经过dispatchMessage()后&#xff0c;交回给Handler的handleMessage()来进行相应地处理。

将Message加入MessageQueue时&#xff0c;处往管道写入字符&#xff0c;可以会唤醒loop线程&#xff1b;如果MessageQueue中没有Message&#xff0c;并处于Idle状态&#xff0c;则会执行IdelHandler接口中的方法&#xff0c;往往用于做一些清理性地工作。

消息分发的优先级&#xff1a;

Message的回调方法&#xff1a;message.callback.run()&#xff0c;优先级最高&#xff1b;

Handler的回调方法&#xff1a;Handler.mCallback.handleMessage(msg)&#xff0c;优先级仅次于1&#xff1b;

Handler的默认方法&#xff1a;Handler.handleMessage(msg)&#xff0c;优先级最低。



推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 本文介绍了Java中Hashtable的clear()方法,该方法用于清除和移除指定Hashtable中的所有键。通过示例程序演示了clear()方法的使用。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
author-avatar
傻傻的笑没心没肺wy
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有