你真的了解Android的Handler机制吗?

在Android系统中,Handler机制应用较广,尤其是在App层面,基本每个App都会用到。使用的场景主要是向主线程发送事件,更新UI。但大家真的了解Handler机制吗?看一下面的几个问题是否可以回答:
a.Handler是如何实现多个线程之前事件传递的?
b.Handler、Message、MessageQueue、Looper相互之间的数量比是多少,都是1:1吗?
c.每个变量运行的线程是那个?
d.ThreadLocal是怎么回事,在Handler机制中起什么作用?
能准确回答上述问题,说明对Handler机制的理解已相当到位了。
    下面论述一下Handler机制各类的实现原理,从而揭露内部的工作流程。(源码环境为Android8.0)
1. 先来看Handler类:
1.1 构造方法:
Handler的构造方法总共有如下几个,最终实现为下面两个方法:
public Handler(Callback callback, boolean async) {
if ( FIND_POTENTIAL_LEAKS ) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier. STATIC ) == 0 ) {
Log. w ( TAG , "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

mLooper = Looper. myLooper ();
if ( mLooper == null ) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()" );
}
mQueue = mLooper .mQueue;
mCallback = callback;
mAsynchronous = async;
}

public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
最终都调至Handler(Callback callback, boolean async)和Handler(Looper looper, Callback callback, boolean async),看一下源码可知,主要为初始化几个变量:mLooper、mQueue、mCallback,mAsynchronous,前三个变量非常重要,有以下几个结论:
a.一个Handler中含有一个Looper
b.Handler中持有的MessageQueue源于Looper中,与Looper中该变量行为一致
c.一个Handler中含有一个Callback
Handler中还有如下几个重要的方法,提供了Handler对外的能力:
1.2 obtainMessage方法,对外提供一个获取Message缓存池中Message的方法,避免应用产生过多的Message对象。根据参数的不同,重载有多个该方法,实现原理都一样,以其中之一分析一下:
// obtainMessage@Handler.java
public final Message obtainMessage()
{
return Message. obtain ( this );
}
// obtain@Message.java
public static Message obtain(Handler h) {
Message m = obtain ();
m.target = h;

return m;
}
// obtain @Message.java
public static 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;
}
}
return new Message();
}
sPool提供复用功能的实现,其存储结构为链表,通过sPoolSize的大小及SPool头部指针位置作到这一点,感兴趣的同学可以研究下
1.3 post方法、sendMessage方法,Handler提供了多个post方法,按参数不同,提供了诸如指定时间、延时的功能,基本功能是一样的,以其中某一个方法说一下其实现:
public final boolean post(Runnable r)
{
return sendMessageDelayed( getPostMessage (r), 0 );
}
private static Message getPostMessage(Runnable r) {
Message m = Message. obtain ();
m.callback = r;
return m;
}
从getPostMessage方法可以看出,传入的Runnable被Message所持有。
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 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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this ;
if ( mAsynchronous ) {
msg.setAsynchronous( true );
}
return queue.enqueueMessage(msg, uptimeMillis);
}
由上面可知如下结论:
a.post方法的实现是与sendMessage一致的,最终都调至sendMessage中
b.干了个什么事呢:得到一个message,将它的callback设为传入的callback(如果有的话),将它的target设为自身,然后调用MessageQueue的enqueueMessage处理该Message,实现原理讲到MessageQueue再行讨论。
1.4 dispatchMessage方法,该方法从Handler发起流程中难以看到在什么地方调用了,是一个消息处理时的回调,先讲一下该方法的逻辑,后面再说调用时机
public void dispatchMessage(Message msg) {
if (msg.callback != null ) {
handleCallback (msg);
} else {
if ( mCallback != null ) {
if ( mCallback .handleMessage(msg)) {
return ;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
从上面的逻辑中看到如下结论:
消费该Hanlder事件总共有三种可能的途径,分别为Message中的callback、Handler所持有的Callback对象、Handler的自身的handleMessage方法(一般写法是构造匿名子类,实现该方法),三种途径的优先级从高到低。
Handler在该机制中的作用为:
a.对外提供可复用的Message
b.对外提供发消息的接口
c.对外提供回调的实现,三种方式
2.Looper类
在主线程中使用Handler,看不到Looper,因为系统已经帮我们自动生成了Looper,在工作线程中使用Handler则必须先调用Looper的prepare生成Looper,否则会报错,原因如下:
测试代码:
Thread t = new Thread ( new Runnable() {
@Override
public void run() {
Handler handler = new Handler();
}
});
t.start();
运行后报错:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
Handler最终会调至(1.1中第一个构造方法)
if ( mLooper == null ) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()" );
}
因此正确的写法为:
Thread t = new Thread ( new Runnable() {
@Override
public void run() {
Looper. prepare ();
Handler handler = new Handler();
// ignore
Looper. loop ();
}
});
2.1 prepare方法:
public static void prepare() {
prepare ( true );
}
private static void prepare( boolean quitAllowed) {
if ( sThreadLocal .get() != null ) {
throw new RuntimeException( "Only one Looper may be created per thread" );
}
sThreadLocal .set( new Looper(quitAllowed));
}
sThreadLocal变量定义如下:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
使用ThreadLocal类对Looper进行了一层封装。
像c++的内联函数一样,此处我们直接内联一下ThreadLocal这个类,看一下该类的作用,先从Looper中跳出去一下

--------------------------------此处插入ThreadLocal类----------------------------------
3. ThreadLocal类
ThreadLocal达到的目的是:每一个ThreadLocal的变量在多个线程中都有一份copy,相互间独立存储,互不影响,但变量名只有一个。也就是说对于该变量的使用方来讲,看起来像是只定义了一个变量,但实际上在多线程环境中,有互不影响的、每个线程都有一份的对象存在。如何作到这一点呢,是通过其对外提供的get方法和set方法做到的。
3.1 先来看set方法:
public void set( T value) {
Thread t = Thread. currentThread ();
// 第一步,拿到每个线程所对应的ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 第二步,将该值存入或更新map的对应位置
if (map != null )
map.set( this , value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap( this , firstValue);
}
// ThreadLocalMap的构造方法
ThreadLocalMap (ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[ INITIAL_CAPACITY ];
int i = firstKey.threadLocalHashCode & ( INITIAL_CAPACITY - 1 );
table [i] = new Entry(firstKey, firstValue);
size = 1 ;
setThreshold( INITIAL_CAPACITY );
}
// set@ThreadLocalMap
private void set(ThreadLocal<?> key, Object value) {

// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.

Entry[] tab = table ;
int len = tab. length ;
int i = key.threadLocalHashCode & (len- 1 );

for (Entry e = tab[i];
e != null ;
e = tab[i = nextIndex (i, len)]) {
ThreadLocal<?> k = e.get();

if (k == key) {
e. value = value;
return ;
}

if (k == null ) {
replaceStaleEntry(key, value, i);
return ;
}
}

tab[i] = new Entry(key, value);
int sz = ++ size ;
if (!cleanSomeSlots(i, sz) && sz >= threshold )
rehash();
}
上述逻辑第一步拿到每个线程的ThreadLocalMap对象
// threadlocals#Thread
ThreadLocal .ThreadLocalMap threadLocals = null ;
也就是说每个线程维护一个ThreadLocalMap来存储该线程所用到的所有ThreadLocal变量,ThreadLocalMap的实现原理与HashMap基本一致,之前讨论过HashMap的实现原理,感兴趣的同学对比一下源码就能明白。
第二步中将传入的值存入ThreadLocalMap的对应位置,该位置的key由ThreadLocal本身的hash值去运算得到的。从上面的代码中可以看出。每一个线程持有一个ThreadLocalMap,set的value将和ThreadLocal自身组成key-value结构存在于该Map中。
3.2 get方法
public T get() {
Thread t = Thread. currentThread ();
ThreadLocalMap map = getMap(t);
if (map != null ) {
ThreadLocalMap.Entry e = map.getEntry( this );
if (e != null ) {
@SuppressWarnings ( "unchecked" )
T result = ( T )e. value ;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread. currentThread ();
ThreadLocalMap map = getMap(t);
if (map != null )
map.set( this , value);
else
createMap(t, value);
return value;
}
理解了set方法之后,get方法就较为简单了,反向拿到存入的值即可,ThreadLocal对象本身在多个线程中只有一份,但通过其set和get,可以得到其包装对象在每个线程中只有一份。
--------------------------ThreadLocal分析完毕,回至Looper----------------------------------
Looper的prepare为一个静态方法,目的是可以生成每个线程都有一份的Looper
private static void prepare( boolean quitAllowed) {
if ( sThreadLocal .get() != null ) {
throw new RuntimeException( "Only one Looper may be created per thread" );
}
sThreadLocal .set( new Looper(quitAllowed));
}
2.2 Looper构造方法:
private Looper( boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread. currentThread ();
}
持有一个MessageQueue对象,持有当前的线程对象
2.3 loop方法
public static void loop() {
// 第一步:myLooper的实现看下方,得到当前线程的Looper
final Looper me = myLooper ();
if (me == null ) {
throw new RuntimeException( "No Looper; Looper.prepare() wasn't called on this thread." );
}
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 ();
// 第二步:定义一个无限循环体,遍历MessageQueue,取其中的有效Message,进行处理,处理语名为:msg.target.dispatchMessage(msg);该方法请看上面1.4
for (;;) {
Message msg = queue.next(); // might block
if (msg == null ) {
// No message indicates that the message queue is quitting.
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 );
}

final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace. isTagEnabled (traceTag)) {
Trace. traceBegin (traceTag, msg.target.getTraceName(msg));
}
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);
}
}
if (slowDispatchThresholdMs > 0 ) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w( TAG , "Dispatch took " + time + "ms on "
+ Thread. currentThread ().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg. what );
}
}

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 );
}
// 此处实现Message的循环利用
msg.recycleUnchecked();
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal .get();
}
在loop的处理中,有一点需重点关注,loop在线程中是一个无限循环,跳出该循环的条件为MessageQueue的next返回值为空,看一下4.2的next方法实现,返回空依赖于mQuitting变量,也就是说在线程中不需要一直等待事件时,要把MessageQueue该变量置为true,设置入口为 quit@Looper
2.4 quit方法
public void quit() {
mQueue .quit( false );
}
// quit@MessageQueue
void quit( boolean safe) {
if (! mQuitAllowed ) {
throw new IllegalStateException( "Main thread not allowed to quit." );
}

synchronized ( this ) {
if ( mQuitting ) {
return ;
}
mQuitting = true ;

if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}

// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake ( mPtr );
}
}
总结一下:Looper在Handler机制中起的作用是:
a.提供每个线程唯一的实例
b.触发MessageQueue的循环遍历
4. MessageQueue类
顾名思义,该类需提供三个能力:
a.Message队列存储
b.Message入队
c.Message出队
Message队列以链表形式存储,其头部指针存放于mMessages
4.1 enqueueMessage方法,该方法用于Message入队
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null ) {
throw new IllegalArgumentException( "Message must have a target." );
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use." );
}

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;
// 以下为入队的关键代码,找到链表的队尾,放入该Message
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;
// 注意,入队的时候,已经按时间次序将Message插入队列中的合适位置
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 ;
}
需要注意,在上面的入队处理中,要按Handler传入的执行时间插入
enqueueMessage调用的入口请查看1.3
4.2 next方法,该方法用于MessageQueue出队
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
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 ;
// 第二步:取到最早入队的有效的Message
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) {
Log. wtf ( TAG , "IdleHandler threw exception" , t);
}

