主要内容
- 为什么要使用Handler
- 什么是Handler
- 如何使用Handler
- 在子线程中创建Handler
为什么要使用Handler
子线程不允许访问UI,UI操作必须在UI线程,也就是主线程中执行。
Android UI是线程不安全的,要想在子线程中更新UI,必须通过线程间通信,可以使用Handler,AsyncTask,runonUiThread等来实现。
什么是Handler
Handler允许发送和处理与MessageQueue相关的Message和Runnable对象,每个Handler实例都与一个线程和这个线程的MessageQueue相关联。当你new一个Handler时,它将绑定到创建他的Thread/MessageQueue上,然后它将向MessageQueue上发送消息或Runnable对象,并在他们从MessageQueue中释放时执行他们。
它实际上是一种消息循环处理机制。其中Handler,Looper都是在当前线程,也就是创建Handler的线程。
如何使用Handler
常用用法
新写一个类继承Handler
private static class MyHandler extends Handler {
private Context context;
private MyHandler(Context context) {
this.context = context;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
Toast.makeText(context, "hha", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
}
创建Handler实例
private MyHandler handler = new MyHandler(this);
这里需要注意的是:静态内部类只能访问外部类的静态属性和方法,非静态内部类会持有外部类的引用,可能会造成内存泄露。如果Handler在主线程中创建,那么它自动绑定主线程。Looper在哪个线程,Handler就在哪个线程处理消息。
模拟子线程发送消息
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 1; // 标志位,可以区分不同类型的消息,类比type
message.obj = 2; // 传递的对象
handler.sendMessage(message);
}
}).run();
与Handler一样,这里Thread同样存在可能造成内存泄漏的问题,但是偷个懒,这里暂时不做处理。
Toast
因为在主线程中才可以展示Toast,为了防止代码中出现子线程展示Toast的情况,我们可以在工具类中对Toast做处理
private static Handler handler;
public static void showToast(final Context context, final String msg) {
if (context == null || TextUtils.isEmpty(msg)) {
return;
}
if (handler == null) {
handler = new Handler(Looper.getMainLooper());
}
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, msg, LENGTH_SHORT).show();
}
});
}
在子线程中创建Handler?
上面是关于主线程的Handler,也就是子线程通知主线程,那么主线程如何通知子线程呢?
仿照子线程通知主线程,我们将子线程的Looper设置到Handler中去
private static class MyThread extends Thread {
/**
* 取出子线程的Looper
*/
private Looper looper;
public Looper getLooper() {
return looper;
}
@Override
public void run() {
super.run();
// 创建子线程的Looper
Looper.prepare();
// 取出该子线程的Looper
looper = Looper.myLooper();
// 只有调用下面的方法才可以循环取用消息
Looper.loop();
}
}
初始化对象
private MyThread thread;
private Handler handler;
调用
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
thread = new MyThread();
thread.start();
handler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
handler.sendEmptyMessage(0);
}
但是这样会报空指针异常,为什么呢?因为在OnCreate()方法中,子线程执行之后,下面的代码也会依此执行,可能出现的情况就是Looper还没来得及初始化,就直接在Handler中使用了,那么就会出现空指针异常。也可能不会出现异常,但这都是随机的。
我们可以使用这种方式在子线程中创建Handler
private static class LooperThread extends Thread {
private Handler handler;
public Handler getHandler() {
return handler;
}
@Override
public void run() {
super.run();
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 处理消息
Log.i("TAG","hha");
}
};
Looper.loop();
}
}
但是需要注意的是,Handler可能为空
HandlerThread
上面的这种方式过于麻烦,我们可以使用HandlerThread。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 这里是子线程,可以处理耗时操作
// 处理消息
}
};
handler.sendEmptyMessage(0);
}
HandlerThread还可用于多任务下载
引用
https://www.jianshu.com/p/0a274564a4b1
https://blog.csdn.net/iispring/article/details/47115879
https://developer.android.com/reference/android/os/Handler
https://blog.csdn.net/chenxiaofeng_/article/details/51492764
https://www.cnblogs.com/lang-yu/p/6228832.html
https://blog.csdn.net/u011240877/article/details/72905631#handlerthread-%E7%AE%80%E4%BB%8B