真的了解 Handler 源码吗

写在前面的话

要参加Android面试,Handler被问到的概率是非常大的,其实我心里一直有个疑问,Handler既然是做线程通信的,那么它是如何做到线程通信的,我一直很困惑,我很想知道,它底层逻辑到底是怎么写的,具体的代码到底在哪,网上很多人画了图,但看的还是茫茫然不知所云,感觉他们说了很多,但还是没法真正让我去认识到,到底是怎么穿数据的,比如说,我一个字符串,他们是要如何从线程A传给线程B的呢?谁来传,怎么传?MessageQueue?为了获得答案,才有了这片文章,里面有c++代码,也有汇编代码。

ActivityThread

ActivityThread是主线程,也称UI线程,在main中能看到Looper的代码。

[->/frameworks/base/core/java/android.app.ActivityThread.java]

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

先看看prepareMainLooper到底做了什么事情,代码如下#Looper

Looper

[->/frameworks/base/core/java/android.os.Looper.java]

    final MessageQueue mQueue;
    final Thread mThread;
    private static Looper sMainLooper;  // guarded by Looper.class
		static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

		public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

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

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

从上面代码可以看出,prepareMainLooper做的事情,就是将Looper对象存入ThreadLocal中,还赋值给sMainLooper。

这里涉及到ThreadLocal,为什么是用ThreadLocal来保存Looper而不是其他,还有,为什么要保存Looper呢?先了解下ThreadLocal是干嘛的?#ThreadLocal

ThreadLocal

[->/libcore/luni/src/main/java/java.lang.ThreadLocal.java]

看了下源码,在Android6.0中,ThreadLocal中用的是ThreadLocal.Values,而Android10上看到的是ThreadLocal.ThreadLocalMap。

在这里,我顺带拓展下线程的知识点,知道线程中会运行属于自己的动作和程序,线程间运行数据是互不打扰的。

那么到底什么是线程,线程又有哪些特性,是如何实现和保持这些特性的,而且,线程中ThreadLocal起到一个什么作用,用自己的语言来表达清楚,理解透彻。

Thread

因为我下载的Android6源码不全,所用直接复制了项目中Android10中的源码,来解析Thread

[->/libcore/luni/src/main/java/java.lang.Thread.java]

    @FastNative
    public static native Thread currentThread();

    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

		private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        // Android-removed: SecurityManager stubbed out on Android
        // SecurityManager security = System.getSecurityManager();
        if (g == null) {
           g = parent.getThreadGroup();
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        
        this.target = target;
        init2(parent);

        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

    // Android-added: Helper method for previous constructor and init(...) method.
    private void init2(Thread parent) {
        this.contextClassLoader = parent.getContextClassLoader();
        this.inheritedAccessControlContext = AccessController.getContext();
        if (parent.inheritableThreadLocals != null) {
            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
                    parent.inheritableThreadLocals);
        }
    }

从这里可以看出,当我们创建一个新线程的时候,都是从当前进程(即父线程)中拷贝内容给子线程的。比如是否是守护进程(daemon),优先级(priority),类加载器(ClassLoader),但ThreadLocal.ThreadLocalMap是需要重新创建的。

ThreadLocal.ThreadLocalMap

		static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
    static class ThreadLocalMap {
        private Entry[] table;

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
      
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;// 这里将父线程的Entry对象全部拿到
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }   
      
        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }      
    }

从这里能得出的结论是,每个线程都有自己的ThreadLocal.ThreadLocalMap,而每个ThreadLocalMap都维护着Entry[]数组,这个数组中保存的是父类和子类中所有的Entry对象,这是个弱应用对象,弱应用对象会在下一次GC前被回收。

总结

这里做个小结!所有的子线程都是通过父线程拷贝过来的,拷贝了是否是守护线程,优先级,类加载器等,所有的线程中都维护着一个叫ThreadLocal.ThreadLocalMap的对象,这个对象包含了所有父线程和自己线程的ThreadLocalMap私有数据。

有了这些知识之后,继续回来看#Looper->sThreadLocal.set(new Looper(quitAllowed));。

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals; // threadLocals是在Thread中定义的ThreadLocalMap
    }

   void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    static class ThreadLocalMap {

        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) { // 如果当前ThreadLocal存着,则更新
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
    }

