我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就会崩溃,解决的方案应该也是很清楚,就是创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了。这种处理方式被称为异步消息处理线程。会用,不是我们的最终目标,要弄懂它们的原理才是,只有明白了它的原理,用起来才能得心应手,出了问题也比较容易快速定位。
关于在怎么用真实源码来讲清楚,网上一搜一大把,我不想重复造轮子,我今天就用我的方法来解读源码,我先讲原理。
首先从哪里讲起呢?首先要读懂handler机制,你得知道handler本身是个什么东西,它是个通讯工具,看过抗战题材的影视剧同学都知道,那个嘀嘀哒哒的,电报机。
那它能收命令也能发命令。命令是什么?就是message,那messagequeen顾名思义自然就是命令队列了。注意,不是集合,是队列。就是电报机发出的命令一个个排好队等着,发一个就入队一个,嗯,没错就是那个你们看到的那个queue.enqueueMessage(msg, uptimeMillis);uptimeMillis这个参数是可以空的。
那么,它是怎么出去的呢?肯定不是自己进自己出啊,那还有什么意义呢?需要借助另外一个重要的东西:Looper,看到名字,先不要胆怯,核心代码都是外国人写的,去百度翻译一下先,loop:环、回路、圈的意思,想象一下,再对应前面,我们有消息队列,而且知道它通过handler可以不断入队,那looper肯定就是出队啊,怎么出呢?先别急,我们要得知道它是从怎么来的。这就真的看源码了,ActivityThread中的main()
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
第七行,Looper.prepareMainLooper()就是制造出一个looper,倒数第二行的 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();
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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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就是一个消息处理中心,它负责管理消息队列,负责让消息发出去,msg.target.dispatchMessage(msg)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
handleMessage(msg)是不是很熟悉?不用怀疑,就是我们写的那个,它又转回来了。到此,整个流程就完成了。
细心一点的人就会问啦,那个子线程里面的looper是怎么跑到ui线程里面去的呢?
答案就是ThreadLocal
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
sThreadLocal.set(new Looper()) 设置了looper
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
myLooper()就是拿到looper。