概念
主要用于子线程和主线程(UI线程ActivityThread)通信。【事件驱动模式】
为什么系统不允许子线程更新UI?
(1)UI控件不是线程安全的,多线程并发访问可能会导致UI控件处于不可预期的状态,
(2)虽然加锁可以解决线程安全问题,但是加锁后又两个对手机不可接受的缺点:
上锁会让UI控件变得复杂和低效;并且会阻塞某些进程的执行.
Handler实现机制
1.Handler发送消息:调用MessageQueue的enqueueMessage()向消息插入到MessageQueue
2.Looper不断轮询调用MessageQueue的next()方法出队(先进先出)
Looper.loop()轮询消息——>调用messageQueue.next()出队
——>调用消息对应的handler来处理消息[msg.target.dispatchMessage(msg)];
——>消息对应的handler调用handleMessage()来处理消息.
Message msg = queue.next(); // might block
msg.target.dispatchMessage(msg);
3.伪代码
// 1.handler.sendMessage发送消息
Message msg =Message.obtain();
msg.what= MSG_UPDATE_WAY_TWO;
//handler发送消息的时候会给消息一个标志target(就是一个当前handler对象)
mHandler.sendMessage(msg);
// 2.Looper.loop()轮询消息,分发消息到指定handler【通过消息的target来判断】
loop(){
final Looper me = myLooper();//获取当前线程缓存的looper状态
if (me == null) {//缓存为空,抛异常
throw new RuntimeException("No Looper; Looper.prepare()
wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
//无限轮询
for (;;) {
//调用messageQueue.next()出队消息,获取到消息
Message msg = queue.next();
if (msg == null) {//没有消息的时候,轮询结束
// No message indicates that the message queue is quitting.
return;
}
//通过target【当前出队的msg对应的handler对象】来分发该消息
msg.target.dispatchMessage(msg);
}
//消息处理
dispathMessage() {//handler的方法
if (msg.callback != null) {
//handler构造方法传入的回掉,需要在回掉里重写handleMessage()方法
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Handler,Looper,MessageQue三者关系
* 一个线程只能有一个Looper
* 一个Looper只能创建一个MessageQue[Looper.prepare()会创建MessageQueue]
* 多个handler可以绑定一个Looper[可以在Handler构造方法里面传进looper对象]
* ThreadLoacal.set(new Looper(...))存储Looper的状态,
只有当前线程才能访问.
子线程能直接创建Handler吗? Answer:不能
(1)每个Handler必须绑定一个Looper
【一个线程只能有一个Looper,
一个Looper只能创建一个MessageQue,多个handler可以绑定一个Looper】
(2)子线程创建Handler时,需要在之前调用Looper.prepare()来创建Looper对象;
主线程在被创建的时候就已经创建了Looper()对象;
Code:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//创建Looper
handler2 = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.arg1==1) {
Toast.makeText(MainActivity.this,"hanlder2",
Toast.LENGTH_SHORT).show();
}
super.handleMessage(msg);
}
};
Message message = handler2.obtainMessage();
message.arg1 = 1;
handler2.sendMessage(message);
Looper.loop();
}
}).start();
或者:new Handler(Looper.getMainLooper());
直接在Handler构造方法里面传入主线程的Looper对象.
Handler的正确写法(防止内存泄露)
/**
* Handler正确的使用姿势
* Created by zk on 2017/10/17.
*/
public class HandlerTestActivity extends AppCompatActivity {
private final static int TXT_CHANGE=1;
private final static int IMG_CHANGE=2;
// ===== Handler作为静态类,避免内存泄露======
// ===== 1.非静态类会持有外部类的引用,而handler的target标记的延迟消息
发送到messageQue队列中
// ===== 2.Looper会去处理(Handler#handleMessage(Message)),处理消息就是通
过持有Handler的引用来去处理的
// ===== 3.而Looper的主要工作就是一个一个处理消息队列中的消息对象,主线程
中的Looper生命周期和当前应用一样长(事件驱动模式)
// ===== 4.这样Looper会长期的持有handler的引用
private MyHandler myHandler = new MyHandler(this);
public static class MyHandler extends Handler {
private final WeakReference<HandlerTestActivity> mActivity;
public MyHandler(HandlerTestActivity activity){
mActivity = new WeakReference<HandlerTestActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what){//处理handler发送的消息
case TXT_CHANGE:
Log.d("HandlerTestActivity","文本更改");
break;
case IMG_CHANGE:
Log.d("HandlerTestActivity","图片更改");
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//开启子线程
new Thread(new Runnable() {
@Override
public void run() {
try {
//睡眠1秒
Thread.sleep(1000);
Message msg = new Message();
msg.what = TXT_CHANGE;//Message的识别码
msg.obj=null;
//通过给obj赋值Object类型,向Message传入任意数据
//setData()和getData()向Message中写入和读取Bundle类型数据
//msg.setData(null);
//Bundle data = msg.getData();
myHandler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
网友评论