Handler面试知识点看这篇就够了


  • Java 中的实现
    • ThreadLocal
    • MessageQueue
      • 异步消息和消息屏障
        • MessageQueue.postSyncBarrier
        • 消息屏障的消费
        • 异步消息的作用
    • Looper
    • Handler
    • MessageQueue.IdleHandler
  • Native的实现
    • MessageQueue
    • Looper#创建epoll事件
    • epoll
    • nativePollOnce
    • nativeWake
    • postDelay的实现
  • HandlerThread
  • IntentService
  • 参考和问题

大纲

强烈建议看一下Gityuan的文章

  • Handler
  • Looper
  • ThreadLocal
  • MessageQueue
  • HandlerThread
  • IntentService
  • MessageQueue.IdleHandler
    • 《你知道android的MessageQueue.IdleHandler吗?》

简而言之,就是在looper里面的message暂时处理完了,这个时候会回调这个接口,返回false,那么就会移除它,返回true就会在下次message处理完了的时候继续回调

/**
  * Callback interface for discovering when a thread is going to block
  * waiting for more messages.
*/
public static interface IdleHandler {
    /**
    * Called when the message queue has run out of messages and will now
    * wait for more.  Return true to keep your idle handler active, false
    * to have it removed.  This may be called if there are still messages
    * pending in the queue, but they are all scheduled to be dispatched
    * after the current time.
    */
    boolean queueIdle();
}

Java 中的实现

涉及到的4个类:

  • Handler
  • Looper
  • ThreadLocal
  • MessageQueue

11.webp

ThreadLocal

每个线程有一些和自己相关的变量,ThreadLocal的作用就是保存这些变量的。所有的变量是通过内部的静态类Value存储的。虽然,线程都是通过访问相同的ThreadLocal,但是每个线程保存的变量是分开的:

public void set(T value) {    
      Thread currentThread = Thread.currentThread();    
      Values values = values(currentThread);    
      if (values == null) {        
            values = initializeValues(currentThread);    }    
      values.put(this, value);
}

上面的set方法中,values方法返回当前线程的localValues成员变量:

/** 
  * Gets Values instance for this thread and variable type. 
  */
Values values(Thread current) {    
      return current.localValues;
}

那么线程内部的localValues是什么?

public class Thread implements Runnable {
    /** 
      * Normal thread local values. 
      */
    ThreadLocal.Values localValues;
    /*省略若干代码*/
}

可以看到,Thread中的localValues是定义在ThreadLocal中线程本地的变量。如果在 values ()方法取得null值,就执行initializeValues方法。 initializeValues是如何实现的呢?

Values initializeValues(Thread current) {    
      return current.localValues = new Values();
}

然后将value的值放在当前线程的的localValues里。这样,虽然看起来访问的是用一个ThreadLocal,但是得到的值却是根据线程而不同的。

注:不同sdk中ThreadLocal内部的实现时不一样的,比如在6.0的版本实现的方式就不是上面的方式,但是原理还是一样的

举个例子

public class JsonTestMetaData {
    public String json_str;
}

public class MainActivity extends Activity {

    ThreadLocal local = new ThreadLocal<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
       
        JsonTestMetaData data = new JsonTestMetaData();
        data.json_str = "main_thread";
        local.set(data);

        Log.e(TAG, local.get().json_str);

        new Thread(new Runnable() {
            @Override
            public void run() {
                JsonTestMetaData data = new JsonTestMetaData();
                data.json_str = "other_thread";
                local.set(data);
                Log.e(TAG, local.get().json_str);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                if (local.get() != null) {
                    Log.e(TAG, local.get().json_str);
                } else {
                    Log.e(TAG, "local.get() is null");

                }
            }
        }).start();


        Log.e(TAG, local.get().json_str);

    }
}

得到的结果:

01-09 14:28:36.410 29303-29303/com.xx.app.javabcsxtest E/MainActivity: main_thread
01-09 14:28:36.412 29303-29303/com.xx.app.javabcsxtest E/MainActivity: main_thread
01-09 14:28:36.412 29303-29331/com.xx.app.javabcsxtest E/MainActivity: other_thread
01-09 14:28:36.413 29303-29332/com.xx.app.javabcsxtest E/MainActivity: local.get() is null

