欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android Handler消息机制实现原理

程序员文章站 2022-07-14 19:50:25
...

网上关于Handler实现机制的介绍很多,本文主要写自己关于Handler的一些理解

1.一些常见疑问解答

Q1:简述Handler消息机制的实现原理

A1:Handler对象在创建的时候,会有一个Looper初始化,初始化的同时会创建一个MessageQueue对象,并启动消息循环,当调用sendMessage方法的时候,会调用到MessageQueue的enqueueMessage方法将消息插入消息队列,在此方法里面,会调用nativeWake来唤醒Looper,取出队头消息,然后通过target.dispatchMessage方法来分发消息,最终在Handler.handleMessage方法里面处理消息

Q2:Handler发送消息需要哪些条件

A2:Handler必须有一个关联的Looper对象来实现消息循环,有2种获取Looper方法,一是使用当前Activity所在主线程的Looper;二是自己创建一个Looper对象,继承HandlerThread,此类内部有实现一个Looper对象

Q3:Looper对象怎么创建,主线程的Looper对象是什么时候创建的

A3:Looper提供了prepare和prepareMainLooper方法来创建Looper对象;主线程的Looper对象是在ActivityThread的main方法里面创建的

Looper.prepareMainLooper();
Looper.loop();

Q4:Looper消息是循环的,在等待的过程中,为什么没有阻塞主线程

A4:Looper消息循环其实是调用MessageQueue.next方法来实现的,在next方法里面,使用的一个native方法nativePollOnce来实现,利用linux epoll机制来实现

Q5:怎么创建一个Message对象

A5:Message没有提供构造方法,一般建议使用obtain方法来复用Message,用完后使用recycleUnchecked来回收,只是把对象里面的属性全部重置,对象未删除;内部有消息池,最大支持50个,超过50个则不再创建新的Message对象

2.关键类属性及方法接受

MessageQueue.java是串联Java层和Native层的关键,内部定义了几个native方法,用来和native层进行交互

2.1 Java层主要相关的类:

代码路径:

frameworks/base/core/java/android/os/Handler.java
frameworks/base/core/java/android/os/MessageQueue.java
frameworks/base/core/java/android/os/Looper.java
frameworks/base/core/java/android/os/Message.java
  • Handler 用来发送和执行Message和Runnable
    • Looper mLooper 关联的Looper实例
    • MessageQueue mQueue 关联的mQueue实例
    • Callback mCallback 回调CallBack
    • final boolean mAsynchronous 是否异步
  • MessageQueue 消息队列,消息不能直接添加, 需要通过Handler
    • long mPtr 给Native用的,代表Native层的MessageQueue
    • native static long nativeInit() 构造方法中给mPtr初始化
    • native void nativePollOnce 阻塞的获取一条消息
    • next() 获取队列中的下一条消息,在Looper对象中被调用
  • Looper 给一个线程执行消息循环,线程默认是没有相关联的Looper的
    • Looper sMainLooper 主线程的Looper
    • MessageQueue mQueue 关联的MessageQueue
    • Thread mThread Looper所处的线程
    • ThreadLocal sThreadLocal 用来保存和当前进程关联的Looper对象
    • prepare()和loop() 创建Looper对象的个步骤
    • public static void loop() 启动消息循环,在一个死循环中调用queue.next()方法来获取下一条消息
  • Message 代表一个消息
    • public int what 消息值
    • public Object obj 消息所带的对象
    • Handler target 消息被处理的Handler
    • Message sPool 消息池,最大支持50个,建议通过obtain()方法来复用,recycleUnchecked来回收,只是把对象里面的属性全部重置,对象未删除

2.2 Native层主要相关的类:

代码路径

framework/base/core/jni/android_os_MessageQueue.cpp

system/core/libutils/Looper.cpp
system/core/include/utils/Looper.h
system/core/libutils/RefBase.cpp

framework/base/native/android/looper.cpp
framework/native/include/android/looper.h

3.实现原理

3.1.Looper对象创建

