Android基础之Handler分析,vivo安卓开发面试

2.dispatchMessage

image-20210412083713262

Looper

关键方法

//1.初始化
Looper.prepare()
//2.开启循环
Looper.loop()
for (;😉 {
//获取消息
queue.next();

}

  1. 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线程隔离工具类:

image-20210412083733468

  1. 创建MessageQueue以及Looper与当前线程的绑定

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建了MessageQueue
mThread = Thread.currentThread(); //当前线程的绑定
}

  1. 开启循环

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的主要函数

  1. MessageQueue的构造方法

MessageQueue(boolean quitAllowed) {
//mQuitAllowed决定队列是否可以销毁 主线程的队列不可以被销毁需要传入false, 在MessageQueue的quit()方法
mQuitAllowed = quitAllowed;        
mPtr = nativeInit();  
}

  1. 向消息队列添加消息

MessageQueue.enqueueMessage();

  1. 从消息队列中获取消息,使用for循环

MessageQueue.next();

image-20210412083928599

入队: 根据时间排序,当队列满的时候队列(一个优先级队列)进入阻塞状态,直到用户通过next取出消息。当next方法被调用,通知MessagQueue可以进行消息的入队。

出队:Looper.loop()启动轮询器对queue进行轮询。当消息达到执行时间就取出来;当messageQueue为空的时候队列阻塞,等消息队列调用enqueue Message的时候通知队列可以取出消息,停止阻塞。

消息入队时的插入方法

采用插入排序的算法对优先级队列进行排序

image-20210412083857551

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,继续调用messageQueueenqueueMessage方法,将消息保存在了消息队列中,而最终由Looper取出,交给Handler的dispatchMessage进行处理。

我们可以看到在dispatchMessage方法中:

  1. messagecallback是一个Runnable对象,如果callback不为空,则直接调用callbackrun方法;
  2. 否则判断mCallback是否为空,mCallback在Handler构造方法中初始化;
  3. 在主线程通直接通过无参的构造方法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

image-20210412084557971

image-20210412084604872

疑惑点2: Looper什么时候退出

在子线程创建Looper经常会有内存泄漏,因为,Looper没有释放

image-20210412084208416

4.如何保证线程安全?

Handler是用于线程间通信的,但是它产生的根本并不只是用于UI处理,而更多的是handler是整个app通信的框架, 大家可以在ActivityThread里面感受到,整个App都是用它来进行线程间的协调。Handler既然这么重要,那么它的线程安全就至关重要了,那么它是如何保证自己的线程安全呢?

Handler机制里面最主要的类MessageQueue,这个类就是所有消息的存储仓库,在这个仓库中,我们如何的管理好 消息,这个就是一个关键点了。消息管理就2点:

  1. 消息入库(enqueueMessage)
  2. 消息出库(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移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。需要的朋友都可以点击GitHub直接获取方式

image

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高级架构师之路很漫长,一起共勉吧!

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值