TheadLocal可能会引起内存泄漏,假如Thread没有退出的话。

MessageQueue

MessageQueue是一个消息队列,包含成员变量Message mMessages;,可以理解成链表的头部。存储的形式不是队列,而是单链表。 内部包含5个native方法:

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native static void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsIdling(long ptr);

底层还是通过native代码完成的。后面的部分有展开。

在Java层面,主要是

  • next方法,获得下一个消息;
  • enqueueMessage 将消息插入到队列中。

异步消息和消息屏障

在ViewRootImpl的scheduleTraversals方法发现这样一段代码

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

发现使用MessageQueue添加了一个消息屏障

MessageQueue.postSyncBarrier

代码比较简单,只是向队列中插入一个Msg,when是SystemClock.uptimeMillis(),并且这个Msg没有target

    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

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

消息屏障的消费

MessageQueue的next方法,如果遇到消息屏障,就去寻找链表后第一个异步消息,优先处理异步消息

Message next() {
    nativePollOnce(ptr, nextPollTimeoutMillis);
    synchronized (this) {
        Message prevMsg = null;
        Message msg = mMessages;

        
        if (msg != null && msg.target == null) {
            do {
                prevMsg = msg;
                msg = msg.next;
            } while (msg != null && !msg.isAsynchronous()); 
        }
        if (msg != null) {
            if (now < msg.when) {//异步消息还没到处理时间,就在等会(超时时间)
                nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
            } else {
                //异步消息到了处理时间,就从链表移除,返回它。
                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 {
            nextPollTimeoutMillis = -1;
        }
        //...
    }
}

异步消息的作用

可以去看一下Message的setAsynchronous方法的注解

在View的invalidate重绘的过程,会引入同步屏障,其实就是保证异步Message优先处理,相当于插队呗。?

Certain operations, such as view invalidation, may introduce synchronization barriers into the {@link Looper}'s message queue to prevent subsequent messages from being delivered until some condition is met. In the case of view invalidation, messages which are posted after a call to {@link android.view.View#invalidate} are suspended by means of a synchronization barrier until the next frame is ready to be drawn. The synchronization barrier ensures that the invalidation request is completely handled before resuming.

Asynchronous messages are exempt from synchronization barriers. They typically represent interrupts, input events, and other signals that must be handled independently even while other work has been suspended.

Looper

Looper是做什么的?Looper的职能是为一个线程创建MessageQueue,绑定到这个线程,为此线程执行消息循环。

Looper内部包含MessageQueue和Thread的引用

MessageQueue在prepare方法中创建,在loop方法开始循环。

Java层的Looper和MessageQueue有在C++对应的类,分别是Looper(Native)和NativeMessageQueue类

线程默认是没有looper的,除非你在线程调用prepare方法,然后才能执行loop方法才能进行消息处理。

prepare做了什么呢?

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

你可以看到prepare()方法只能调用一次。在最后会创建一个Looper放在ThreadLocal里保存。 Looper是如何创建的呢?

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

可以看到,构造方法是私有的,新创建了一个MessageQueue,mThread就是当前线程。 那么Looper是如何执行消息循环的?

public static void loop() {    
        /*中间省略若干代码*/
        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;    
        for (;;) {        
              Message msg = queue.next();       
              msg.target.dispatchMessage(msg);     
              msg.recycleUnchecked();    
        }
}

可以看到,通过一个无限循环,不停的在消息队列中拿消息,将消息分发到指定的地方。

Message的target其实就是Handler

所以,在你写的线程中,可以这样使用:

class LooperThread extends Thread {     
      public Handler mHandler;      
      public void run() {          
            Looper.prepare();
            mHandler = new Handler() {
                  public void handleMessage(Message msg) {
                       // process incoming messages here
                  }
            };
            Looper.loop();
     }
}

不过上述的方式太low,在代码中也不方便,可以这样写:

HandlerThread

HandlerThread thread  = new HandlerThread("new_handler_thread");
Handler handler = new Handler(thread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
});

HandlerThread继承自Thread,自身会创建一个Looper。

关于 HandlerThread可参考 Android HandlerThread 完全解析

大多数和消息循环的交互都是通过Handler去完成的,就像你在Handler那部分看到的那样。记得在你不再执行消息循环的时候调用Looperquit方法。

Handler

Handler能够发送和处理和MessageQueue关联的 MessageRunnable。每个Handler和一个单独的线程关联,这个线程就是你创建这个Handler的时候所在的线程,需要处理的MessageQueue也是这个线程的MessageQueue

请看:

public Handler(Callback callback, boolean async) {    
    /*省略若干代码*/
    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有很多个构造函数,最终都会调用到这个构造函数。你可以看到Handler中的的Looper成员,就是通过Looper的静态方法myLooper得到的,myLooper是干啥的?你可以看Looper的内容,在上面代码中得到了一个和这个线程关联的Looper。如果 mLooper成员是null,那么就抛出异常。你可能会问,我在activity中随便创建handler啊,没有调用Looper.myLooper()方法。那是因为当你的应用运行的时候,Android已经通过Looper的静态方法prepareMainLooper创建了,这个方法只能执行一次,否则就会抛出异常了。这个Looper适合线程绑定的,你再看看mQueue,是从mLooper中拿到的。

调用的顺序如下:

  • Message的callback是否是null?不是调用callback的run方法(其实这里Message的callback是一个Runnable对象,通过Handler的post方法传递)
  • Handler的callback是否是null?不是调用callback。这里的callback的类型是Handler.Callback()
  • 最终调用Handler的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);
        }
    }

