我觉得我有必要讲一下Handler机制这个问题,因为经常面试的同学都知道Handler机制是面试官最喜欢问的问题。刚毕业那两年就经常栽倒在这个问题,所以有必要对Handler机制有深刻的了解。下面我将详细讲解Handler机制。
我们都知道Android的第一个程序是init进程,这个程序是一个用c写的应用程序,它通过rc脚本启动java的第一个程序Zygote(ZygoteInit.java)进程。
Zygote进程又名孵化器,它创建着Android系统的大部分服务和一些重要进程,其中ActivityThread类就是在这个进程实例化并运行的。
下面我们将讲一下ActivityThread.java的main函数:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
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();
想要知道这行代码是什么意思,就只能先看看这个方法的内容
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));
}
/**
* 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);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
从上面prepareMainLooper分析,先执行了prepare(false)方法,从这个方法上
看就是Looper如何已经存在那么就抛出异常终止运行。而sThreadLocal.get()
方法是怎么实现的呢?那就让我我们来看看,ThreadLocal是什么
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
上面的代码就是ThreadLocal<T>类中的get、getMap方法,从代码上分析意
思是:获取当前线程的threadLocals属性,这个属性是ThreadLocalMap类
的对象,下面看看这个对象是什么
这个对象也就是包含了一个Entry[]数组,这个Entry的父类是弱引用类
获取到e对象后,就返回它的value值,也就是上面说的Looper()对象,
经过上面的分析我们得出总结:
从当前线程中的数组获取Looper对象,这个下标是sThreadLocal对象
的哈希码。然后因为sThreadLocal是一个静态属性,所以下标是固定的。
既然下标是固定的,那么Looper保存到数组里,就表明数组里只有
一个Looper对象,最后的结论是一个线程只有一个Looper对象。
第一阶段将完了,开始下一个阶段
其他的我们就不讲了,因为我们只讲跟Handler机制有关的,跟Handler
机制有关的还有Looper.loop()方法。
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; // 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); boolean slowDeliveryDetected = false; 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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; try { msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logSlowDelivery) { if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); slowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; } } } if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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(); } }
根据上面的代码我们可以知道Looper有一个MessageQueue,我们先来看看
MessageQueue.next()方法,这个方法返回了一个Message对象,在这个
方法中主要设计的是message.next和message.target,我们来看看这个
类Message。
上面的图片是Message的部分代码,target是Handler对象,next表示
下个Message对象,表明了MessageQueue是一个链表结构。
根据我们实际的应用,发送message时是用Handler.sendMessage
发送消息的。
这样我们就发现了一个问题,如果发送的Message都包含Handler那么就很容易造成内存
泄露(在Handler长时间存在的情况下)。
所以Android为避免出现这个问题,设计了一个obtain()方法:
上面的代码是Message.java的一部分,从代码上看出,sPool是一个静态
的Message变量,而obtain()方法就是首先从Message池里获取message
对象,没有的话就新建,其实message使用完毕后又会保存到message池中,
MAX_POOL_SIZE=50表示,message池大小是50。
下面我们回到最开始的地方
从上面的代码上分析,for是一个一个死循环,queue.next()方法会
获取Message对象来执行,没有对象就会阻塞。下一步就是执行message
消息,msg.target.dispatchMessage(msg);
queue.next()的部分代码如下:
nativePollOnce()方法是一个阻塞方法,意思是有message添加到
队列里就就会发送释放消息,这个方法才会解除阻塞。
Looper.loop()方法的最后一步:回收
面我们将讲解,上面没说完的message回收到Message池的内容
上面的代码是Message.recycleUnchecked()回收方法,重置message
对象后就保存到message池中,上限是MAX_POOL_SIZE=50.
第二阶段我们讲完了,下面开始第三阶段
上面我们讲到一个阻塞方法nativePollOnce(),那我们将详细说明解除
阻塞的内容。
说到解除阻塞的内容,我们就需要讲一下发送消息。我们都知道发送消息
是通过Handler.sendMessage()方法发送,那我就列举Handler的核心
代码:
上面的代码是Message.recycleUnchecked()回收方法,重置message
对象后就保存到message池中,上限是MAX_POOL_SIZE=50.
第二阶段我们讲完了,下面开始第三阶段
上面我们讲到一个阻塞方法nativePollOnce(),那我们将详细说明解除
阻塞的内容。
说到解除阻塞的内容,我们就需要讲一下发送消息。我们都知道发送消息
是通过Handler.sendMessage()方法发送,那我就列举Handler的核心
代码:
从上面的代码分析出:发送消息,最终还是要执行MessageQueue中
的enqueueMessage()方法。
上面就是MessageQueue.enqueueMessage方法,这个方法的意思是
把message保存到链表里,并执行nativeWake解除阻塞。
到这里Handler机制我们算是讲完了,主要是总结了一下内容:
-
一个线程只有一个Looper
-
message消息的执行,和阻塞
-
message消息的发送,和解除阻塞
-
message的回收
关注微信公众号,即可得知Android相关知识