在Andrid启动的时候,默认会有一个主线程,也就是UI线程,在Android3.0之后的版本中,Android禁止在主线程中进行网络请求,通常情况下网络请求会一个极其耗时的操作,所以耗时操作会严重影响用户的体验,甚至会出现ANR,但是有一个需求永远存在,就是在耗时操作之后,把耗时操作之后的结果更新到界面上来。一般情况下,可以这么用:
new Thread(new Runnable(){
@override
public void run(){
//这个是随意定义的耗时操作
String response = HttpUtils.post(url,map);
//这个时候想把获取的response显示到界面上去,但是只有UI线程才能更新UI界面
//而且这个子线程获取到的response与显示界面的操作得同步,我们可以这么用
runOnUiThread(new Runnable(){
@override
public void run(){
//将网络获取到的结果显示到UI界面上去
mTvDisplay.setText(response);
}
});
}
}).start();
而runOnUiThread()
方法底层则是调用了Android
提供的异步消息处理机制Handler
,该方法是对异步消息处理机制的一个封装,这个异步消息处理机制Handler
处理消息的过程可以简单地分为:
Message
、Handler
、MessageQueue
、Looper
,他们的关系如下:
这张图可以一目了然地将他们之间的关系描述得非常清楚,他们的处理过程是这样的:
UI线程的消息循环是在ActivityThread.main()
方法中创建的,源码如下:
public static void main(String[] args){
...
Looper.prepareMainLooper();//创建消息循环Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if(sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();//UI线程的Handler
}
...
Looper.loop();//执行消息循环
}
开始执行从main()
方法开始,如何将Message
投递到MessageQueue
中,如何将Message
从MessageQueue
中取出,这其中的关键就是Handler
。其实每一个Handler
都会关联一个消息队列,消息队列被封装在Looper
中,而每个Looper
又会关联一个线程(Looper
通过ThreadLocal
封装),最终就等于每个消息队列会关联一个线程。默认情况下,消息队列只有一个,就是UI线程的消息队列,它是在main
方法中被 创建的,Looper.prepareMainLooper()
方法来创建,Looper.loop()
来启动消息循环,Handler
是如何关联消息队列以及线程的,看源码:
public Handler(){
...
mLooper = Looper.myLooper();//获取Looper
if(mLooper == null){
//throw Exeception;
}
mQueue = mLooper.mQueue;//获取消息队列
mCallback = null;
}
public static Looper myLooper(){
return sThreadLocal.get();
}
//设置UI线程的Looper
public static void prepareMainLooper(){
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
private synchroinzed static void setMainLooper(Looper looper){
mMainLooper = looper;
}
//为当前线程设置一个Looper
public static void prepare(){
if(sThreadLocal.get() != null){
//throw Exception
}
sThreadLocal.set(new Looper());
}
Handler
的构造函数中看到,Looper
是通过Looper.myLooper()
来获取的,然后看myLooper()
方法,直接通过sThreadLocal.get()
方法获取的,那么既然能够获取就肯定已经存储在里面了,什么时候把Looper
存进去呢,看prepareMainLooper()
方法,调用了prepare()
方法,在prepare()
方法的末端,直接new Looper()
存储在了sThreadLocal
中,到这里,队列就跟线程关联上了,代表着不同的线程就不能访问别的消息队列。
回到Handler
中来,消息队列通过Looper
与线程关联上,而Handler
又与Looper
关联,因此Handler
最终就和线程、线程的消息队列关联上了。所以,更新UI的Handler
必须i要在主线程中创建,因为Handler
要与主线程的消息队列关联上,这样handleMessage
才会执行在UI线程。
Looper
怎么创建的知道了,Looper
怎么循环起来的呢,是通过looper()
方法,源码如下:
public static void loop(){
Looper me = myLooper();
if(me == null){
//throw Exception
}
MessageQueue queue = me.mQueue;//获取消息队列
...
while(true){
Message msg = queue.next();//不断获取消息
if(msg != null){
if(msg.target == null){
return;
}
...
msg.target.dispatchMessage(msg);//处理消息
msg.recycle();
}
}
}
一个死循环,代表着不断从MessageQueue
中取消息,对于Looper
的步骤大概就是以下两步:
1.通过
Looper.prepare()
来创建Looper
对象,消息队列在Looper
对象中,存储在sThreadLocal
2.通过Looper.loop()
来启动消息循环
从上述的loop()
源码中可以看出,最最最有用的一行代码就是msg.target.dispatchMessage(msg)
,那么
target
是什么,dispatchMessage(msg)
又拿着这个msg
去干了什么事,接下来继续看Message
的成员变量:
public final class Message implements Parcelable{
Handler target;
Runnable callback;
Message next;
...
}
显然target
是Handler
类的对象,那么dispatchMessage(msg)
就是Handler
拿着去干了什么,看Handler
其中的几个方法:
//一般new的Handler都会重写这个方法,用来实现自己的自定义业务
public void handleMessage(Message msg){
}
private final void handleCallback(Message message){
message.callback.run();
}
public void dispatchMessage(message msg){
if(msg.callback != null){
handleCallback(msg);
} else {
if(mCallback != null){
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg);
}
}
上述这段代码的意思就是,sHandler.post(new Runnable())
,那么就直接调用这个Runnable
的run()
,如果是直接sendMessage(msg)
的话,就直接调用重写的handleMessage()
,这里可以看一下post()
和sendMessage()
的两种实现,先看post()
:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r),0);
}
private final Message getPostMessage(Runnable r){
Message m = Message.obtain();
m.callback = r;
return m;
}
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){
boolean sent = false;
MessageQueue queue = mQueue;
if(queue != null){
msg.target = this;//将target设置为当前的Handler对象
sent = queue.enqueueMessage(msg,uptimeMillis);//将消息插入到消息队列
} else {
...
}
return sent;
}
从上面可以看出,Runnable
被设置到了Message
的Runnable
中去了,然后就直接把Message
扔到了MessageQueue
中,再看sendMessage()
;
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg,0);
}
由此可见,无论是post()
方法还是sendMessage()
方法,他们内部都是经过一步一步的调用,调用到了sendMessageDelayed()
这个方法,然后通过打包成Message
再把他们扔进了MessageQueue
中,然后Looper
从MessageQueue
不断读取消息,然后扔给了Message.target
也就是Handler
的dispatchMessage()
这个方法不断去处理消息,无论是post
的Runnable
还是handleMessage
的处理方式,经过dispatchMessage
中的判定去执行。
所以!
发送消息是Handler
的post()
方法和sendMessage()
方法,
处理消息的还是Handler
的handleMessage()
和handleCallback()
(通过Runnable中的run()方法去执行),
由此可见,有了Handler
,消息循环才能运转起来。
runOnUiThread()的实现过程大概是这样:
事件响应→开启线程→runOnUiThread→判断是否当前线程为主UI线程(是就立刻执行,不是就通过handler.post()发送到动作序列中,等到是主UI线程再立刻执行)
Handler的实现过程大概是这样:
事件响应→开启线程→new Message()→handler.sendMessage(msg);→new handler(msg.getdata→getString→更改UI)
由此可见,通过Handler
可以更加精细地控制自己的业务逻辑更好的在一些复杂的情况下进行合理的操作。
Android的异步消息机制大概就这样,过程使用,源码分析,如有不对指出望请指出批评,谢谢。
参考资料:
1.《Android开发进阶从小工到专家》
2. http://blog.csdn.net/qq_22603017/article/details/46847231