MessageQueue.IdleHandler

  • 《你知道android的MessageQueue.IdleHandler吗?》

简而言之,就是在looper里面的message暂时处理完了,这个时候会回调这个接口,返回false,那么就会移除它,返回true就会在下次message处理完了的时候继续回调

Native的实现

这里可以参考Gityuan - Android消息机制2-Handler(Native层)

22.webp

MessageQueue

MessageQueue.java


MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();  //mPtr记录native消息队列的信息 【2】
}

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

可以看到java层的mPtr实际上是native层地址指针,关于reinterpret_cast

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    //初始化native消息队列 【3】
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    nativeMessageQueue->incStrong(env); //增加引用计数
    return reinterpret_cast(nativeMessageQueue);
}

和Java不一样的是,native的MessageQueue内部有一个looper

NativeMessageQueue::NativeMessageQueue()
            : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {

    mLooper = Looper::getForThread(); //获取TLS中的Looper对象
    if (mLooper == NULL) {
        mLooper = new Looper(false); //创建native层的Looper 【4】
        Looper::setForThread(mLooper); //保存native层的Looper到TLS
    }
}

  • nativeInit()方法:
    • 创建了NativeMessageQueue对象,增加其引用计数,并将NativeMessageQueue指针mPtr保存在Java层的MessageQueue
    • 创建了Native Looper对象
    • 调用epoll的epoll_create()/epoll_ctl()来完成对mWakeEventFd和mRequests的可读事件监听
  • nativeDestroy()方法
    • 调用RefBase::decStrong()来减少对象的引用计数
    • 当引用计数为0时,则删除NativeMessageQueue对象
  • nativePollOnce()方法
    • 调用Looper::pollOnce()来完成,空闲时停留在epoll_wait()方法,用于等待事件发生或者超时
  • nativeWake()方法
    • 调用Looper::wake()来完成,向管道mWakeEventfd写入字符;

Looper#创建epoll事件

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    mWakeEventFd = eventfd(0, EFD_NONBLOCK); //构造唤醒事件的fd
    AutoMutex _l(mLock);
    rebuildEpollLocked();  //重建Epoll事件【5】
}

eventfd

void Looper::rebuildEpollLocked() {
    if (mEpollFd >= 0) {
        close(mEpollFd); //关闭旧的epoll实例
    }
    mEpollFd = epoll_create(EPOLL_SIZE_HINT); //创建新的epoll实例,并注册wake管道
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); //把未使用的数据区域进行置0操作
    eventItem.events = EPOLLIN; //可读事件
    eventItem.data.fd = mWakeEventFd;
    //将唤醒事件(mWakeEventFd)添加到epoll实例(mEpollFd)
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);

    for (size_t i = 0; i < mRequests.size(); i++) {
        const Request& request = mRequests.valueAt(i);
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);
        //将request队列的事件,分别添加到epoll实例
        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
    }
}

