粗读 Android Handler 源码
Handler源码和实现机制主要包含了几个关键的类Handler、Message、ThreadLocal、Looper、MessageQueue、ActivityThread。
当我一次无意中在子线程中尝试创建一个Handler实例的时候,果断程序就奔溃报错了。
“Can’t create handler inside thread that has not called Looper.prepare()”
意思大概是说不能在Looper.prepare()方法没执行前创建Handler对象。当时也没在意就把它放到了外面初始化了。后面看源码的时候自己就思考这个问题为啥在外面可以创建呢?原来报错的原因是因为因为mLooper对象为空值
//获得一个Looper对象实例
mLooper = Looper.myLooper();
if (mLooper == null) {
//说明Looper对象初始化失败了(没有初始化)
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
````
那按这样推倒意思就是说Looper.prepare()方法的必须在之前执行过啊。看下源码
<div class="se-preview-section-delimiter"></div>
``` java
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));
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
果然如我们所料啊。原来Looper.prepare()方法的作用是做了初始化的工作。那我们回到前面说的,那我在在线程中创建Handler实例之前执行下这个方法是不是就可以呢?答案是肯定的。那有人肯定会问为啥直接在主线程中创建Handler实例就不会报异常呢,按这个来推倒的话那肯定是在主线程某个地方做了这件事啊。这时候我们的ActivityThread就登场了,咋们直接定位到他的main()函数。查看里面的代码
public static void main(String[] args) {
....
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
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()其实最终也是调用了啊Looper.prepare(),除此之外我们还有意外收获那就是另一个很重要的方法 Looper.loop()调用了这个方法。
这方法调用的时机一般是在Handler初始化完成之后,那我们就可以判断出Activity的一些创建 绘制什么的肯定也是在Looper.prepareMainLooper()和Looper.loop()进行的至于为什么这里就不细说了。出门百度 O(∩_∩)O哈哈~
好那我们去看下这个核心方法吧
/**
* 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();
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 traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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();
}
}
这里用了一个for(;;)循环对消息队列进行无限轮询,我们只要关心主要的代码就可以了
msg.target.dispatchMessage(msg),这个方法就是分发我们的消息的进去我们可以看到如下
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/**
* 当以Post形式发送消息的时候回调其
* run方法
* @param message
*/
private static void handleCallback(Message message) {
message.callback.run();
}
至此我们熟悉的终于出现了,这里主要体现了一个优先级Handler消息接收也有这两种方法。接口优先然后才是重写的方法。如果接口为空则调用handleMessage(msg);最终就到了我们这里了
private Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
String s= (String) msg.obj;
tv1.setText(s);
break;
}
}
};
* 所以我们要在子线程中模仿上面的步骤自己实现一个Handler除了在创建Hnadler实例之前需要调用Looper.prepare()方法还需在实例创建完成后执行Looper.poop()方法启动消息队列轮询 *
让我们回到开始,我们使用Handler发送消息一般有两种方式分别是是handler.sendXXX()和handler.postXXX(),但是不管哪一种最终都是调用了sendMessageAtTime(Message msg,long uptimeMillis)方法,然后在调用MessageQueue的enqueueMessage()方法,作用是往队列里面插入一条Message,然后再loop中通过next方法不断地取出Message进行处理。
盗用网上一张图描述这几者之间的关系网(希望作者不要跟我一般计较):
以上就是我看源码的一些过程,期间也有参阅一些资料。感谢那些乐于分享的前辈!!
可能有点乱,全当记录一下。供以后翻阅吧,如有错…请指出。轻喷哈