2.dispatchMessage
Looper
关键方法
//1.初始化
Looper.prepare()
//2.开启循环
Looper.loop()
for (;😉 {
//获取消息
queue.next();
}
- Looper的初始化方法
prepare有两个重载的方法,主要看 prepare(boolean quitAllowed)
,quitAllowed
的作用是在创建MessageQueue时标识消息队列是否可以销毁。
//1.方式一:对当前线程的初始化,子线程要开启Looper必须调用该方法
public static void prepare() {
prepare(true); //消息队列可以quit
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //不为空表示当前线程已经创建了Looper
throw new RuntimeException(“Only one Looper may be created per thread”); //每个线程只能创建一个Looper
}
sThreadLocal.set(new Looper(quitAllowed)); //创建Looper并设置给sThreadLocal,这样get的 时候就不会为null了
}
//2.方式二:对主线程的初始化,在ActivityThread中被调用
@Deprecated
public static void prepareMainLooper() {
prepare(false); //消息队列不可以quit,主线程不可被销毁
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException(“The main Looper has already been prepared.”);
}
sMainLooper = myLooper();
}
}
Looper对象是一个TheadLocal ,即每一个线程只有一个Looper对象,保证Looper的唯一性。ThreadLocal线程隔离工具类:
- 创建MessageQueue以及Looper与当前线程的绑定
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建了MessageQueue
mThread = Thread.currentThread(); //当前线程的绑定
}
- 开启循环
public static void loop() {
final Looper me = myLooper(); //里面调用了sThreadLocal.get()获得刚才创建的Looper对象
if (me == null) { //如果Looper为空则会抛出异常
throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);
}
if (me.mInLoop) {
Slog.w(TAG, “Loop again would have the queued messages be executed”
- " before this one completed.");
}
me.mInLoop = true;
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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell ‘setprop log.looper.1000.main.slow 1 && stop && start’
final int thresholdOverride =
SystemProperties.getInt(“log.looper.”
- Process.myUid() + “.”
- Thread.currentThread().getName()
- “.slow”, 0);
boolean slowDeliveryDetected = false;
//这是一个死循环,从消息队列不断的取消息
for (;😉 {
Message msg = queue.next(); // might block
if (msg == null) {
//由于刚创建MessageQueue就开始轮询,队列里是没有消息的,等到Handler sendMessage enqueueMessage后
//队列里才有消息
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// Make sure the observer won’t change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
///msg.target就是绑定的Handler,详见后面Message的部分,Handler开始
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, “Drained”);
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, “delivery”,
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, “dispatch”, 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();
}
}
MessageQueue
MessageQueue的主要函数
- MessageQueue的构造方法
MessageQueue(boolean quitAllowed) {
//mQuitAllowed决定队列是否可以销毁 主线程的队列不可以被销毁需要传入false, 在MessageQueue的quit()方法
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
- 向消息队列添加消息
MessageQueue.enqueueMessage();
- 从消息队列中获取消息,使用for循环
MessageQueue.next();
入队: 根据时间排序,当队列满的时候队列(一个优先级队列)进入阻塞状态,直到用户通过next取出消息。当next方法被调用,通知MessagQueue可以进行消息的入队。
出队: 由Looper.loop()
启动轮询器对queue
进行轮询。当消息达到执行时间就取出来;当messageQueue
为空的时候队列阻塞,等消息队列调用enqueue Message
的时候通知队列可以取出消息,停止阻塞。
消息入队时的插入方法
采用插入排序的算法对优先级队列进行排序
Message
创建Message
可以直接new Message,但是有更好的方式 Message.obtain。因为可以检查是否有可以复用的Message,用过复用避免过多的创建、销毁Message对象达到优化内存和性能的目地。
public static Message obtain(Handler h) {
Message m = obtain();//调用重载的obtain方法
m.target = h;//并绑定的创建Message对象的handler
return m;
}
public static Message obtain() {
synchronized (sPoolSync) {//sPoolSync是一个Object对象,用来同步保证线程安全
if (sPool != null) {//sPool是就是handler dispatchMessage 后 通过recycleUnchecked 回 收用以复用的Message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize–;
return m;
}
}
return new Message();
}
Message和Handler的绑定
创建Message的时候可以通过 Message.obtain(Handler h)
这个构造方法绑定。当然可以在 在Handler 中的 enqueueMessage()
也绑定了,所有发送Message的方法都会调用此方法入队,所以在创建Message的时候是可以不绑定的。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //自动绑定
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler发送消息
Handler发送消息的重载方法很多,但是主要只有2种。 sendMessage(Message)
方法通过一系列重载方法的调用,sendMessage
调用sendMessageDelayed
,继续调用sendMessageAtTime
,继续调用 enqueueMessage
,继续调用messageQueue
的enqueueMessage
方法,将消息保存在了消息队列中,而最终由Looper取出,交给Handler的dispatchMessage
进行处理。
我们可以看到在dispatchMessage
方法中:
message
中callback
是一个Runnable对象,如果callback不为空,则直接调用callback
的run
方法;- 否则判断
mCallback
是否为空,mCallback
在Handler构造方法中初始化; - 在主线程通直接通过无参的构造方法new出来的为null,所以会直接执行后面的
handleMessage()
方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//1.callback在message的构造方法中初始化或者使用 handler.post(Runnable)时候才不为空
handleCallback(msg);
} else {
if (mCallback != null) {//2.mCallback是一个Callback对象,通过无参的构造方法创建出来的handler, 该属性为null,此段不执行
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//3.最终执行handleMessage方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
Handler处理消息 在handleMessage(Message)方法中,我们可以拿到message对象,根据不同的需求进行处理,整个Handler机制的 流程就结束了。
3.设计亮点
分享handler延迟的根本
亮点一: 享元模式
recycleUnchecked 在looper里面的调用。
如果是new message() ,会申请内存使用完之后再回收,整个过程会产生内存碎片。 如果频繁的生成对象就会产生内存抖动,也会引发OOM。Handler的message采用享元设计,目的是实现内存复用
难点一: nativePollOnce/NativeWake
疑惑点2: Looper什么时候退出
在子线程创建Looper经常会有内存泄漏,因为,Looper没有释放
4.如何保证线程安全?
Handler是用于线程间通信的,但是它产生的根本并不只是用于UI处理,而更多的是handler是整个app通信的框架, 大家可以在ActivityThread里面感受到,整个App都是用它来进行线程间的协调。Handler既然这么重要,那么它的线程安全就至关重要了,那么它是如何保证自己的线程安全呢?
Handler机制里面最主要的类MessageQueue,这个类就是所有消息的存储仓库,在这个仓库中,我们如何的管理好 消息,这个就是一个关键点了。消息管理就2点:
- 消息入库(enqueueMessage)
- 消息出库(next),所以这两个接口是确保线程安全的主要档口。
enqueueMessage 源码如下:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException(“Message must have a target.”);
}
//锁开始的地方
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don’t have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
//锁结束的地方
return true;
}
synchronized锁是一个内置锁,也就是由系统控制锁的lock unlock时机的。这个锁,说明的是对所有调用同一个MessageQueue对象的线程来说,他们都是互斥的,然而在我们的Handler里 面,一个线程是对应着一个唯一的Looper对象,而Looper中又只有一个唯一的MessageQueue(这个在上文中也有介绍)。所以,我们主线程就只有一个MessageQueue对象,也就是说,所有的子线程向主线程发送消息的时候, 主线程一次都只会处理一个消息,其他的都需要等待,那么这个时候消息队列就不会出现混乱。
另外再看next函数
@UnsupportedAppUsage
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;😉 {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
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) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。需要的朋友都可以点击GitHub直接获取方式
Android高级架构师之路很漫长,一起共勉吧!
[外链图片转存中…(img-s4xQgXAz-1710818983657)]
[外链图片转存中…(img-PwGdJkrI-1710818983657)]
[外链图片转存中…(img-6F9qDIG3-1710818983657)]
[外链图片转存中…(img-9D3Z531L-1710818983658)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-gnb1ZyVd-1710818983658)]
最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。需要的朋友都可以点击GitHub直接获取方式
[外链图片转存中…(img-zKtwyF6G-1710818983659)]
Android高级架构师之路很漫长,一起共勉吧!