epoll

Gityuan - select/poll/epoll对比分析

监视的描述符数量不受限制,所支持的FD上限是最大可以打开文件的数目,具体数目可以cat /proc/sys/fs/file-max查看,一般来说这个数目和系统内存关系很大,以3G的手机来说这个值为20-30万。

IO性能不会随着监视fd的数量增长而下降。epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的,只有就绪的fd才会执行回调函数。

nativePollOnce

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    for (;;) {
        ...
        nativePollOnce(ptr, nextPollTimeoutMillis); //阻塞操作 【2】
        ...
    }

android_os_MessageQueue.cpp

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) {
    //将Java层传递下来的mPtr转换为nativeMessageQueue
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis); 【3】
}

android_os_MessageQueue.cpp

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    mLooper->pollOnce(timeoutMillis); 【4】
    mPollObj = NULL;
    mPollEnv = NULL;
    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}

Looper.h

inline int pollOnce(int timeoutMillis) {
    return pollOnce(timeoutMillis, NULL, NULL, NULL); 【5】
}

Looper.cpp

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        // 先处理没有Callback方法的 Response事件
        while (mResponseIndex < mResponses.size()) {
            const Response& response = mResponses.itemAt(mResponseIndex++);
            int ident = response.request.ident;
            if (ident >= 0) { //ident大于0,则表示没有callback, 因为POLL_CALLBACK = -2,
                int fd = response.request.fd;
                int events = response.events;
                void* data = response.request.data;
                if (outFd != NULL) *outFd = fd;
                if (outEvents != NULL) *outEvents = events;
                if (outData != NULL) *outData = data;
                return ident;
            }
        }
        if (result != 0) {
            if (outFd != NULL) *outFd = 0;
            if (outEvents != NULL) *outEvents = 0;
            if (outData != NULL) *outData = NULL;
            return result;
        }
        // 再处理内部轮询
        result = pollInner(timeoutMillis); 【6】
    }
}

nativeWake

调用Looper::wake()来完成,向管道mWakeEventfd写入字符;(epoll的唤醒事件)

boolean enqueueMessage(Message msg, long when) {
    ... //将Message按时间顺序插入MessageQueue
    if (needWake) {
            nativeWake(mPtr); 【2】
        }
}

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
    nativeMessageQueue->wake(); 【3】
}

void NativeMessageQueue::wake() {
    mLooper->wake();  【4】
}

void Looper::wake() {
    uint64_t inc = 1;
    // 向管道mWakeEventFd写入字符1
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

postDelay的实现

  • 1.调用sendMessageAtTime,计算当前时间和delay时间,调用MessageQueue的enqueMessage入队
  • 2.MessageQueue的enqueueMessage会遍历队列,根据Message的when计算插入到队列的位置,如果next的死循环处于阻塞,就会调用native方法唤醒NativeMessageQueue
  • 3.nativePollOnce会将时间传递给Looper.cpp的方法,实际上是epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); 也就是epoll的阻塞时间。
public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

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

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

HandlerThread

继承自Thread,run的时候会调用Looper.prepare();有两个比较重要的方法

public Looper getLooper()

public Handler getThreadHandler()

使用完成后需要调用quit()

IntentService

onCreate()会创建HandlerThread并start,创建刚才的HandlerThread的Looper的ServiceHandler Overide的onHandleIntent方法是在异步线程中执行

相关视频推荐:

【Android handle面试】Handler中的Message如何创建?
【Android handle面试】MessageQueue如何保持各线程添加消息的线程安全?
Android(安卓)开发零基础从入门到精通教程:Studio安装/UI/Fragment/四大组件/流行框架/项目发布与管理/项目实战

本文转自 https://juejin.cn/post/6984960343130767373,如有侵权,请联系删除。

相关