• 只要是开发Android的同学,Handler这个经常都会看到,也会使用到,本文章就做个笔记。
  • Android规定了 只能在主线程更新UI ,那子线程想更新UI,在操作完成后,就可用通过Handler发消息,然后在主线程更新UI了。其实可以理解为 生产者-消费者 模式,发送消息,取出消息并处理。
  • Android系统源码 中,Android的消息机制中,大量使用Handler,所以了解Handler非常的有必要。
  • 下图是一个消息发送的简易流程,一各个步骤分析。

    2.Handler简单使用

    2.1 发送消息

  • 最基本的使用就是各种 sendMessage ,带不带参数,是否延迟等。
  • 发送消息方法非常多,根据自己需求选择,所有发送消息最后都是
  • enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis)
  • 2.2 使用

  • 这样直接使用会有内存泄漏风险,后面说。
  • Handler创建有两种,一个是在构造方法传 CallBack ,一个是重写类的 handleMessage() 方法。
  • 	private
    
    
    
    
        
     static final int MESSAGA_TEST_1 = 1;
         * 主线程有初始化好Looper,所以在主线程处理消息
        private Handler mHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                switch (msg.what) {
                    case MESSAGA_TEST_1:
                        if (tvTest != null) {
                            //传递消息obj
                            tvTest.setText((String) msg.obj);
                        break;
                return false;
        private Handler mHandler2 = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what) {
                    case MESSAGA_TEST_1:
                        if (tvTest != null) {
                            //传递消息obj
                            tvTest.setText((String) msg.obj);
                        break;
        private void onClick() {
            //当点击事件执行,就会在子线程发送消息更新textview
            new Thread(new Runnable() {
                @Override
                public void run() {
                    clickEndMessage();
            }).start();
         * 可以在子线程发送
        private void clickEndMessage() {
            //obtain享元模式
            Message message = Message.obtain(mHandler);
            //what相当于标记
            message.what = MESSAGA_TEST_1;
            //obj传数据
            message.obj = new String("baozi");
            //普通发送message
            mHandler.sendMessage(message);
            //发送标记,不带其他内容,内部会封装成只有标记的Message对象
            mHandler.sendEmptyMessage();
            //尾部带有Delayed,发送延迟消息,单位毫秒
            mHandler.sendMessageDelayed(message, 1000);
            //尾部带有AtTime,发送消息的时间跟Delayed差别就是Delayed是执行的当前时间+传进去的时间,AtTime就传进去的绝对时间
            mHandler.sendMessageAtTime();
            //在队列头插入消息
            mHandler.sendMessageAtFrontOfQueue(message);
        @Override
        protected void onDestroy() {
            if(mHandler!=null){
            	//关闭activity时,移除消息
                mHandler.removeMessages(MESSAGA_TEST_1);
                mHandler = null;
            super.onDestroy();
    

    2.3 view.post()

  • 比如view,post()、postDelayed() 方法,可以延迟五秒后更新UI,实际就是使用Handler。
  • tvTest.postDelayed(new Runnable() {
        @Override
        public void run() {
            tvTest.setText("5s");
    },5*1000);
     * View 源码
    public boolean postDelayed(Runnable action, long delayMillis) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.postDelayed(action, delayMillis);
        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().postDelayed(action, delayMillis);
        return true;
    

    2.4 runOnUiThread

  • 经常用的 runOnUiThread() 方法也是用Handler。
  • runOnUiThread(new Runnable() {
        @Override
        public void run() {
            //更新UI
     * Activity 源码
    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
    

    3.子线程中使用

    3.1 子线程直接创建Handler错误

  • 子线程不能直接创建Handler,会报异常,因为Looper还没创建,而主线程默认就初始化好Looper
  • 应该先Looper.
  •     private Handler handler2;
         * 子线程
        private void thread() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    handler2 = new Handler(new Handler.Callback() {
                        @Override
                        public boolean handleMessage(@NonNull Message msg) {
                            return false;
            }).start();
    
  • 提示的错误。

    3.2 主线程默认初始化Looper

  • ActivityThread 类就能主线程找到Looper初始化,Looper.prepareMainLooper();
  •     public static void main(String[] args) {
            Looper.prepareMainLooper();
            ActivityThread thread = new ActivityThread();
    

    3.3 Handler构造方法查看

  • 构造方法中可以看到 mLooper = Looper.myLooper(); 获取的 mLooper 为null,就报上面的那个异常了。
  •     public Handler(@Nullable Callback callback, boolean async) {
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
    

    3.4 子线程正确的创建

  • 先执行,Looper.prepare() 初始化Looper,然后调用loop()方法,再创建Handler。
  • 每个线程 Looper.prepare() 只能调用一次,否则会报错。
  •     private Handler handler2;
         * 子线程
        private void thread() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Looper.prepare();
                    Looper looper = Looper.myLooper();
                    looper.loop();
                    handler2 = new Handler(looper, new Handler.Callback() {
                        @Override
                        public boolean handleMessage(@NonNull Message msg) {
                            return false;
            }).start();
    

    4.Message

  • Message就是一个存放消息的类,是一个链表结构。
  • 4.1 基本参数

    public final class Message implements Parcelable {
       	//用于handler标记处理的
        public int what;
        //可以传递的int参数1
        public int arg1;
        //可以传递的int参数2
        public int arg2;
    	//可以传递的obj参数
        public Object obj;
        //执行时间
        public long when;
    	//传递的bundle
        Bundle data;
    	//Message绑定的Handler
        Handler target;
    	//Handler.post()时传的callback
        Runnable callback;
    	//链表结构
        Message next;
    

    4.2 享元模式obtain()

  • obtain() 可以重用Message,减少开销提高性能。
  • 菜鸟教程-享元模式
  • * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. 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();

    4.3 回收recycle()

  • 如果发送的延迟消息,或者消息在执行,就会报错,一般我们不用调用recycle方法。
  • * Return a Message instance to the global pool. * You MUST NOT touch the Message after calling this function because it has * effectively been freed. It is an error to recycle a message that is currently * enqueued or that is in the process of being delivered to a Handler. public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); return; recycleUnchecked();
  • Message用完并不是内存回收,只是把里面的内容清空,等下次复用。
  • 这个链表也不是无限的,最多就50个节点
  •     private static final int MAX_POOL_SIZE = 50;
        @UnsupportedAppUsage
        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) {
            	//最多50个
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
    

    5.MessageQueue

  • 这是一个阻塞队列。
  • 队列是在不停的for循环的,是一个死循环,那不就一直占用着cpu?所以就有了native的方法,处理休眠唤醒。
  • 5.1 MessageQueue每个线程只有一个

  • 消息队列每个线程只有一个,跟Looper绑定在一起,在分析Looper时会一起分析。
  • 5.2 消息入队

  • 前面我们知道Handler所有消息入队最后都是调用 enqueueMessage(Message msg, long when)
  • 判断插入消息的位置,还判断是否唤醒操作。
  •     boolean enqueueMessage(Message msg, long when) {
            //handler为空就报异常
            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) {
                	//如果队列为空,或者消息延迟时间为0,或者延迟时间小于mMessage的,就插入在头部
                    // 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;
    

    5.3 消息出队

  • 消息出队其实应该放在 Looper.loop() 里面分析更合适,这里先写,后面结合 loop() 一起看会更好。
  • 前面 return null; 看注释的意思是,如果looper已经退出和释放,就返回null。
  • 这里无限循环,就是一定要取到消息,有消息,阻塞时间为消息的执行时间减去当前时间,如果没消息就阻塞, nativePollOnce(ptr, nextPollTimeoutMillis)
  • 这 next() 方法只有一个地方返回msg,关注这里就行了。
  •     @UnsupportedAppUsage
        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;
                    if (msg != null && msg.target == null) {
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                            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();
                            //只有这里返回msg
                            return msg;
                    } else {
                    	//没有更多消息,设置为-1,阻塞
                        // No more messages.
                        nextPollTimeoutMillis = -1;
    

    5.4 退出

  • 主线程是不能退出。
  • 传入的 safe 处理,分别是移除消息未执行的消息,和移除全部消息。
  •     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);
    	 * 移除全部消息
        private void removeAllMessagesLocked() {
            Message p = mMessages;
            while (p != null) {
                Message n = p.next;
                p.recycleUnchecked();
                p = n;
            mMessages = null;
    	 * 移除未执行的消息,正在运行的等待完成再回收
        private void removeAllFutureMessagesLocked() {
            final long now = SystemClock.uptimeMillis();
            Message p = mMessages;
            if (p != null) {
                if (p.when > now) {
                	//如果执行时间还未到,即未执行的消息,移除回收
                    removeAllMessagesLocked();
                } else {
                	//等待在执行的消息执行完再回收移除
                    Message n;
                    for (;;) {
                        n = p.next;
                        if (n == null) {
                            return;
                        if (n.when > now) {
                            break;
                        p = n;
                    p.next = null;
                        p = n;
                        n = p.next;
                        p.recycleUnchecked();
                    } while (n != null);
    

    6.Looper

  • Handler要负责发送消息,MessageQueue消息队列存放消息,Looper就是负责消息循环。
  • 商场的扶手电梯大家应该都知道吧,可以把扶手电梯的电机看成Looper,一梯一梯看成消息队列MessageQueue,坐电梯的人看成Message,就这样不停的循环,把消息送去处理。
  • 6.1 ThreadLocal

  • Android 开发也要掌握的Java知识 -ThreadLocal 可以看下ThreadLocal原理。
  • Looper就是用到了ThreadLocal,Looper内部直接就有一个,还定义成static,final了,也就意味着Android里面获取的ThreadLocal只有这一个。
  •     @UnsupportedAppUsage
        static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    

    6.2 初始化prepare(),为何只能调用一次

  • 在prepare()方法可以看到,如果初始化过,就调用就会报错了,所以每个线程最多只有一个Looper,但Handler可以有很多个。
  •     /** Initialize the current thread as a looper.
          * This gives you a chance to create handlers that then reference
          * this looper, before actually starting the loop. Be sure to call
          * {@link #loop()} after calling this method, and end it by calling
          * {@link #quit()}.
        public static void prepare() {
            prepare(true);
        private static void prepare(boolean quitAllowed) {
        	//每个线程只能有一个looper
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            //初始化后设置给sThreadLocal
            sThreadLocal.set(new Looper(quitAllowed));
    

    6.3 绑定当前线程,创建消息对列

  • 创建消息对列,Looper绑定到当前Thread。
  • quitAllowed消息队列是否可销毁,主线程的是不可销毁的,子线程默认是可销毁。
  •     private Looper(boolean quitAllowed) {
        	//创建消息队列
            mQueue = new MessageQueue(quitAllowed);
            //绑定当前线程
            mThread = Thread.currentThread();
    

    6.4 拿到当前线程的looper

    * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. public static @Nullable Looper myLooper() { return sThreadLocal.get();

    6.5 loop()

  • 首先拿到当前线程的looper,如果没有prepare()那就是空,报异常。
  • 然后就是不停的循环,取出消息,再处理消息。
  • Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
  • * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. public static void loop() { //拿到当前线程的looper,如果没有prepare()那就是空,报异常 final Looper me = myLooper(); if (me == null) { 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."); for (;;) { //不停的取消息 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; try { //处理消息,msg.target就是绑定的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); //回收消息 msg.recycleUnchecked();

    6.6 退出

  • 退出 Looper 其实就是调用 MessageQueue 的退出,传的 safe 不同而已,那这两段注释说啥。
  • 传 false ,就是说后面发送消息都不会再处理了,发送消息全部都失败,而且该方法不安全,建议使用 quitSafely()。
  • 传 true,跟上面效果差不多,就是MessageQueue的逻辑,移除未执行的消息,正在运行的等待完成。
  • * Quits the looper. * Causes the {@link #loop} method to terminate without processing any * more messages in the message queue. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p><p class="note"> * Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. * @see #quitSafely public void quit() { mQueue.quit(false); * Quits the looper safely. * Causes the {@link #loop} method to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * However pending delayed messages with due times in the future will not be * delivered before the loop terminates. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. public void quitSafely() { mQueue.quit(true);

    7.Handler

    7.1 消发送息

  • Handler 发送消息最后都是执行 enqueueMessage()
  • 这就做了 Message 的 target 指向 handler 自己。
  • 消息入队。
  • mAsynchronous 默认就是false , 构造方法我们基本也不会去动这参数,默认handler 是同步的。
  •     private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                long uptimeMillis) {
            //消息的target就是handler
            msg.target = this;
            msg.workSourceUid = ThreadLocalWorkSource.getUid();
    		//设置异步
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            //消息入队
            return queue.enqueueMessage(msg, uptimeMillis);
    

    7.2 处理消息

  • Looper.loop() 里面 msg.target.dispatchMessage(msg); 就是让 Handler 处理消息。
  • 这里可以看到三种,第一种是 Handler 发送消息用的 post , 就会把 Callback 封装到 Message ,走 Message 自己的 Callback 。
  • 第二种是Handler 创建时,重写了 Callback,这是有返回值得,如果为 true ,就会再执行下面的 handleMessage 方法。
  • 第三种就是没传 Callback ,就执行 Handler 自己的 handleMessage ,但要重写该方法。
  • * Handle system messages here. public void dispatchMessage(@NonNull Message msg) { //Message 内部的callback,就是Handler,post()方法封装在Message内部的 if (msg.callback != null) { handleCallback(msg); } else { //如果Handler创建时传Callback就执行这里 if (mCallback != null) { //如果返回值为false 就结束 if (mCallback.handleMessage(msg)) { return; //如果mCallback为null,或者上面返回值为true,就执行这里 handleMessage(msg); * Message 内部的callback,就是Handler,post()方法封装在Message内部的 private static void handleCallback(Message message) { message.callback.run(); * 创建时如果传Callback,就执行 public interface Callback { * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired boolean handleMessage(@NonNull Message msg); * 创建时如果没传Callback,而是重写了该方法 public void handleMessage(@NonNull Message msg) {

    8.内存泄漏

  • 面试的时候贼喜欢问这个问题!!!
  • 小题大做 | 内存泄漏简单问,你能答对吗
  • 9.HandlerThread

  • HandlerThread 是谷歌封装好 Looper 的 Thread ,子线程如果需要Handler推荐使用
  • 这里就是保证在使用Handler之前,Looper 一定准备好。
  • public class HandlerThread extends Thread {
        int mPriority;
        int mTid = -1;
        Looper mLooper;
        private @Nullable Handler mHandler;
        public HandlerThread(String name) {
            super(name);
            mPriority = Process.THREAD_PRIORITY_DEFAULT;
     	@Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            //获得锁创建looper
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
         * This method returns the Looper associated with this thread. If this thread not been started
         * or for any reason isAlive() returns false, this method will return null. If this thread
         * has been started, this method will block until the looper has been initialized.
         * @return The looper.
        public Looper getLooper() {
            if (!isAlive()) {
                return null;
            // If the thread has been started, wait until the looper has been created.
            synchronized (this) {
               //如果线程存活而且looper为空,就等待让出锁,直到looper创建
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
            return mLooper;
    

    文章写完了,发现别人有更好的,想了想还是把自己写的发出去吧。。。 Android消息机制1-Handler(Java层) 小题大做 | 内存泄漏简单问,你能答对吗 又又又又攒了一个月的Android面试题

    分类:
    Android
    标签: