Handler详解和源码分析

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情


1 Handler基本介绍

1.1 什么是Handler?

Handler是Android的消息通信机制,用于线程间通信。Handler通信方案是内存共享。Handler可以从子线程向主线程发消息,也可以从主线程向子线程发消息。

在Android开发中,经常会在子线程中进行一些耗时操作,等到操作完成后,就会用Handler将数据发送给主线程,通知主线程去做相应的操作。比如请求到网络数据之后更新UI等。子线程、Handler、主线程构成了一个消费者-生产者模式。生产者和消费者在同一时间段内共用同一个存储空间,生产者(子线程)向存储空间中添加数据(消息),消费者(主线程)向存储空间中取出数据(消息)。

1.2 Handler的相关类

Message

Message是消息实体。

MessageQueue

MessageQueue是一个由单链表实现的优先级消息队列。

Handler

Handler是消息处理类,可以发送消息、获取消息、处理消息、移除消息。

子类实现这个方法来处理从队列中取出的消息。

Looper

用来轮询消息队列。

1.3 Handler消息机制架构图

Handler将消息插入MessageQueue,然后LooperMessageQueue轮询中取出消息交给Handler去处理。

2 Handler的使用

2.1 创建Handler

2.1.1 在主线程中创建Handler

2.1.1.1 匿名内部类的形式

匿名内部类创建Handler是一种常用的方式,简单快捷。但是这样会有内存泄漏的问题,Message持有Handler,而Java中内部类会持有外部类,所以Handler会持有Activity,这样就会可能发生内存泄漏。

private final Handler mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        if (msg.what == 0) {
            Log.d(TAG, "handleMessage: 收到了消息");
        }
    }
};
复制代码

2.1.1.2 静态内部类继承Handler

可以将Handler定义成静态内部类,内部使用WeakReference持有Activity的弱引用,然后onDestroy()的时候即使移除所有回调和消息。

public class HandlerActivity02 extends AppCompatActivity {
    
    private static final String TAG = "HandlerActivity02";
    
    private MyHandler mHandler;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler02);
        
        mHandler = new MyHandler(this);
        
        findViewById(R.id.button).setOnClickListener(v ->
                                                     new Thread(() -> mHandler.sendEmptyMessage(0)).start()
                                                    );
    }
    
    @Override
    protected void onDestroy() {
        //移除所有回调以及消息
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
    
    private static class MyHandler extends Handler {
        
        private WeakReference<Activity> weakReference;
        
        public MyHandler(Activity activity) {
            this.weakReference = new WeakReference<>(activity);
        }
        
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            Activity activity = weakReference.get();
            if (activity != null && msg.what == 0) {
                Log.d(TAG, "handleMessage: 收到了消息");
            }
        }
    }
}
复制代码

2.1.2 在子线程中创建Handler

2.1.2.1 方式1

子线程中添加Looper.prepare()Looper.loop()

new Thread(() -> {
    //方式1
    //实例化Handler之前加上 Looper.prepare()
    Looper.prepare();
    mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0) {
                Log.d(TAG, "handleMessage: 收到了消息");
            }
        }
    };
    mHandler.sendEmptyMessage(0);
    //最后加上Looper.loop()
    Looper.loop();
}).start());
复制代码

2.1.2.2 方式2

使用Handler的有参构造方法Handler(Looper looper),传入Looper.getMainLooper()

new Thread(() -> { 
    //方式2
    //使用有参构造方法Handler(Looper looper),传入Looper.getMainLooper()
    mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0) {
                Log.d(TAG, "handleMessage: 收到了消息");
            }
        }
    };
    mHandler.sendEmptyMessage(0);·
}).start());
复制代码

2.2 生成Message

生成消息的方式有三种,分别是new Message()Message.obtain()mHandler.obtainMessage()

//生成消息方式1
Message message1 = new Message();
message1.what = 0;
message1.arg1 = 1;
message1.obj = null;
mHandler.sendMessage(message1);

//生成消息方式2
Message message2 = Message.obtain();
message2.what = 0;
message2.arg1 = 2;
message2.obj = null;
mHandler.sendMessage(message2);

//生成消息方式3
Message message3 = mHandler.obtainMessage();
message3.what = 0;
message3.arg1 = 3;
message3.obj = null;
mHandler.sendMessage(message3);
复制代码

2.3 发送消息

sendMessage(Message msg)

发送消息,将一条消息添加到消息队列。

sendMessageDelayed(Message msg, long delayMillis)

在以系统开机时间的相对时间(当前时间+delayMillis)之前的所有挂起的消息之后,将一条消息放入消息队列。

sendMessageAtTime(Message msg, long uptimeMillis)

在以系统开机时间的绝对时间(以毫秒为单位)uptimeMillis之前,在所有挂起的消息之后将消息放入消息队列。

sendMessageAtFrontOfQueue(Message msg)

将消息放入消息队列的前端,在消息循环的下一次迭代中进行处理。

sendEmptyMessage(int what)

发送空消息,将一条空消息添加到消息队列。

sendEmptyMessageDelayed(int what, long delayMillis)

在以系统开机时间的相对时间(当前时间+delayMillis)之前的所有挂起的消息之后,将一条空消息放入消息队列。

sendEmptyMessageAtTime(int what, long uptimeMillis)

在以系统开机时间的绝对时间(以毫秒为单位)uptimeMillis之前,在所有挂起的消息之后将空消息放入消息队列。

 mHandler.sendMessage(message);
复制代码

2.4 处理消息

mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        //处理消息
    }
};
复制代码

3 Handler源码分析

3.1 核心流程图

Handler流程图如下所示,展示了整个消息机制的核心流程。

3.2 Looper

在使用Handler发送以及处理消息之前,必须先创建Looper,才能从消息队列中轮询消息。

在主线程中,创建Handler的时候并没有手动去调用Looper.prepare()Looper.loop(),这是因为APP启动过程中在ActivityThread.main()中,已经调用了Looper.prepareMainLooper()Looper.prepareMainLooper()会调用Looper.prepare()

为什么在ActivityThread.main()中调用了进行了Looper的创建,在主线程也就是Activity中就不用再创建Looper了呢?这就涉及到了APP的启动流程,APP的启动流程如下图所示:

从流程图中可以看到在APP进程创建后,会走到ActivityThread.main()中,ActivityThread.main()调用了Looper.prepareMainLooper()Looper.loop(),所以在主线程中就有了Looper

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    ...
    //实例化Looper和MessageQueue
    Looper.prepareMainLooper();
    ...
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    
    if (false) {
        Looper.myLooper().setMessageLogging(new
                                            LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    //轮询消息
    Looper.loop();
    
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码

Looper.prepareMainLooper()方法调用Looper.prepare()。会传入一个boolean型的参数quitAllowed,表示能否退出队列。这里传入的是false,所以在主线程中队列不能退出和销毁。

@Deprecated
public static void prepareMainLooper() {
    //调用Handler.prepare(),传入false,表示消息队列不可退出
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            //如果主线程中Looper已创建,会抛出异常。
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
复制代码

Looper.prepare()new出了一个Looper,并且将其存入ThreadLocal中。这里会判断ThreadLocal中是否已经有Looper存在,如果已经存在,会抛出异常,如果不存在,会new出一个Looper。所以,一个线程中只能有一个Looper存在

private static void prepare(boolean quitAllowed) {
    //这里会判断ThreadLocal中是否已经有Looper存在,如果已经存在,会抛出异常。
    //一个线程中只能有一个Looper存在。
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //new出一个Looper。
    sThreadLocal.set(new Looper(quitAllowed));
}
复制代码

prepare还有一个无参的重载方法,调用该方法,传入的quitAllowed是true,表示可以退出队列。

public static void prepare() {
    //消息队列可以退出
    prepare(true);
}
复制代码

而在Looper的构造方法中,创建了一个MessageQueue,这也同时说明,MessageQueue也是唯一的。

private Looper(boolean quitAllowed) {
    //实例化MessageQueue
    mQueue = new MessageQueue(quitAllowed);
    //Looper与当前线程绑定
    mThread = Thread.currentThread();
}
复制代码

ActivityThread.main()中,最后调用了Looper.loop()

public static void loop() {
    final Looper me = myLooper();//调用sThreadLocal.get(),从中取出Looper
    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.");
    }
    
    me.mInLoop = true;
    
    // 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);
    
    me.mSlowDeliveryDetected = false;
    
    for (;;) {
        //死循环,调用loopOnce方法
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}
复制代码

Looper.loop()中有一个死循环,调用Looper.loopOnce(),从MessageQueue中取消息。

private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        //调用MessageQueue的next()方法取消息
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            //消息为空,会退出消息队列
            // No message indicates that the message queue is quitting.
            return false;
        }

        // 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);
        }
        ...
        try {
            msg.target.dispatchMessage(msg);//msg.target是绑定的Handler
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } 
        ...
        msg.recycleUnchecked();

        return true;
    }
复制代码

3.3 Handler

创建Handler的时候调用Handler的构造函数。

public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}
复制代码

接着调用3个参数的构造方法。

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    //传入了一个Looper
    mLooper = looper;
    //从looper中获取一个MessageQueue
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
复制代码

Handler提供了很多发送消息的方法,常用的是sendMessage(Message msg)

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}
复制代码

sendMessage(Message msg)调用了sendMessageDelayed(Message msg, long delayMillis),不管是sendMessage(Message msg)还是sendEmptyMessage(int what),最后调用了sendMessageAtTime(Message msg, long uptimeMillis)

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
复制代码
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);
}
复制代码

接着调用了Handler.enqueueMessage()Handler.enqueueMessage()调用了MessageQueue.enqueueMessage()

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                               long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //调用MessageQueue.enqueueMessage()
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

3.4 MessageQueue

MessageQueueHandler中的核心类,这个类是所有消息的存储仓库,消息管理有两点,入库enqueueMessage()和出库next(),这两个方法是确保线程安全的关键节点。这里主要涉及线程同步消息屏障两大核心问题。

3.4.1 线程同步

MessageQueue.enqueueMessage()

synchronized是一个内置锁,也就是有系统去控制lock和unlock的时机。synchronized (this) 表示对所有调用MessageQueue对象的线程来说,都是互斥的。由上面源码分析可知,一个线程对应一个Looper,一个Looper只会创建一个MessageQueue,所以所有子线程向主线程发消息时,主线程一次都只会处理一条消息,其他的都处于等待状态,这样就保证了消息队列的有序性。

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;
}
复制代码

MessageQueue.next()

每次取消息都是从队头去取,但是还是要使用synchronized (this)去加锁,以保证MessageQueue.enqueueMessage()MessageQueue.next()是互斥的,这样消息队列的操作才能有序进行。

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.
                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;
    }
}
复制代码

3.4.2 消息机制的同步屏障

由上面分析可知,线程的消息都是放到同一个MessageQueue里面,取消息的时候是互斥的,而且只能从队列头部取消息,而添加消息是按照消息的执行的先后顺序进行的排序。假如同一个时间范围内的某个消息需要立刻执行的,那就要给紧急需要执行的消息开通一个绿色通道,这个绿色通道就是同步屏障的相念。

屏障的意思即为阻碍,顾名思义,同步屏障就是阻碍同步消息,只让异步消息通过。开启同步屏障调用的是MessageQueue.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;
        
        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            while (p != null && p.when <= when) {
                //如果开启同步屏障的时间不为0,且当前的同步消息里有时间小于同步屏障时间的
                prev = p;
                p = p.next;
            }
        }
        if (prev != null) { // invariant: p == prev.next
            //prev不为null,将消息按照时间顺序插入消息队列的合适位置
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}
复制代码

开启同步屏障之后,异步消息在MessageQueue.next()中如何处理?

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    //nextPollTimeoutMillis = -1 一直阻塞不会超时
    //nextPollTimeoutMillis = 0 不会阻塞,立刻返回
    //nextPollTimeoutMillis > 0 最长阻塞nextPollTimeoutMillis
    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) {
                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;
    }
}
复制代码

3.5 Message

Message可以直接new,但是更推荐使用Message.obtain()mHandler.obtainMessage(),因。mHandler.obtainMessage()调用了Message.obtain(this),最终都是调用的Message.obtain()Message.obtain()中检查了是否有可以复用的Message,避免过多的创建和销毁Message,达到了内存优化的目的。

public final Message obtainMessage()
{
    return Message.obtain(this);//调用Message.obtain(this)
}
复制代码
public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;//这里将Message和Handler绑定
    
    return m;
}
复制代码
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            //检查是否有可复用的Message
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    //没有可复用的Message,再创建新的
    return new Message();
}
复制代码

3.6 Message和Handler绑定

直接调用mHandler.obtainMessage()或者Message.obtain(handler)的时候,传入了一个Handler,使用m.target = h进行MessageHandler的绑定。

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;//将Message和传入的Handler进行绑定
    
    return m;
}
复制代码

如果是直接new Message(),则会在将消息插入队列的时候调用Handler.enqueueMessage()的时候进行绑定。

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                               long uptimeMillis) {
    msg.target = this;//将Message和当前Handler进行绑定
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

4 Handler相关问题

4.1 一个线程有几个Handler?

可以有无数个,Handler可以随便new

4.2 一个线程有几个Looper?

只有1个,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));
}
复制代码

