一.场景
Handler 机制是Android异步消息的核心(线程间通信), 其实Handler不局限于子线程与主线程(UI线程) ,我们完全可以创建一个子线程,然后初始化Looper,Handler,我们可以通过Handler在其他线程(包括主线程)往该子线程发送消息。例如系统帮我们封装好的--> HandlerThread (一个封装好Looper,MessageQueueu的线程(不死线程)),发送消息给子线程有什么好处呢, 这里拿HandlerThread说明,现在有个场景, 我们都知道UI线程不能做耗时的操作, 会影响程序的性能,用户体验,所以耗时(IO,网络,数据库操作)操作全部往子线程堆。怎么解决呢, 我们可以创建一个子线程HandlerThread, 拿到该线程的Looper--HandlerThread.getLooper(),然后可以new Handler(Looper).post(Runnable r)往该线程发送消息, 然后就会在该子线程执行消息;
二. 重要的概念(Handler , Looper,MessageQueue,Message)
Handler 看看官方是怎么说的吧 A Handler allows you to send and process Message
and Runnable objects associated with a thread's MessageQueue ,简单说Handler主要的作用就是往MessageQueue插入一个消息
Looper --Looper作用是从MessageQueue读取Message,然后交给Handler处理消息,另外Looper还可以保证线程不死
MessageQueue 消息队列, 链表插入和删除方便, 主要用于存放消息(Message)
Message 消息(消息会包装发送Message的Handler,详细见下面 一一源码验证)
三.源码解析(UI线程应用Handler机制)
<span style="color:#3333ff;"> </span>private static final TaskHandler TASK_HANDLER = new TaskHandler();
public void doClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message message = Message.obtain();
message.arg1 = ++i;
TASK_HANDLER.sendMessage(message);
}
}).start();
}
static final class TaskHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.i("Info", "message:"+msg.arg1);
}
}
上面代码很简单, 估计大家都会, 在子线程中睡三秒, 然后往UI线程发送一个消息。很简单就完成了一个子线程和UI线程的通信,并且没有任务问题, 其实,Android在背后为我们做了很多封装和处理了,才可以往UI线程发送消息, 不信见如下:
new Thread(new Runnable() {
@Override
public void run() {
handler = new Handler();
handler.sendEmptyMessage(0x33);
}
}).start();
}
在子线程初始化一个Handler,然后发送一个消息。结果:
07-10 15:12:53.251: E/AndroidRuntime(5296): FATAL EXCEPTION: Thread-158
07-10 15:12:53.251: E/AndroidRuntime(5296): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
07-10 15:12:53.251: E/AndroidRuntime(5296): at android.os.Handler.<init>(Handler.java:121)
07-10 15:12:53.251: E/AndroidRuntime(5296): at com.example.just_intentservice.MainActivity$1.run(MainActivity.java:25)
07-10 15:12:53.251: E/AndroidRuntime(5296): at java.lang.Thread.run(Thread.java:856)
抛出异常了,大概意思是说没有,不能在没有初始化Looper的线程去创建Handler;
点进Handler源码查看下,
public Handler() {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
先会到当前线程查找Looper ,如果没有Looper就会抛出上面的异常。所以不能在没有初始化Looper的线程初始化Handler,UI线程之所以可以初始化Handler,我们见以下,Android UI线程是如何初始化Looper吧...
Java应用程序入口都是从mian函数, Android也不例外,只不过被SDK用hide标识着,我们无法查看,看下Android,UI线程是如何玩转Handler机制的,在ActivityThread里面main函数代码如下
public static void main(String[] args) {
//******省略若干代码****//
Looper.prepareMainLooper();//初始一个Looper
Looper.loop();//开始无限循环读取消息
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我们代码追踪看Android是如何初始换Looper,在Looper类里面
public static void prepareMainLooper() {//初始化MainLooper也是UI线程的Looper
prepare();
setMainLooper(myLooper());
}
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
private Looper() {
mQueue = new MessageQueue();//初始化一个MessageQueue
mThread = Thread.currentThread();
}
先判断下当前线程是否存在Looper,存在抛出异常, 也就是说一个线程自能有一个Looper, 如果没有,则new出一个Looer(在构造方法里面初始化了一个对应的MessageQueue), 然后通过sThreadLocal保存起来, ThreadLocal用于线程保存对象 。然后来看下
setMainLooper(myLooper());
public static Looper myLooper() {
return sThreadLocal.get();
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
可以看出myLooper就是从当前线程中拿出Looper,然后返回,setMainLooper(),就更简单了,其实就是保存个全局looper。
就这样UI线程就吧Looper初始化完成了,所以Handler可以正常的在主线程初始化;
下面我们来走一把完整的Handler机制通信流程
handler.sendEmptyMessage(0x33); <span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">----子线程</span>
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);// 包装下时间调用两个参数<span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">sendEmptyMessageDelayed方法</span>
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();//获取一个Message
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;//如果时间小于0则赋值为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;//this表示当前handler
sent = queue.enqueueMessage(msg, uptimeMillis);//进入队列
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
子线程的handler发送一个简单的空消息到UI线程, 要经过不断的包装, 可以看出,最后都是包装Message然后进入队列(MessageQueue)其实post(Runnable r)最后也是经过包装成Message对象,发送到队列, 这里就不做说明了, 详细见源码。 我们知道Looper在UI线程初始化完成就开始无限死循环读取MessageQueue里面消息,我们看Looper是如何读取和处理消息的:
public static void loop() {
Looper me = myLooper();//获取当前线程的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue; //获取Looper里面的消息队列
while (true) { //死循环读取
Message msg = queue.next(); // might block 从队列里面拿到消息
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
//省略一些代码
msg.target.dispatchMessage(msg);//从message中拿到handler,然后调用<span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">dispatchMessage</span>
msg.recycle();//最终销毁消息
}
}
}
Handler类里面的dispatchMessage如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//callback其实是一个Runnable对象, 一般post(Runnable r)是执行
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//回调handlerMessage函数--一般该方法由我们继承去实现的
}
}
private final void handleCallback(Message message) {
message.callback.run();//执行run方法体
}
说明下, 子线程handler.sendEmptyMessage()当Message到达MessageQueue,这里就是子线程切换到UI线程了, 因为MessageQueue跑在UI线程,上面代码可以看出,Looper会不断的在MessageQueue里面读取消息,然后读到的message为空的化return掉, 其实里面还有一些唤醒机制(保证UI线程的不阻塞,当消息过来了才唤醒,这里不细说)如果message不为空,则从message中取出handler,然后调用handler的dispathMessage(Message message)方法来方法消息, 如果消息为post(Runnable r)则走handlerCallback,否则走handleMessage(msg),整个消息通信结束。
通过源码分析, 我们完全可以创建一个不死子线程,然后在主线程中往子线程发送消息:
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0x88) {
Log.i("Info", "i am sub");
}
}
};
Looper.loop();
}
}).start();
}
public void doClick(View v) {
handler.sendEmptyMessage(0x88);
}
最后总结:Handler往MessageQueue插入Message,Looper往messageQueue里面读取message,最后都是交给Handler分发处理。