if (!keep) {
synchronized ( this ) {
mIdleHandlers .remove(idler);
}
}
}

// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0 ;

// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0 ;
}
}
next方法的调用入口位于2.3中

小结一下,通过上面的代码讲解,可以得到以下几个结论:
1. 首先Handler对象本身位于构造Handler的当前线程,他的事件要发往那个线程,取决于构造方法传 的Looper变量
2. MessageQueue与Looper位于Handler所要发往的工作线程中,Looper持有MessageQueue对象,MessageQueue中看不到Looper
3. Looper与线程为1对1的关系,也就是说一个线程只有一个Looper,一个Looper只服务于一个线程,达到这个目的是通过ThreadLocal包装Looper实现的
4. 从线程的角度讲,Handler对象自身与线程没有任何关系,它是通过Looper持有的MessageQueue实现向Looper所服务的线程事件队列(MessageQueue)插入事件(Message)的

看一下文章开头所说的几个问题是不是已经有了答案:
a.Handler是如何实现多个线程之前事件传递的?
请看上面总结的第4点
b.Handler、Message、MessageQueue、Looper相互之间的数量比是多少,都是1:1吗?
Handler n--1 Looper(以主线程为例,主线程的Looper可以定义非常多Handler)
Looper持有一个MessageQueue(不可逆,看上面总结的第2点)
MessageQueue 1--n Message
c.每个变量运行的线程是那个?
Handler位于定义线程、Looper、MessageQueue位于工作线程,线程的轮询依赖于Looper.loop
d.ThreadLocal是怎么回事,在Handler机制中起什么作用?
可查看代码分析部分的第3部分

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值