通过上面代码可以知道,ThreadLocal的set(),做了一件事,就是将key,value塞进Entry[]数组中。

所以,再次总结下:

在UI线程中,一开始,我们做的事情就是,将Looper存入到UI线程的ThreadLocal.ThreadLocalMap中。

Looper

创建Looper

上面需要补充一点,当在new Looper()的时候,MessageQueue也被创建出来了。具体代码如下:

[->/frameworks/base/core/java/android.os.MessageQueue.java]
		private native static long nativeInit();
		MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }    

可以看到这里调用了本地方法nativeInit();继续去看这个方法干了什么事。

[->/frameworks/base/core/jni/android_os_MessageQueue.cpp]
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}
static JNINativeMethod gMessageQueueMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
    ...
};

这里最终调用的是NativeMessageQueue对象的incStrong方法。如何找到incStrong的实现方法呢?

  1. 首先根据android_os_MessageQueue.cpp中的引用android_os_MessageQueue.h,找到MessageQueue.h
[->/frameworks/base/core/jni/android_os_MessageQueue.cpp]
#include "android_os_MessageQueue.h"
class NativeMessageQueue : public MessageQueue, public LooperCallback {
  ...
}
[->/frameworks/base/core/jni/android_os_MessageQueue.h]
#include <utils/Looper.h>
class MessageQueue : public virtual RefBase {
}
  1. 这里引出了RefBase和utils/Looper.h,没看到RefBase的直接引用,说明RefBase可能来自于Looper.h,为了证实这个问题,继续找Looper.h的代码
[->/system/core/include/utils/Looper.h]
#include <utils/RefBase.h>
class Looper : public RefBase {
}
// 这里找到了utils/RefBase.h,以及对incStrong的申明
[->/system/core/include/utils/RefBase.h]
class RefBase
{
public:
            void            incStrong(const void* id) const;
            void            decStrong(const void* id) const;
    
            void            forceIncStrong(const void* id) const;

            //! DEBUGGING ONLY: Get current strong ref count.
            int32_t         getStrongCount() const;
  ......
}
  1. 有了RefBase.h中的申明,那么再搜索看下哪里有没有RefBase.cpp,看是否有做incStrong的实现。找到了代码如下:
[->/system/core/libutils/RefBase.cpp]
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    void addStrongRef(const void* /*id*/) { }
    void removeStrongRef(const void* /*id*/) { }
    void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { }
    void addWeakRef(const void* /*id*/) { }
    void removeWeakRef(const void* /*id*/) { }
};  
void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
    ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    refs->mBase->onFirstRef();
}

RefBase::incStrong表示这个方法是RefBase中的incStrong。代码看到了这里,似乎已经挺底层了,但还是很懵,这到底干了什么?

再往深看下代码:

// refs->incWeak(id);的实现
void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id); // 这个是关键干的事
    const int32_t c __unused = android_atomic_inc(&impl->mWeak);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

......
    void addWeakRef(const void* id) {
        addRef(&mWeakRefs, id, mWeak);
    }
    void addStrongRef(const void* id) {
        //ALOGD_IF(mTrackEnabled,
        //        "addStrongRef: RefBase=%p, id=%p", mBase, id);
        addRef(&mStrongRefs, id, mStrong);
    }
......
private:
    struct ref_entry
    {
        ref_entry* next;
        const void* id;
#if DEBUG_REFS_CALLSTACK_ENABLED
        CallStack stack;
#endif
        int32_t ref;
    };

    void addRef(ref_entry** refs, const void* id, int32_t mRef)
    {
        if (mTrackEnabled) {
            AutoMutex _l(mMutex);

            ref_entry* ref = new ref_entry;
            // Reference count at the time of the snapshot, but before the
            // update.  Positive value means we increment, negative--we
            // decrement the reference count.
            ref->ref = mRef;
            ref->id = id;
#if DEBUG_REFS_CALLSTACK_ENABLED
            ref->stack.update(2);
#endif
            ref->next = *refs;
            *refs = ref;
        }
    }

    ref_entry* mStrongRefs;
    ref_entry* mWeakRefs;

可以看到refs->incWeak(id);和refs->addStrongRef(id);干的事情是一样的,都是在ref_entry链表前加入一条数据。

总结一下:

