Android:Handler源码阅读与理解

Handler是什么?只是为了跨线程更新UI吗?它是怎么把消息从子线程传递到主线程的?消息管理机制又是怎样的?带着这些疑问开始阅读Handler模块源码

APP启动流程

Lancher:zygote ==> jvm ==> ActivityThread.main
下面是 ActivityThread 的 Main() 的部分代码

 public static void main(String[] args) {
 		。。。
 	 Looper.prepareMainLooper();
 	 	。。。
 	 Looper.loop();
 		。。。
 }		

ActivityThread 中初始化 Looper 并开始 loop(),loop 内部是死循环,APP所有的代码都是在 handler 上运行的

Handler的工作流程

流程: handler.sendMessage() --> messageQueue.enqueueMessage() 把消息放到消息队列,消息队列插入节点(优先级队列,单链表) --> Looper.loop 内部有个死循环,调用messageQueue.next() 取走消息 --> messageQueue.next() 取走消息 --> msg.target.dispatchMessage(msg) 消息分发出去 target是handler --> handler.handleMessage()

handler 调用 sendMessage(或其他函数) 发送消息,最后都会走到 sendMessageAtTime() 函数

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);
}

进而调用 enqueueMessage 函数

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);
 }

整体调用流程如下图
在这里插入图片描述
下面是 enqueueMessage 的具体实现

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        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;
        }
    }
    return true;
}

上面逻辑比较简单,是消息的入队操作。此处的数据结构是个优先级队列,以时间为顺序。
队列的好处?先进先出,取数据直接取头部
至此 Handler 发送的消息已经进入 MessageQueue 的队列中。那消息是怎么被取走的?还记得 Looper 的 loop() 函数吗

public static void loop() {
     //  此处是死循环的地方,也是取消息 
     for (;;) {
     	// might block  此处可能会阻塞,比如 MessageQueue 为空;;不断的从 MessageQueue 中取走消息
         Message msg = queue.next(); 
         if (msg == null) {
             // No message indicates that the message queue is quitting.
             return;
         }
       
         try {
         	//	target 就是 handler ,调用 handler 的 dispatchMessage 函数
             msg.target.dispatchMessage(msg);
             if (observer != null) {
                 observer.messageDispatched(token, msg);
             }
             dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
         }
     }
 }

Loop函数的作用是不断的取走MessageQueue中的msg

 Message msg = queue.next(); 

msg 中有个变量 target 存储着 handler,调用其 dispatchMessage 分发消息

msg.target.dispatchMessage(msg);

dispatchMessage函数实现

 /**
  * Handle system messages here.
  */
 public void dispatchMessage(@NonNull Message msg) {
     if (msg.callback != null) {
         handleCallback(msg);
     } else {
         if (mCallback != null) {
             if (mCallback.handleMessage(msg)) {
                 return;
             }
         }
         handleMessage(msg);
     }
 }

到这里就是我们熟悉的 handleMessage 了,整个流程到此结束。从上面流程不难发现,整个流程在动的只有Message,message只是一块内存,不分什么主线程或子线程,这也解释了消息如何从子线程发送到主线程,运用的内存共享机制。

Looper

核心:构造函数、loop函数、ThreadLocal

构造函数
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper的构造函数是私有的,只能由prepare创建,如何保证每个线程最多只有一个Looper?ThreadLocal

	//	static 、 final 修饰,每个线程的Looper都存在这里
	static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
    	//	判断当前的线程是否已经存在,如果存在则抛出异常,不存在则创建当前线程的 Looper 并存入 sThreadLocal
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

线程 ===》 ThreadLocalMap ===》 <唯一的ThreadLocal,value> ThreadLocal 保证了 key 唯一
sThreadLocal.get() != null 判断保证了值唯一 ===》 保证 Looper 唯一,MessageQueue唯一

    final MessageQueue mQueue;
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper 保证唯一后,在 Looper 的构造函数中初始化 mQueue ,mQueue 由 final 修饰,初始化后唯一
如此:每个线程保证了最多只有一个 Looper,MessageQueue

loop函数 与 消息管理机制

消息的睡眠与唤醒机制理论上应该是个:生产者 - 消费者设计模式
在这里插入图片描述
入队操作:根据时间排序,当队列满的时候阻塞直到用户通过next取出消息。当next方法被调用,通知MessageQueue可以进行消息的入队
出队操作:由Looper.loop()启动轮询器,对queue进行轮询。当消息达到执行时间就取出来。当MessageQueue为空的时候,队列堵塞,等消息队列调用enqueueMessage的时候通知队列可以取出消息,停止阻塞

