扯掉“桥梁Handler”的底裤
Posted 初一十五啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了扯掉“桥梁Handler”的底裤相关的知识,希望对你有一定的参考价值。
android跨进程要掌握的是Binder
, 而同一进程中最重要的应该就是Handler
消息通信机制了。
参考内容:
腾讯Android开发笔记+2022Android十一位大厂面试真题+音视频60道面试题+Jetpack+Matrix+JVM
一丶什么是Handler?
Handler
主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的[函数]即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。
二丶Handler特点
传递Message
。用于接受子线程发送的数据, 并用此数据配合主线程更新UI。
在Android中,对于UI的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,那么就需要把数据消息作为一个Message
对象发送到消息队列中,然后,由Handler
中的handlerMessage
方法处理传过来的数据信息,并操作UI。当然,Handler
对象是在主线程中初始化的,因为它需要绑定在主线程的消息队列中。
类sendMessage(Message msg)
方法实现发送消息的操作。 在初始化Handler
对象时重写的handleMessage
方法来接收Message
并进行相关操作。
传递Runnable
对象。用于通过Handler
绑定的消息队列,安排不同操作的执行顺序。
Handler
对象在进行初始化的时候,会默认的自动绑定消息队列。利用类post
方法,可以将Runnable
对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable
对象中的run
方法。
三丶Handler怎么用?
public class HandlerActivity extends AppCompatActivity
private static final String TAG = "HandlerActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
testSendMessage();
public void testSendMessage()
Handler handler = new MyHandler(this);
Message message = Message.obtain();
message.obj = "test handler send message";
handler.sendMessage(message);
//注1: 为什么要用静态内部???
static class MyHandler extends Handler
WeakReference<AppCompatActivity> activityWeakReference; // 注2:为何要用弱引用???
public MyHandler(AppCompatActivity activity)
activityWeakReference = new WeakReference<>(activity);
@Override
public void handleMessage(@NonNull Message msg)
super.handleMessage(msg);
Log.d(TAG, (String) msg.obj);
四丶Handler源码怎么读?
从使用方式的场景,咱们一步一步的探究里面是怎么实现的,还有上面的标注的两点,在后面我都会介绍的,各位客官听我慢慢道来。首先,看下四大金刚关系图,文字表述再多,不如一张图来的直接。
通过上图就可以简单看出Handler、MessageQueue、Message、Looper
这四者是怎么样互相持有对方的,大概可以了解消息的传递。
下面我们先来一张时序图,看下消息是怎么一步步发送出来的。
此刻,应该要开车了。前方高能!!!
1.进入的是Handler.sendMessage
方法
public final boolean sendMessage(@NonNull Message msg)
return sendMessageDelayed(msg, 0);
2.接下来继续调用Handler.sendMessageDelayed
方法
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis)
if (delayMillis < 0)
delayMillis = 0;
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
3.接着走Handler.sendMessageAtTime
方法,这里面就要用到MessageQueue
对象了,此处说明一下,这个mQueue
是在哪里获取到的,是在Handler
构造方法里。此处贴图,从图中可以看出mLooper=Looper.myLooper() mQueue=mLooper.mQueue Handler
中的MessageQueue
是Looper
中持有的MessageQueue
对象 。
注1 为啥要用静态内部类---->如果我们使用Handler
类,没有用static
关键字修饰的话,则会输出Log: The following Handler class should be static or leaks might occur
: 会提示你可能会引起内存泄漏。因此在注1 处我用了static
修饰。
好,这里就说这么多,接着开车:
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
MessageQueue queue = mQueue;
if (queue == null)
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
return enqueueMessage(queue, msg, uptimeMillis);
4.接着时序图上的流程走,此时要进入到MessageQueue.enqueueMessage
方法中,该方法就是将msg
对象存入到MessageQueue
队列中,注意此处,将该handler
对象赋值给了msg.target
,这个后面会用到的,很关键。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis)
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous)
msg.setAsynchronous(true);
return queue.enqueueMessage(msg, uptimeMillis); //3,即将进入MessageQueue.enqueueMessage 方法。
5.接着来看MessageQueue.enqueueMessage
方法,该方法就是按照时间的顺序插入到Message
这个链表结构的数据对象中去。
boolean enqueueMessage(Message msg, long when)
if (msg.target == null) //4. 后面说明,这个也就是四大金刚图里的msg.target 所持有的Handler 对象。
throw new IllegalArgumentException("Message must have a target.");
synchronized (this)
...
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
// 链表的插入操作,不太熟悉的可以看看数据结构。(此处是根据时间来排序的)
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); //画重点,此处唤醒等待的next 方法。
return true;
此时,一条消息就相当于入队了。 MessageQueue
从名称来看是队列,实际上,使用的还是Message.next
指针来进行操作的,也即是链表的操作。消息的入队完成,后面将会介绍该消息是怎么发送出去的。
6.Loop.loop
方法,敲重点。省略了部分代码,只关注核心代码。这里用到了死循环,不停的获取Message
对象,获取到之后直接调用Message.target
变量所持有的Handler
对象,然后调用Handler.dispatchMessage
方法,这样就完成了消息的分发。
public static void loop()
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;)
Message msg = queue.next(); // might block //7.通过MessageQueue.next()方法获取Message对象。
...
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
finally
if (traceTag != 0)
Trace.traceEnd(traceTag);
...
msg.recycleUnchecked();
MessageQueue.next()
方法获取Message
对象。
Message next()
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) //死循环
if (nextPollTimeoutMillis != 0)
Binder.flushPendingCommands();
nativePollOnce(ptr, nextPollTimeoutMillis); // 5: 避免了阻塞的关键点,释放资源,处于等待。疑点:处于等待,肯定需要一个东西来唤醒它。上面第5步分析enqueueMessage的时候有行代码if (needWake)
nativeWake(mPtr); //画重点,此处唤醒等待的next 方法。
。
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) //******此条件可以先不看,因为通过Handler 发送的消息target 都会持有Handler,该逻辑不会触发。消息同步屏障的时候会优先触发该逻辑。
// 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) //查找当前的msg 对象。
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;
...
nextPollTimeoutMillis = 0;
8.Handler.dispatchMessage
方法,此处有判断,如果在Activity
中使用view.post
方法调用的时候,就会走到handleCallback
回调中。通过sendMessagexxx
函数发送消息的就会走到handleMessage
回调中去。
/**
* 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);
该方法会将msg
对象发送到客户端定义Handler
的地方,重写的handleMessage
方法。至此,Handler
发送消息的流程大致介绍完成。
五丶总结
Handler
发送消息的时候,在Handler.enqueueMessage
方法中,将该Handler
对象添加到Message
中的target
属性中,这样就完成了Message
持有Handler
的操作,为最后Message.target.dispatchMessage
做了保证。然后将该Message
对象放入到MessageQueue
中的Message.next
中去,完成了消息链表的添加;而这个MessageQueue
是Looper
中所持有的对象,这样就可以通过Looper
类通过对MessageQueue.next()---->Message.next()-->Message.target.dispatchMessage(msg)
完成了消息的分发。
知识点补充:
1.Looper
对象是怎么new
出来的?
2.为什么将Looper
保存在ThreadLocal
中?
3.Message
为什么推荐使用obtain()
方式获取Message
对象,而不推荐使用new Message()
?
4.同步屏障
5.Handler
为什么会导致内存泄漏以及解决方案?
参考内容:
腾讯Android开发笔记+2022Android十一位大厂面试真题+音视频60道面试题+Jetpack+Matrix+JVM
以上是关于扯掉“桥梁Handler”的底裤的主要内容,如果未能解决你的问题,请参考以下文章