当创建MessageQueue对象时,会执行本地方法nativeInit();本质上是创建了一个ref_entry的链表。

这里补充一个c++的语法:

class RefBase::weakref_impl : public RefBase::weakref_type { // :: 有点类似java中的"."
  public: // 表示之后的方法都是public类型的
};
// : 有点类似java中的继承
class 派生类名 : 继承方式 基类名{
  派生类的成员
};
looper()

继续回到#Looper->looper();这个方法是等待消息进入消息队列,并取出来。先看看它是如何取的,我们知道,在UI线程中,就有looper()的执行,也就是说,UI线程在一开始就会等待消息进来。接下来去寻找,UI线程是从哪里取出消息内容,怎么取,又发送到哪里去,谁消费掉了这个消息。

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper(); // 获取当前线程中ThreadLocal.ThreadLocalMap.Entry[t].value,即Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;// 当前线程中Looper对象下的MessageQueue

        // 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();

        for (;;) {// 无限循环
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

根据me.mQueue;可以知道,UI线程取的MessageQueue是它自身的消息队列,也就是说,并不是子线程的消息队列。问题来了,这个消息队列与子消息队列是否一致呢?

Message msg = queue.next();具体实现如下:

[->/frameworks/base/core/java/android.os.MessageQueue.java]

    Message next() {
        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);
            ......
        }
    }

这里精简了代码,主要是nativePollOnce(ptr, nextPollTimeoutMillis);继续查看它的实现

[->/frameworks/base/core/jni/android_os_MessageQueue.cpp]

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    mLooper->pollOnce(timeoutMillis);// 这句是核心代码
    mPollObj = NULL;
    mPollEnv = NULL;

    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

所以,本质上,pollOnce调用的是C++中Looper中的pollOnce方法。

[->/system/core/libutils/Looper.cpp]

#include <utils/Looper.h>
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        ......
        result = pollInner(timeoutMillis);// 这里是实现的方法
    }
}

int Looper::pollInner(int timeoutMillis) {
		......
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); // 这里取出数据
  	......

    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
Done: ;
		......
      // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd, response.request.seq); // 这里还有清除工作,也就是说,取出数据之后是要删掉的
            }
            response.request.callback.clear();
            result = POLL_CALLBACK;
        }
    }
    return result;
}

可以看到这里有epoll_wait(),那如何找到这个方法的实现呢?Looper.cpp中导入了Looper.h,而Looper.h中又导入了 sys/epoll.h。epoll_wait就是在epoll.h中实现的。

epoll.h的路径 [->/bionic/libc/include/sys/epoll.h]

Looper.h的路径 [->/system/core/include/utils/Looper.h]

而epoll.h中定义的epoll_wait的实现又是在epoll_wait.cpp中,代码如下:

[->/bionic/libc/bionic/epoll_wait.cpp]

#include <sys/epoll.h>

int epoll_wait(int fd, struct epoll_event* events, int max_events, int timeout) {
  return epoll_pwait(fd, events, max_events, timeout, NULL);
}

同目录下epoll_pwait.cpp,代码实现如下:

#include <sys/epoll.h>

#include "private/kernel_sigset_t.h"

extern "C" int __epoll_pwait(int, epoll_event*, int, int, const kernel_sigset_t*, size_t);

int epoll_pwait(int fd, epoll_event* events, int max_events, int timeout, const sigset_t* ss) {
  kernel_sigset_t kernel_ss;
  kernel_sigset_t* kernel_ss_ptr = NULL;
  if (ss != NULL) {
    kernel_ss.set(ss);
    kernel_ss_ptr = &kernel_ss;
  }
  return __epoll_pwait(fd, events, max_events, timeout, kernel_ss_ptr, sizeof(kernel_ss));
}

所以,最后的实现是在__epoll_pwait。通过全局搜索发现,该方法存在.S文件中,说明是汇编语言,不同的CPU有不同的实现。比如在x86中,代码如下:

[->/bionic/libc/arch-x86/syscalls/__epoll_pwait.S]

