Android学习之Handler使用小结
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
上面理论上看起来理解不是很理想,下面我找了几张图来方便理解。
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());
}
// 其他方法
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
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());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
主线程向子线程发送消息:
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();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
主线程发送:
new TestThread().handler.sendEmptyMessage(0);
- 1
- 1
Handler的post()方法:
new Thread(){
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
//更新UI
}
});
};
}.start();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
runOnUiThread()方法:
new Thread(){
public void run() {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
//更新UI
}
});
};
}.start();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
post和runOnUiThread都可以在子线程中更新UI,但是实际上他们是将消息post到UI线程中处理,所以run()里面不能做耗时操作。

浙公网安备 33010602011771号