队列满了不能阻塞,但队列空了是可以阻塞的,这里可以分两种情况:
  1. 如果 next() 拿到的 msg 还没到执行时间 - - - 自动唤醒
//	Looper.java  loop函数
Message msg = queue.next(); // might block
//	MessageQueue.java   next()函数
Message next() {
    ···
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //  第二次循环到此时,阻塞时间 nextPollTimeoutMillis
        //  调用底层 native 函数 --- Looper.cpp  epoll_wait(),阻塞线程
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            ····
            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;
            }
        }
    }
}

loop 在调用 queue.next() 时,如果获取的msg还没到执行时间 if (now < msg.when) 计算出要等待的时间,在下次循环中调用nativePollOnce(ptr, nextPollTimeoutMillis) 实现阻塞

  1. 消息队列为空 - - - 无限等待
   for (;;) {
	//	第二次循环至此,开始无限等待
	 nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            if (msg != null) {
               。。。。
            } else {
                // No more messages.  没有消息了,开始无限等待
                nextPollTimeoutMillis = -1;
            }
    }        
阻塞机制的实现

翻看源码,我们可以得知阻塞由Linux层的epoll、wake实现
在这里插入图片描述
在这里插入图片描述

native层有对应的Looper、MessageQueue一套机制。与select相比,epoll 的时间复杂度是o(1),而select的时间复杂度是o(n)。

MessageQueue

MessageQueue的数据结构:优先级队列

MessageQueue是一个优先级队列

//	MessageQueue.java 里
Message mMessages;
//	Message.java 里
Message next;		//	指向下一个msg
public long when;	//	执行时间

Message 中有个 next 引用指向下一个msg,构成队列;msg 入队时根据执行时间排序;所以MessageQueue是个优先级队列

消息机制 之 同步屏障

同步屏障,怎么理解?什么地方应用?
当下很多手机都是60hz,即1秒刷新60次屏幕,即16ms刷新一次屏幕,所以所有与UI刷新的msg都需要优先处理!
先看下 MessageQueue 里的代码实现

//	MessageQueue.java  next()函数
if (msg != null && msg.target == null) {
        // Stalled by a barrier.  Find the next asynchronous message in the queue.
        do {
            prevMsg = msg;
            msg = msg.next;
            //	找到 msg.isAsynchronous() == true 的 msg
        } while (msg != null && !msg.isAsynchronous());
}

如果第一个 msg 的 target=null 则遍历队列,找到 msg.isAsynchronous(),找到后先执行
看个同步屏障的具体实现

ViewRootImpl.java 
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //  handler 发送 同步屏障 刷新 UI
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //  发送 同步屏障后, 发送异步消息
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

//   mChoreographer.postCallback 最终会调用下面函数
private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {
    if (DEBUG_FRAMES) {
        Log.d(TAG, "PostCallback: type=" + callbackType
                + ", action=" + action + ", token=" + token
                + ", delayMillis=" + delayMillis);
    }
    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            scheduleFrameLocked(now);
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            //  重点 !!! 设置是否是异步消息
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}

//	看下 postSyncBarrier 的实现
private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;
            //  此处没有设置 target,即 target = null,并直接放到队列头部
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

//  取消同步屏障
void unscheduleTraversals() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        //  取消同步屏障
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        mChoreographer.removeCallbacks(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}

整理下思路,屏幕点击后先发一个 target 为 null 的 msg 并放到 MessageQueue 的队头

 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

然后再发一个 msg.isAsynchronous()=true 的消息

 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

当 Looper 的 loop() 函数调用 queue.next() 时,因为首个 message 的 target==null ,遍历队列找到 msg.isAsynchronous()=true的消息并执行,这就是同步屏障!

Message 享元设计模式

Message的创建以及销毁是非常频繁的,如此频繁的操作不做优化很容易造成内存碎片化、内存抖动进而引发内存泄漏OOM

	//	内存回收
    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;
		//	维护了一个池子,用完清空数据并放进来
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

HandlerThread 存在的意义

1. 方便使用:a.方便初始化 b.方便获取线程Looper
2. 保证线程安全
@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        //  唤醒其他等待的锁,但不会释放当前的锁,要执行完当前的代码块才会释放
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                //  释放锁
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

安全体现在哪?举个栗子,run 函数 Looper.prepare(); 还没执行完有其他线程调用了 getLooper() 函数,判断 mLooper == null 调用 wait() 释放当前锁,run 函数继续执行,对 mLooper 进行初始化并调用 notifyAll() 函数唤醒下面的 getLooper() 函数,run 函数锁内的代码执行完成后 getLooper 内的代码继续执行,返回 mLooper !
两个点:wait() 会释放当前锁 ; notifyAll 唤醒其他线程但不会释放当前锁