ENTRY(__epoll_pwait)
    pushl   %ebx # 这里可能是第一个入参
    .cfi_def_cfa_offset 8
    .cfi_rel_offset ebx, 0
    pushl   %ecx # 这个是events入参 ecx是寄存器的名字
    .cfi_adjust_cfa_offset 4
    .cfi_rel_offset ecx, 0
    pushl   %edx
    .cfi_adjust_cfa_offset 4
    .cfi_rel_offset edx, 0
    pushl   %esi
    .cfi_adjust_cfa_offset 4
    .cfi_rel_offset esi, 0
    pushl   %edi
    .cfi_adjust_cfa_offset 4
    .cfi_rel_offset edi, 0
    pushl   %ebp
    .cfi_adjust_cfa_offset 4
    .cfi_rel_offset ebp, 0
    mov     28(%esp), %ebx
    mov     32(%esp), %ecx # 将esp寄存器32位置开始上的数据移动到ecx寄存器中
    mov     36(%esp), %edx # 将esp寄存器34位置开始的数据移动到edx寄存器中
    mov     40(%esp), %esi
    mov     44(%esp), %edi
    mov     48(%esp), %ebp
    movl    $__NR_epoll_pwait, %eax
    int     $0x80
    cmpl    $-MAX_ERRNO, %eax
    jb      1f
    negl    %eax
    pushl   %eax
    call    __set_errno_internal
    addl    $4, %esp // 将esp寄存器中的数据和$4中的数据相加,并放入$eax寄存器中
1:
    popl    %ebp
    popl    %edi
    popl    %esi
    popl    %edx
    popl    %ecx
    popl    %ebx
    ret
END(__epoll_pwait)

从mov 32(%esp), %ecx中可以看到,esp寄存器从32位开始,里面存储的是epoll_event* events对象的指针,而这句话就是将指针mov移出道ecx寄存器出。所以,这里其实完成的就是一个poll过程。所以,可以得出结论,pollOnce最终是通过汇编,对寄存器中的数据进行移动取出的过程。注意观察这句:call __set_errno_internal

这里是需要调用c中的__set_errno_internal方法的。也就是说,对内存中的数据一顿处理之后,是通过这个方法将数据返回到c语言中的。

[->/bionic/libc/bionic/__set_errno.cpp]

extern "C" __LIBC_HIDDEN__ long __set_errno_internal(int n) {
  errno = n;
  return -1;
}

// This one exists for the LP32 NDK and is not present at all in LP64.
#if !defined(__LP64__)
extern "C" long __set_errno(int n) {
  return __set_errno_internal(n);
}
#endif

这几步,我其实没怎么看懂,迷迷糊糊的,有知识盲区在。大致猜测意思是,汇编结束后,会有int类型的n返回,并且赋值给了errno。到这里似乎就断层了,不知道这个errno是给了谁,又在哪被使用了。当然还有一种可能,在__epoll_pwait.S中,已经对内存的数据进行了操作,后面要用直接取出来就行,这样就实现了数据的交互,说白了就是内存中的数据写和读的过程,只有等我能看懂汇编,或许能知道答案。

介绍完Looper取数据的过程,其实就是对寄存器中的位置进行移动的过程,当然这个过程最终的表现看到是在内存中的数据读取和删除。那接下来,需要去看看,数据是如何写入进去的。

Handler

使用

先看下面这段使用代码

public class MyHandler<T> extends Handler {

    private WeakReference<T> mWeakReference;

    public MyHandler(T t) {
        this.mWeakReference = new WeakReference(t);
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        T t = mWeakReference.get();
        if(t instanceof Activity) {
            //执行业务逻辑
            Toast.makeText((Activity)t,"handleMessage",Toast.LENGTH_SHORT).show();
        }
    }
}

public class LoginActivity extends AppCompatActivity{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        MyHandler myHandler = new MyHandler(this);

        new Thread(new Runnable() {
            @Override
            public void run() {
                //使用 handler 发送消息
                myHandler.sendMessage(myHandler.obtainMessage(1));
            }
        }).start();  
    }
}
  1. 在Activity中,如果直接通过new Handler(); 来定义Handler,那么如果Activity退出了,但这个时候,Handler还在执行,那么会出现内存泄露的现象。解决办法就是在使用弱引用来处理,在GC回收内存的时候,会查找WeakReference引用的对象是否还在被使用,没有的话就会收掉。还能达到优化内存的作用。

  2. obtainMessage方法。在第一次Message创建之后,然后loop()取出消息之后,执行“msg.recycleUnchecked();”方法,

        private static final int MAX_POOL_SIZE = 50;
    
    		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 = -1;
            when = 0;
            target = null;
            callback = null;
            data = null;
    
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this; // 给sPool赋值
                    sPoolSize++;
                }
            }
        }
    

    这里就是给sPool进行复制,也就是说,之后再次调用Message中的obtain()方法,就可以直接在Message中的next链表中直接插入数据,这样可以避免对象的再次创建,可以使用50次,也就是说,这个链表的长度是50。这里也是内存优化的一个点。

