写在前面的话
要参加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的实现方法呢?
- 首先根据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 {
}
- 这里引出了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;
......
}
- 有了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();
}
}
-
在Activity中,如果直接通过new Handler(); 来定义Handler,那么如果Activity退出了,但这个时候,Handler还在执行,那么会出现内存泄露的现象。解决办法就是在使用弱引用来处理,在GC回收内存的时候,会查找WeakReference引用的对象是否还在被使用,没有的话就会收掉。还能达到优化内存的作用。
-
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方法,写到文件中,但读的时候,是通过寄存器,读取内存中的消息,这点也很好理解,写的消息是很多的,如果写了之后立马读,那肯定会响应不过来,所以写可以慢一点,读就可以快一点。
不错的地址:
- [Android——Handler详解][https://blog.csdn.net/ly0724ok/article/details/117324053/]:这个博客里总结了一些面试题的问答,挺好的,可以看看。