IntentService 存在的意义

Service一般处理后台耗时任务
应用需求:一项任务分成几个子任务,子任务按顺序先后执行,子任务全部完成后这项任务才算成功
这个需求可以用多个多线程来处理,一个线程处理完 -> 下一个线程 -> 下一个线程
IntentService就可以帮我们完成这个工作。而且能够很好的管理线程,保证只有一个子线程处理工作,而且是一个一个的完成任务,有条不紊的进行
优点:

  1. 任务结束后调用 stopSelf(msg.arg1) 结束Service,释放内存
  2. 可以保证所有的任务都在一个线程中执行,HandlerThread保证
  3. 保证子任务按顺序依次执行,由MessageQueue保证,优先级队列

面试题

面试题1:为什么MessageQueue不设置Message的数量上限?

:Handler不只是开发者使用,系统也在使用!系统的消息进不去不完蛋了嘛~

面试题2:一个线程有几个Handler?

:随便new,想几个就几个

面试题3:一个线程有几个Looper?如何保证?

:一个线程只有一个Looper,以ThreadLocal保证,prepare() 判断sThreadLocal.get() != null

面试题4:Handler 内存泄漏原因?为什么其他的内部类没有这个问题

:内部类持有外部类对象;生命周期一致则不会内存泄漏
:handler 持有当前Activity,而 handler 发送的 msg 持有 handler,如果 msg 20分钟后执行,activity不能释放造成内存泄漏

//  Handler.enqueueMessage()
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //  handler 调用 MessageQueue 的 enqueueMessage() 方法
    return queue.enqueueMessage(msg, uptimeMillis);
}

//  MessageQueue.enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
·····
}

// Looper.loop()
public static void loop() {
    //  此处的 target 就是 Handler
    msg.target.dispatchMessage(msg);
}
面试题5:为何主线程可以 new Handler ? 如果想要在子线程中 new Handler 要做些什么准备?
// 子线程必须
Looper.prepare();
Looper.loop();
面试题6:子线程中维护的 Looper,消息队列无消息的时候处理方案是什么?有什么用?

:主线程阻塞,参见上面 MessageQueue 的两个阻塞;
:子线程 调用 quit 函数 - - - 1.唤醒线程 2.MessageQueue返回一个null 3.退出loop

Looper.myLooper().quit();
面试题7:既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能出事不同线程),内部如何确保线程安全?

:锁。synchronized 内置锁?JVM完成,自动完成 synchronized(this)类对象所有的代码块、函数都会锁住,enqueueMessage时next无法调用。也就说一个线程只有一个地方可以操作MessageQueue队列
PS:为啥取消息的时候也要加锁?因为取消息的时候可能在插入,容易混乱~同样quit时也要加锁

面试题8: Message是如何从子线程到主线程的?

:Message本身只是一块内存,不分线程。
:子线程 handler.sendMessage(msg) --> MessageQueue.enequeMessage(msg)
:主线程 轮询MessageQueue --> msg

面试题9:使用 Message 时该如何创建它?

:享元设计模式(内存复用) loop{ msg.recycleUnchecked(); 回收 } 维持一个池子,防止碎片太多,防止内存抖动,防止OOM

面试题10: Looper 的死循环为什么不会导致应用卡死?block为什么不会导致应用卡死?

:卡死即是ANR,Looper的死循环与 ANR 并没有关系,这两个是不相关的!
ARN原因:点击5s没响应;广播10s没响应;service 20s没相应
深层次剖析:点击事件封装成 msg 发出去,5s没处理完,handler 发出 ANR 提醒
block为啥不会导致 ANR ?—》线程没事做了交出CPU,休眠。有事做了会唤醒

面试题11:Handler什么时候会处于阻塞状态?如何唤醒?

:MessageQueue的队头msg未到执行时间,到了执行时间会自动唤醒

 for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //	睡眠 nextPollTimeoutMillis 时长
            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 ,下轮循环的时候睡眠对应的时长
                        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;
                }
            }
        }

//	ptr为-1,表示无限等待;如果为 0 则无需等待
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/

:MessageQueue为空进入无限等待,当MessageQueue执行enqueueMessage时会自动唤醒

//	在 MessageQueue.java 的 enqueueMessage 函数 
if (needWake) {
     nativeWake(mPtr);
 }

看了又看,想了又想,读了又读,这就是源码的魅力吧!

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值