一、什么是Handler
Handler是Android提供的用来更新UI的一套机制,也是一套消息处理机制,我们可以通过它发送消息,也可以通过它处理消息。
二、为什么要使用Handler
Android在设计的时候,就封装了一套消息创建、传递、处理机制,如果不遵循这样的机制就没有办法更新UI信息,就会抛出异常。
三、怎么使用Handler
- 在非UI线程借助Handler.post(Runnable)更新UI
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler();
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
new Thread(){
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("Hello Handler");
}
});
}
}.start();
}
}
- 借助Handler.postDelay(Runnable, DelayTime)定时执行相关动作
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler();
private MyRunnable runnable = new MyRunnable();
private class MyRunnable implements Runnable{
@Override
public void run() {
// 执行动作
handler.postDelayed(runnable, 1000);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler.postDelayed(runnable, 1000);
}
}
- 自定义Handler和Message实现自己的操作处理
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){ // 判断消息类型
// 根据消息类型对消息进行处理
// 可以接收 msg.arg1 , msg.arg2 , msg.obj
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
@Override
public void run() {
Message message = new Message();
// 设置message传递参数
message.what = 0;
message.arg1 = 0;
message.arg2 = 0;
message.obj = null;
// 发送消息
handler.sendMessage(message);
// 或
// message.setTarget(handler);
// message.sendToTarget();
}
}.start();
}
}
ps:Handler消息拦截(不接收消息)
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler(new Callback(){
@Override
public boolean handleMessage(Message msg) {
return true; // 设置true拦截消息
}
}){
@Override
public void handleMessage(Message msg) {
switch (msg.what){ // 判断消息类型
// 根据消息类型对消息进行处理
// 可以接收 msg.arg1 , msg.arg2 , msg.obj
}
}
};
}
四、为什么Android要设计只能通过Handler机制更新UI?
- 最根本的目的就是解决多线程并发问题
假设如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样的问题?(更新界面错乱)如果对更新UI的操作都进行加锁处理,又会产生什么样的问题?(性能下降) - 基于对以上目的的考虑,android给我们提供了一套更新UI的机制,我们只需遵循这样的机制,无需考虑多线程问题。
五、Handler的原理是什么?
- Handler封装了消息的发送:内部会跟Looper关联
- Looper(消息封装的载体):内部包含一个消息队列(MessageQueue),所有Handler发送的消息都会走向这个消息队列;Looper.Looper方法是一个死循环,不断的从MessageQueue取消息,如果有消息就处理消息,没有消息就阻塞
- MessageQueue(消息队列):可以添加消息,并处理消息
总结:Handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己(handleMessage),MessageQueue就是一个存储消息的容器。
Handler原理图解六、Handler异步消息处理(HandlerThread)
这里只简单讲一下使用方法,想要仔细了解可以看一下这篇博文
Android HandlerThread 完全解析
public class MainActivity extends AppCompatActivity {
private Handler handler;
private HandlerThread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
thread = new HandlerThread("Handler Thread");
thread.start();
handler = new Handler(thread.getLooper()){
@Override
public void handleMessage(Message msg) {
// 处理耗时操作
}
};
handler.sendEmptyMessage(1);
}
}
七、非UI线程真的不能更新UI吗?
相信大家在日常开发中可能会遇到这种情况:
- 以下代码运行时抛出异常
public class MainActivity extends Activity {
private TextView tvText;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.main_tv);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
tvText.setText("OtherThread");
}
}).start();
}
}
- 以下代码运行正常
public class MainActivity extends Activity {
private TextView tvText;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.main_tv);
new Thread(new Runnable() {
@Override
public void run() {
tvText.setText("OtherThread");
}
}).start();
}
}
一脸懵逼了吧,两部分代码的区别就是是否执行了Thread.sleep(200),追究原因,我们会发现其实在每个View的操作时都会执行ViewRootImpl的成员方法checkThread(),如果更新UI的当前线程不是主线程,则会抛出异常。
那么这里第二部分没有抛出异常是因为在setText操作是在onCreate里面进行的,然后在onResume方法被执行后我们的ViewRootImpl才会被生成,所以并未执行相应的checkThread()操作,而当进程执行sleep操作后,ViewRootImpl已经被创建完毕,故执行checkThread(),然后抛出异常
详细原因可阅读:为什么我们可以在非UI线程中更新UI —— AlgeStudio
哈哈,Handler的基础知识介绍就到这里了,希望对大家有所帮助。
网友评论