Andrid异步更新UI:Handler(一)基础知识

目录

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 工作原理

  1. 当一个线程需要使用Handler来处理消息时,首先需要创建一个Looper对象,并调用其prepare()方法创建一个与当前线程关联的消息队列。
  2. 接着,通过Looper的loop()方法启动消息循环,开始循环读取消息队列中的消息。
  3. 当有消息通过Handler的sendMessage()方法发送到消息队列时,Looper会不断从消息队列中取出消息,并将其传递给对应的Handler进行处理。
  4. 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,消息处理对象和处理的数据等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前期后期

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值