目录
1.为什么要使用Handle?
2.Handle是什么?
3.Handle如何使用?
4.Handler的使用注意事项
一、为什么要使用Handle?
以前,我们发起一个网络请求,请求回来的结果要显示到ui上,那么这个时候,我们可能会使用runOnUiThread,从子线程切换回主线程去执行ui。这种代码少的时候还好,但是一多,代码就会变得复杂,所以,今天我们来学习一下Handle。
二、Handle是什么?
Handler是一个非常重要的类,它用于在不同的线程之间发送和处理消息(Message)和可运行的(Runnable)对象。Handler通常与Looper和MessageQueue一起工作,以实现在不同线程之间的通信,特别是用于从后台线程(如工作线程或子线程)更新UI线程(主线程)上的UI元素。
那么Android为什么不能在非UI线程中更新呢?首先Android的UI控件不是线程安全的,这是因为避免多线程并发所带来不安全问题。例如作一个假设,现在在子线程中刷新界面,同时也在UI线程中刷新界面,就会出现刷新不同步.
三、Handle如何使用
3.1 三步走:创建handler、接收message以及发送message
class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
private var myHandler = MyHandler(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main3)
val msg = Message();
msg.what = 1;
//3.发送消息
myHandler.sendMessage(msg);
}
//1. 定义Handler。Handler静态内部类 防止内存泄漏
private class MyHandler(activity: MainActivity) : Handler() {
private val weakReference: WeakReference<MainActivity> = WeakReference(activity)
override fun handleMessage(msg: Message) {
//2. 处理消息。主线程
val mainActivity = weakReference.get()
if (mainActivity != null) {
Log.d(mainActivity.TAG, "handleMessage: "+msg.what)
}
}
}
}
3.2 工作原理
- 当一个线程需要使用Handler来处理消息时,首先需要创建一个Looper对象,并调用其prepare()方法创建一个与当前线程关联的消息队列。
- 接着,通过Looper的loop()方法启动消息循环,开始循环读取消息队列中的消息。
- 当有消息通过Handler的sendMessage()方法发送到消息队列时,Looper会不断从消息队列中取出消息,并将其传递给对应的Handler进行处理。
- Handler在接收到消息后,会调用自己的handleMessage()方法来处理消息。
3.3 方法
移除消息
(1)handler.removeCallbacks(runnable);在runnable被执行之前取消这个定时任务
(2)mHandler.removeCallbacksAndMessages(null)
是用来移除Handler中所有待处理的消息和Runnable任务。
具体而言,removeCallbacksAndMessages(null)
方法会将Handler中所有标识符为null的消息和Runnable对象从MessageQueue中移除。这意味着所有未被处理的消息和任务都会被取消,并且不会再被执行。
使用removeCallbacksAndMessages(null)
可以避免潜在的内存泄漏问题,特别是当Handler持有对Activity或Fragment等生命周期较长的对象的引用时。通过调用该方法,我们可以确保在Activity或Fragment销毁时,所有与之相关的延迟执行的任务都被取消,从而防止可能导致内存泄漏的情况发生。
需要注意的是,如果想要只移除特定标识符的消息或任务,可以使用removeCallbacks()
或removeCallbacksAndMessages(Object token)
方法,并传递相应的标识符作为参数。
3.4 怎么从主线程发送消息到子线程?(虽然这种应用场景很少)
Thread thread = new Thread(){
@Override
public void run() {
super.run();
//初始化Looper,一定要写在Handler初始化之前
Looper.prepare();
//在子线程内部初始化handler即可,发送消息的代码可在主线程任意地方发送
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//所有的事情处理完成后要退出looper,即终止Looper循环
//这两个方法都可以,有关这两个方法的区别自W行寻找答案
handler.getLooper().quit();
handler.getLooper().quitSafely();
}
};
//启动Looper循环,否则Handler无法收到消息
Looper.loop();
}
};
thread.start();
//在主线程中发送消息
handler.sendMessage();
延时
(1)Handler().postDelayed(Runnable, long):用于执行一个延时的任务
这句代码的执行线程取决于Handler是在哪个线程中创建的。
如果Handler是在UI线程(即主线程)中创建的,那么postDelayed方法中的Runnable将在UI线程中延迟执行。
如果Handler是在一个后台线程中创建的,并且该线程有自己的Looper循环(通过调用Looper.prepare()和Looper.loop()),那么postDelayed方法中的Runnable将在这个后台线程中延迟执行。
如果你在没有Looper的线程中尝试创建Handler(即没有调用Looper.prepare()的线程),那么会抛出RuntimeException。
在Android中,当执行Service的onCreate()方法时,它是在主线程(也称为UI线程)中执行的。Service本身并不运行在单独的线程中,除非你在Service内部明确地创建并启动了一个新的线程。
四、Handle的主要角色
Looper
Looper是一个负责循环遍历MessageQueue并将消息分发给对应Handler的类。每个线程只能有一个Looper实例。Looper使用一个无限循环来检查MessageQueue是否有新的消息,如果有,则将其分发给相应的Handler进行处理。
一个MessageQueue需要一个Looper。创建一个线程时并不会自动创建MessageQueue,但是主线程创建时会默认创建Looper对象,而Looper创建时就会创建MessageQueue,其他非主线程需要looper的时候就会通过调用prepare函数来实现。
UI线程(主线程)自动有一个Looper,但后台线程默认没有。如果你需要在后台线程中处理消息(例如,来自其他线程的消息),你需要手动在该线程中创建Looper。
Handler handler;
Thread thread = new Thread(new Runnable() {
public void run() {
Looper.prepare();
handler = new Handler() {
public void handleMessage(Message msg) {
// 处理消息逻辑
}
};
Looper.loop();
}
});
thread.start();
MessageQueue
MessageQueue是一个存储Message对象的队列。它按照FIFO(先进先出)的顺序管理消息。当我们调用sendMessage()方法时,Message对象会被添加到MessageQueue中等待处理。
Message
Message:消息,就是一个载体,包含消息ID,消息处理对象和处理的数据等