一. 为什么使用Handler
- 当UI线程出现耗时操作时会
ANR(Application Not Responding)
,为了避免ANR
,通常把耗时操作放在子线程里面去执行,但是子线程不能更新UI,当子线程需要更新UI的时候就需要借助Android消息机制,也就是Handler机制。
如果在子线程中做更新UI的操作会出现
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
- Handler还用于处理延时任务
二. Handler消息机制原理
Handler不是独立存在的,一个handler一定有一个专属的线程,一个消息队列(MessageQueue
),和一个looper
与之关联。
- Handler——既是发送者也是接收处理者
-
MessageQueue——
Handler
发送的消息Message需要一个容器来存放消息,MessageQueue
就是这个消息队列。 -
Looper——Message被
Handler
发送出来,会被放入MessageQueue
中,放入其中的Message在条件成熟的时候会被取出来,交由Handler
去处理。整个过程中还需要一个对象来执行从MessageQueue
取出并交给对应Handler
的工作,Looper就是这个对象
过程:先调用Looper.prepare()
创建Looper
,然后执行你的代码,最后调用Looper.loop()
执行死循环。注意Looper.loop()一定放到代码的最后一行
。死循环中会执行MessageQueue.next()
方法去取出队列中的消息,当消息为空时,MessageQueue.next()
方法中会执行nativePollOnce()
的native
方法休眠Looper.loop()
死循环。当有新的消息插入到MessageQueue
中,也就是调用MessageQueue.enqueueMessage()
方法,这个方法当中会判断Looper
是否是休眠状态,如果是休眠状态会执行nativeWeak()
的native
方法来唤醒Looper()
。
注意:
1.创建之前必须先初始化Looper,否则会RuntimeException(在主线程中初始化Handler对象前,系统默认通过Looper对象调用了一次prepare方法)
2.在Looper.prepare()的同时,Looper.loop()来启动循环,否则Handler仍然无法正常接收;
3.因为Looper.loop()有死循环,Looper.loop()之后的代码将无法执行,所以需要将Looper.loop()放在代码最后;
4.Looper在每个线程只能存在一个,如果再去手动创建Looper也会抛出RuntimeException;
三. Handler的使用
- Thread+Handler
- 发送空
private void sendEmpty() {
new Thread(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage(EMPTY);
}
}).start();
}
- 发送非空
private void sendUnEmpty() {
new Thread(new Runnable() {
@Override
public void run() {
Message msg = handler.obtainMessage();
msg.what = UNEMPTY;
Bundle data = new Bundle();
data.putString("send1", "白夜行");
msg.setData(data);
handler.sendMessage(msg);
}
}).start();
}
- 接收
private static final int EMPTY = 1;
private static final int UNEMPTY = 2;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what== EMPTY){
sendEmpty.setText("EMPTY");
} else if(msg.what== UNEMPTY){
Bundle data = msg.getData();
sendUnempty.setText(data.getString("send1"));
}
}
};
- Runnable + Handler.post
new Thread() {
@Override
public void run() {
new Handler().post(new Runnable() {
@Override
public void run() {
//更新界面
}
});
}
}.start();
- TimerTask + Handler——无限循环使用Java自带的TimerTask类,TimerTask相对Thread来说资源消耗更低
private Timer timer = new Timer();
private void goLooper() {
timer.schedule(new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(0);
} }, 0, 1000);
}
//取消timer.cancel();
- 子线程使用Handler
private void createThreadHandler() {
new Thread() {
public void run() {
Looper.prepare();
threadHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 0) {
Toast.makeText(HandlerActivity.this, "子线程Handler", Toast.LENGTH_SHORT).show();
}
}
};
Looper.loop();
}
}.start();
}
private void useThreadHandler() {
if (threadHandler != null) {
new Thread() {
public void run() {
threadHandler.sendEmptyMessage(0);
}
}.start();
}
}
附加:
AsyncTask是android提供的一个助手类,它对Thread和Handler进行了封装,方便我们使用。Android之所以提供AsyncTask这个类,就是为了方便我们在后台线程中执行操作,然后将结果发送给主线程,从而在主线程中进行UI更新等操作。在使用AsyncTask时,我们无需关注Thread和Handler,AsyncTask内部会对其进行管理,这样我们就只需要关注于我们的业务逻辑即可。
网友评论