一、为什么使用Handler
当启动应用程序时,android系统会启动一个主线程,也就是UI线程。UI线程主要负责事件的分发,它是不安全的。如果一个事件(下载、读取本地大文件等比较耗时的操作)响应超过5秒,那么它就会假死,进入强行关闭状态。故而,类似这种超时操作就需要在子线程中进行。那么问题又来了,子线程大多是不能更改UI的,那么当事件完成想在UI上做个显示都不行了。所以,Handler出现了。Handler可以说是子线程与主线程之间协作的桥梁,主线程和子线程之间的消息传递都是通过Handler来实现的。
二、如何使用Handler
这就涉及到是主线程传递消息给子线程,还是子线程传递消息给主线程的问题了。
子线程传递消息给主线程
布局文件是默认hello_world,点击发生内容改变。
package com.yixingu.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.tv);
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
String s = (String)msg.obj;
tv.setText(s);
}
};
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
String s = "内容更改";
Message msg = handler.obtainMessage();
msg.obj = s;
handler.sendMessage(msg);
}
}).start();
}
});
}
}
可以看出Handler是在主线程中定义并初始化的,并重写handleMessage(Message msg)方法,故而子线程中的更新UI功能能实现。而子线程中出现了一个Message类,这就是消息类,主要要功能是进行消息的封装。待会再来详细介绍。
主线程传递消息给子线程
布局文件是默认hello_world,点击将信息传递给子线程,并在后台输出。
package com.yixingu.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView tv;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.tv);
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Message msg = handler.obtainMessage();
msg.obj = "内容更改";
handler.sendMessage(msg);
}
});
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println((String)msg.obj);
}
};
Looper.loop();
}
}).start();
}
}
看到这里相信很多人都一头雾水了。首先不说那个Message类,那个Looper类又是什么,还有那两个方法又是什么,而且为什么子线程向主线程传递消息时,又不用写Loop类的?还有它们之间的联系又是怎样的?
Handler深度解剖
首先分别了解一下Message和Looper类的作用:
Message类:
android.os.Message的主要功能是进行消息的封装,同时可以指定消息的操作形式,Message类定义的变量和常用方法如下:
(1)public int what:变量,用于定义此Message属于何种操作
(2)public Object obj:变量,用于定义此Message传递的信息数据,通过它传递信息
(3)public int arg1:变量,传递一些整型数据时使用
(4)public int arg2:变量,传递一些整型数据时使用
(5)public Handler getTarget():普通方法,取得操作此消息的Handler对象。
在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,但是有这么几点需要注意:
(1)尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。
(2)如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
(3)擅用message.what来标识信息,以便用不同方式处理message。
Looper类
Looper就是一个循环通道。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。设置为Looper线程主要用到prepare()和loop()两个方法。而android系统中启动MainActivity时就为我们启动了Looper对象,故而在主线程传递消息给子线程时就不用初始胡Looper对象了,而在一个用户自定义的类中,则需要用户手工调用Looper类中的方法,然后才可以正常启动Looper对象,实现也就是前面所写的:
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// ...其他处理,如实例化handler
// 开始循环处理消息队列
Looper.loop();
}
}
从上面几个步骤,我们基本可以知道Handler在消息传递机制中是处于充当中间者的角色,上有Looper的prepare()方法,下有loop()方法。那么,他们之间是如何进行联系的呢?也就是我们中的一个问题。
这只能从源码中找到根源了。
首先看prepare()方法:
public class Looper {
// 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper内的消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
//其他属性
// 每个Looper对象中有它的消息队列,和它所属的线程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
// 我们调用该方法会在调用线程的TLS中创建Looper对象
public static final void prepare() {
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper将抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
// 其他方法
}
从中可以知道,prepare方法实现了绑定当前线程,实例化一个Looper对象并保证只有一个Looper对象,核心就是将looper对象定义为ThreadLocal。ThreadLocal可以在一个线程中存储变量。
再看Handler做了什么。
public class handler {
final MessageQueue mQueue; // 关联的MQ
final Looper mLooper; // 关联的looper
final Callback mCallback;
// 其他属性
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());
}
}
// 默认将关联当前线程的looper
mLooper = Looper.myLooper();
// looper不能为空,即该默认的构造方法只能在looper线程中使用
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
mQueue = mLooper.mQueue;
mCallback = null;
}
}
handler起到了处理MQ上的消息的作用(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper。
最后看Looper的loop()方法:
public static final void loop() {
Looper me = myLooper(); //得到当前线程Looper
MessageQueue queue = me.mQueue; //得到当前looper的MQ
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 开始循环
while (true) {
Message msg = queue.next(); // 取出message
if (msg != null) {
if (msg.target == null) {
// message没有target为结束信号,退出循环
return;
}
// 日志
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
// 非常重要!将真正的处理工作交给message的target,即后面要讲的handler
msg.target.dispatchMessage(msg);
// 日志
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf("Looper", "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);
}
// 回收message资源
msg.recycle();
}
}
}
可以看出loop()方法所做的就是不断从自己的MQ中取出队头的消息(也叫任务)执行
综上过程,我们稍微总结一下:
1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue相关联。
3、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
本文Handler深度解剖内容参阅了 Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系 以及android的消息处理机制——Looper,Handler,Message ,如有不对,盼指出批评。谢谢!