上面代码知道了,Handler中内存优化的两个地方,接下来看Handler是如何做到将消息放入MessageQueue中的。

[->/frameworks/base/core/java/android.os.Handler.java]

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

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

这段代码描述了,Handler发送消息,最终是回到了MessageQueue中的enqueueMessage方法。这里的Handler是在UI线程中创建的,所以,这里的MessageQueue对象也应该是UI线程中,即在ActivityThread->main()的时候,创建Looper的时候就一起被创建了。当然Handler也可以是子线程创建,但是在子线程中创建需要注意,看代码

[->Handler.java]
    public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper(); // 也就是说,这里获取到的Looper是所属线程自身所拥有的对象
        ...
    }

[->Looper.java]
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

如果我们需要用到这里的Looper,那么,必需在new Handler之间,先Looper.prepare()来创建所属线程的Looper对象。UI线程中之所以不用这样干,是因为在ActivityThread中已经做了。

发送消息

[->/frameworks/base/core/java/android.os.MessageQueue.java]

    boolean enqueueMessage(Message msg, long when) {
        ......
        synchronized (this) {
            ......
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) { // 第一次进入的时候,mMessage是null
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // 在next()中mBlocked会变成true
            } 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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr); // 这个是写入数据的主要代码
            }
        }
        return true;
    }

上面很多是对java层的代码做了操作,并没有真正意义上的处理内存。主要nativeWake(mPtr)起了作用,“mPtr = nativeInit();”这其实就是在Looper创建的时候产生的,里面是会在C++里生成一个ref_entry链表,这里就要使用它。

c++层的调用
android_os_MessageQueue.cpp

[->/frameworks/base/core/jni/android_os_MessageQueue.cpp]

void NativeMessageQueue::wake() {
    mLooper->wake();
}
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}
static JNINativeMethod gMessageQueueMethods[] = {
    ......
    { "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
    ......
};

本质上,还是调用了Looper.cpp中的wake()方法。

Looper.cpp

[->/system/core/libutils/Looper.cpp]

#include <unistd.h>
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
#endif

    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));// 这里write了
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

注意这里的write(mWakeEventFd, &inc, sizeof(uint64_t)。猜测这里应该是写入的过程,继续找源码实现。

找到在unistd.h中有write的定义

[->/bionic/libc/include/unistd.h]

extern ssize_t write(int, const void *, size_t);

这个函数是unix底层函数,write函数是一个系统调用,用于将数据从指定的文件描述符写入到文件中。它的函数原型如下所示:

extern ssize_t write(int fd, const void *buf, size_t count);

fd是文件描述符,buf是要写入的数据的缓冲区,count是要写入的字节数。该函数返回实际写入的字节数,如果出现错误,则返回-1。

总结下思路,这里我们看到了unix函数write知道了是有数据写入到文件中的,看到这里其实大致差不多可以结束了,再深究下去,估计就是Linux内核中对文件的写入的操作了,涉及到的知识点应该挺庞大的,我也不打算深究下去。

到这里其实就基本结束了,最后,Looper.loop()方法会去回调Handler中的dispatchMessage,将Message传回去。

[->Handler.java]
		public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

这里就是Java中的代码了,如果Message中有callback就调用Message中的,否则调用Handler中callback的,如果都没有,那就调Handler自身的handleMessage方法。

所以,发送消息,本质上是调用Linux内核的write方法,写到文件中,但读的时候,是通过寄存器,读取内存中的消息,这点也很好理解,写的消息是很多的,如果写了之后立马读,那肯定会响应不过来,所以写可以慢一点,读就可以快一点。

不错的地址:

  1. [Android——Handler详解][https://blog.csdn.net/ly0724ok/article/details/117324053/]:这个博客里总结了一些面试题的问答,挺好的,可以看看。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值