Handler的创建很简单,重点看一下Looper对象的创建。对外提供2种创建的接口

  • prepare 一般自定义的Looper使用这个方法,可以退出循环
  • prepareMainLooper 给应用的主线程创建Looper
    // 私有构造方法,初始化mQueue和设置关联的mThread,只能通prepare方法创建对象
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    
    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");
        }
        // 每个线程只能创建一个Looper
        sThreadLocal.set(new Looper(quitAllowed));
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false); // mainlooper不能通出
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

对象实现比较简单,利用sThreadLocal来保存Looper对象,供线程内部使用。

对象创建完后,调用loop()方法进入循环,主要有4个动作

  1. 获取MessageQueue对象
  2. 获取下一条要处理的消息
  3. 将Message发送给对应的Handler来处理
  4. 回收消息
    public static void loop() {
        final Looper me = myLooper();
        // 1.获取MessageQueue对象
        final MessageQueue queue = me.mQueue;
        for (;;) {
            // 2.获取下一条要处理的消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            try {
                // 3.将Message发送给对应的Handler来处理
                msg.target.dispatchMessage(msg);
            } catch (Exception exception) {
            } finally {
            }
            // 4.回收消息
            msg.recycleUnchecked();
        }
    }

初始化的时候,MessageQueue是空的,会阻塞在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;
        }
        for (;;) {

            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.
                        // 找到next Message则返回
                        return msg;
                    }
                } else {

                }
            }
        }
    }

nativePollOnce最终会调用到Looper::pollOnce()方法,空闲时停留在epoll_wait()方法,用于等待事件发生或者超时

3.2 MessageQueue对象创建

看MessageQueue的构造方法

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

nativeInit方法是在android_os_MessageQueue.cpp文件里面实现的

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    //初始化native消息队列
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

在NativeMessageQueue方法里面,会创建一个Looper对象,此处Native层的Looper与Java层的Looper没有任何的关系,只是在Native层重实现了一套类似功能的逻辑。

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

    mLooper = Looper::getForThread(); 
    if (mLooper == NULL) {
        mLooper = new Looper(false); //创建native层的Looper 
        Looper::setForThread(mLooper); 
    }
}

在Looper的rebuildEpollLocked方法里面,会利用epoll机制来实现消息处理机制

void Looper::rebuildEpollLocked() {
    // Close old epoll instance if we have one.
    if (mEpollFd >= 0) {
        mEpollFd.reset();
    }

    // Allocate the new epoll instance and register the wake pipe.
    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
    strerror(errno));

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd.get();
    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
  
    for (size_t i = 0; i < mRequests.size(); i++) {
        const Request& request = mRequests.valueAt(i);
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);
        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
    }
}

epoll操作过程有3个方法,分别是epoll_create(), epoll_ctl(),epoll_wait()。

3.3 Handle.sendMessage

Handle.sendMessage通过层层调用,最终会调用到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);
    }
    boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {

            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 3.1 步的阻塞
                nativeWake(mPtr);
            }
        }
        return true;
    }

消息保存在链表里面,成功插入后,调用nativeWake方法来唤醒在3.1 步的阻塞queue.next()

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


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

void Looper::wake() {
    uint64_t inc = 1;
    //  // 向管道mWakeEventFd写入字符1
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
                             mWakeEventFd.get(), nWrite, strerror(errno));
        }
    }
}

其中TEMP_FAILURE_RETRY 是一个宏定义,当执行write失败后,会不断重复执行,直到执行成功为止。nativePollOnce从epoll_wait方法中退出,继续执行后面的,next方法返回一个Message对象

在3.1节Looper.loop()方法中获取到Message对象后,分发给对应的Handler

3.4 Handle.handleMessage

先看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);
        }
    }
  1. 如果有Message.callback非空,则会执行Runnable callback
  2. 如果mCallback非空,则调用mCallback.handleMessage
  3. 最后才调用handleMessage,走到我们复写的handleMessage方法

3.5 Message.recycleUncheck

此方法里面,只是重置了Message对象的相关属性,没有删除对象,保存消息池里面

消息池最大容量是50,超过则不再记录,obtain方法会消耗里面的资源

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