4.3 Handler内存泄漏原因?

由于Java特性,内部类持有外部类的对象,而Handler是通过内部类的形式创建,所以会持有Activity,而Message又持有Handler,如果一条消息需要等待较长时间才能发送,而这时Activity却被销毁,就有可能发生内存泄漏。

而在RecyclerView中,Adapter持有ViewHolderViewHolder也是内部类,但是他们的生命周期一致,所以不会发生内存泄漏。

要解决改问题,可以将Handler定义成静态内部类,内部使用WeakReference持有Activity的弱引用,然后onDestroy()的时候即使移除所有回调和消息。

4.4 为何主线程可以new Handler()?如果想要在子线程new Handler()应该怎么做?

在上面源码分析可知,APP启动过程中,在ActivityThread.main()中已经调用了Looper.prepareMainLooper()Looper.loop()Activity中已经创建了Looper,所以主线程中可以直接new Handler()

想要在子线程new Handler(),有两种方式。

方式1:在子线程中添加Looper.prepare()Looper.loop()

new Thread(() -> {
    //方式1
    //实例化Handler之前加上 Looper.prepare()
    Looper.prepare();
    mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0) {
                Log.d(TAG, "handleMessage: 收到了消息");
            }
        }
    };
    mHandler.sendEmptyMessage(0);
    //最后加上Looper.loop()
    Looper.loop();
}).start());
复制代码

方式2:new Handler()的时候传入Looper.getMainLooper()

new Thread(() -> { 
    //方式2
    //使用有参构造方法Handler(Looper looper),传入Looper.getMainLooper()
    mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0) {
                Log.d(TAG, "handleMessage: 收到了消息");
            }
        }
    };
    mHandler.sendEmptyMessage(0);
}).start());
复制代码

4.5 子线程中维护的Looper,消息队列无消息的时候处理方案是什么?有什么用?

无消息的时候调用Looper.quit()Looper.quitSafely()

public void quit() {
    mQueue.quit(false);
}

public void quitSafely() {
    mQueue.quit(true);
}
复制代码

Looper.quit()Looper.quitSafely()调用MessageQueue.quit()

void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    
    synchronized (this) {
        if (mQuitting) {
            return;
        }
        //mQuitting赋值为true
        mQuitting = true;
        //移除所有消息
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }
        
        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);//调用native方法唤醒之前被阻塞的nativePollOnce(),继续执行MessageQueue.next()
    }
}
复制代码

然后在MessageQueue.next()中判断mQuitting为true,退出循环。

Message next() {
    ...
    for (;;) {
        ...
        nativePollOnce(ptr, nextPollTimeoutMillis);
        ...
        synchronized (this) {
            ...
            //mQuitting为true,退出循环
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
            ...
        }
       ...
    }
 }
复制代码

4.6 既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程),那它内部是如何让保证线程安全的?

使用内置锁synchronizedsynchronized (this) 表示对所有调用MessageQueue对象的线程来说,都是互斥的。一个线程对应一个Looper,一个Looper只会创建一个MessageQueue,所以所有子线程向主线程发消息时,主线程一次都只会处理一条消息,其他的都处于等待状态,这样就保证了消息队列的有序性。

4.7 如何创建Message?

3中方式,new Message()Message.obtain()mHandler.obtainMessage()。推荐使用后两种,mHandler.obtainMessage()调用了Message.obtain(this),最终都是调用的Message.obtain()Message.obtain()中检查了消息池中是否有可以复用的Message,避免过多的创建和销毁Message,达到了内存优化的目的。

4.8 Looper死循环为什么不会导致ANR?

ANR产生的原因有以下几种:

  • Service Timeout:比如前台服务在20s内未执行完成;
  • BroadcastQueue Timeout:比如前台广播在10s内未执行完成
  • ContentProvider Timeout:内容提供者,在publish过超时10s;
  • InputDispatching Timeout: 输入事件分发超时5s,包括按键和触摸事件。

所有的Activity和Service都是运行在Looper.loop()函数中,以消息的方式存在,所以在没有消息产生的时候,Looper会被block(阻塞),主线程会进入休眠,一旦有输入事件或者Looper添加消息的操作后主线程就会被唤醒,从而对事件进行响应,所以不会导致ANR。

4.9 MessageQueue会填满吗?

跟内存有关,内存多大,就能存多少消息。

关注木水小站 (zhangmushui.cn)和微信公众号【木水Code】,及时获取更多最新技术干货。

猜你喜欢

转载自juejin.im/post/7109048202099163167