您的位置:首页 > 移动开发 > Android开发

Android学习之Handler使用小结

2016-06-17 14:10 489 查看
Handler的使用大家都很熟悉,今天做一下关于Handler的总结,方便以后使用。

为什么用Handler

UI线程是非安全线程,不能进行耗时操作,子线程做耗时操作,不能更新UI,否则会出现程序长时间假死状态,即ANR,程序无响应异常。

为了解决以上问题,Android设计了Handler机制,由Handler来负责与子线程进行通讯,从而让子线程与主线程之间建立起协作的桥梁,使Android的UI更新的问题得到完美的解决。


Handler的作用:

1. 在新启动的线程中发送消息;
2. 在主线程中获取、处理消息。


Handler和Looper

Message:消息体
Handler:负责发送消息sendMessage(message),接收消handleMessage(Message msg)
Looper:消息泵,用来管理特定线程内对象之间的消息交换,一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)。主要包含两个方法Looper.prepare(),Looper.loop()

Message Queue:消息队列,存放线程放入的消息
UI线程:主线程,启动的时候会创建消息队列已经 给线程创建一个消息循环Looper.prepare()


@Override
public void run() {
try {
// preparing a looper on current thread
// the current thread is being detected implicitly
Looper.prepare();

// now, the handler will automatically bind to the
// Looper that is attached to the current thread
// You don't need to specify the Looper explicitly
handler = new Handler();

// After the following line the thread will start
// running the message loop and will not normally
// exit the loop unless a problem happens or you
// quit() the looper (see below)
Looper.loop();
} catch (Throwable t) {
Log.e(TAG, "halted due to an error", t);
}
}


上面理论上看起来理解不是很理想,下面我找了几张图来方便理解。

Looper.prepare():



可以看出通过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()会new Looper()创建一个Looper对象,对象里面分别又创建了线程和MQ。由此可知一个Thread只能有一个Looper对象对应一个MQ,三者一一对应。

Looper.loop():



Looper会不断从MQ中取出消息头执行,并推送给Handler执行。

Handler发送消息:



一个线程可以有多个Handler,但是只能有一个Looper!Handler发送消息通过Looper添加到MQ。

Handler处理消息:



Looper取出消息头,Handler执行,完毕后返回Looper继续执行。

总流程:



使用案例

子线程向主线程发送消息:

new Thread(new Runnable() {
@Override
public void run() {
MyHandler handler = new MyHandler(getMainLooper());
Message message = new Message();
message.obj = "子线程发送的消息";
handler.sendMessage(message);

}
}).start();

class MyHandler extends Handler {
public MyHandler(Looper looper) {
super(looper);
}

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("TAG", msg.obj.toString());
}
}


主线程向子线程发送消息:

class TestThread extends Thread
{
private Handler handler;
@Override
public void run() {
super.run();
Looper.prepare();
handler=new Handler(getMainLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("TAG",msg.obj.toString());
}
};
Looper.loop();
}
}


主线程发送:

new TestThread().handler.sendEmptyMessage(0);


Handler的post()方法:

new Thread(){
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
//更新UI
}
});
};
}.start();


runOnUiThread()方法:

new Thread(){
public void run() {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
//更新UI
}
});
};
}.start();


post和runOnUiThread都可以在子线程中更新UI,但是实际上他们是将消息post到UI线程中处理,所以run()里面不能做耗时操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息