带你深入了解Android Handler的用法

Android中,Handler是一类用于异步消息传递和线程之间通信的基础框架。一个Handler是一个线程的处理器,可以接收消息,并调度运行它们。使用Handler,应用程序可以将处理器与一个线程关联,以将来的时间运行任务。而使用Handler,就可以避免启动额外的线程,从而提高代码的效率。本文将详细介绍Handler的概念、使用方法和常见问题等。

什么是Handler?

在 Android 开发中,使用多线程是非常常见的,但是在 Android 中,有一个 UI 线程,也就是主线程,所有的UI操作必须在主线程中完成,否则就会抛出 CalledFromWrongThreadException 异常,这个异常的原因是 Android 不允许在子线程中直接访问 UI 控件。而 Handler 的主要作用就是用于子线程与主线程之间进行通讯,在应用程序内传递消息、更新 UI 等各种场景下,都可以使用 Handler 来实现。
一个Handler是与Looper关联的对象,它拥有处理消息队列的能力,即将消息加入到队列中,并在将来的某个时间处理这些消息。每个线程只能有一个Looper,而每个Looper所属的线程可以拥有一个或多个Handler。当创建一个新的Handler实例时,它们会自动附加到创建它的线程的消息队列。当线程被销毁时,它的消息队列也会被销毁。

Handler使用消息队列来进行异步任务的处理,消息队列是基于先进先出(FIFO)的原则,即在消息队列中消息加入的顺序与取出的顺序相同。消息通常是如下形式的数据结构:

class Message {
    int what; //消息id
    int arg1; //消息参数1
    int arg2; //消息参数2
    Object obj; //消息的Object对象
    Handler target; // 处理该消息的Handler对象
    //省略其他代码
}

为什么要使用Handler?

在 Android 应用程序中,不能在非 UI 线程中直接更新界面,因为 Android 不支持跨线程更新界面。也就是说我们必须找到一种间接的方法来实现,在应用程序中常用的解决方法有以下几种:

  • Timer、 TimerTask :使用定时器 Timer 和 TimerTask 进行轮询,实现 UI 刷新。

  • AsyncTask :非常适合于轻量级的后台任务,并发数默认为一个。

  • Thread :使用普通线程或者线程池配合 Handler、 Message, Runnable,Timer 等类来更新 UI。

实际上,这些方法之间并不是完全割裂的,各个方法之间经常会相互搭配使用。

  • 在 Handler 的基础上,Android 还提供了 Looper 和 MessageQueue 来协同完成线程的消息传递工作。

如何使用Handler?

要实现在线程中更新 TextView 的文字,基本的实现思路如下:

  • 在子线程中新建一个 Handler。

  • 在子线程中开启轮询,执行子线程任务。

  • 在子线程任务执行完毕后,新建一个 Message 对象,并通过 Handler 将 Message 发送出去。

  • 在主线程中新建一个 Handler,并在其 handleMessage 方法中将 Message 对象取出。

  • 通过 Message 中的内容更新 UI。

代码如下:

private TextView textView;
private Handler mHandler; 
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    textView = (TextView)findViewById(R.id.textview);
    mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            textView.setText((String)msg.obj);
        }
    };
    new Thread(new Runnable() {
        @Override
        public void run() {
            // 子线程耗时操作
            Message msg = mHandler.obtainMessage();
            msg.obj = "子线程更新UI";
            mHandler.sendMessage(msg);
        }
    }).start();
}

Handler的常见使用方式

在 Android 应用程序中,将消息发送给 Handler 进行处理的方式有两种:

  1. post()方式

可以通过调用 Handler 的 post() 方法,将 Runnable 对象通过 MessageQueue 发送到消息队列中,即可让主线程处理相应的操作。这种方式可以用于解决在子线程中不能进行 UI 操作的问题,例如我们可以在子线程中通过 post 方式将更新 UI 的任务传递到主线程来完成,这样就不会因为在非 UI 线程中更新 UI 而导致 ANR(Application Not Responding)了。

示例代码如下:

new Thread(new Runnable() {
    @Override
    public void run() {
        //子线程中更新UI
        Handler mainHandler = new Handler(Looper.getMainLooper());
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                //在主线程中更新UI
            }
        });
    }
}).start();
  1. sendMessage()方式

前面提到了,sendMessage方法会将Message发送到消息队列的尾部并等待处理,而post方法则是将Runnable对象直接添加到消息队列中。sendMessage 和 post 的区别在于,sendMessage 会将传递的消息封装成 Message 对象,最后再发送到 MessageQueue 中,适用于需要传递参数并处理结果的情况。

常见的使用方式包括:

  • Handler.sendMessage(Message msg) 发送 Message 对象到消息队列。
  • Handler.sendMessageDelayed(Message msg, long delayMillis) 延迟指定时间后发送 Message 对象到消息队列。
  • Handler.sendMessageAtTime(Message msg, long uptimeMillis) 在指定时间后(毫秒)将 Message 对象加入到消息队列中。
  • Handler.sendEmptyMessage(int what) 发送一个不携带任何数据的空消息。
  • Handler.post(Runnable r) 将 Runnable 对象添加到消息队列中。

示例代码如下:

private Handler mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                //处理消息
                break;
            case 2:
                //处理消息
                break;
            default:
                break;
        }
    }
};

private void sendMessage() {
    Message msg = mHandler.obtainMessage();
    msg.what = 1;
    msg.arg1 = 2;
    msg.obj = "message content";
    mHandler.sendMessage(msg);
}

private void sendEmptyMessage() {
    mHandler.sendEmptyMessage(2);
}

private void postRunnable() {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            //执行操作
        }
    });
}

Handler的作用及其核心流程

Handler的常见用途包括:

  • 在子线程中执行任务后,通过Handler回到主线程更新UI。
  • 延迟执行任务或周期性执行任务。
  • 将多个耗时任务按顺序串行执行。

使用Handler的核心流程如下:

  1. 首先,创建一个Handler实例,并与当前线程的Looper关联。
Handler mHandler = new Handler(Looper.getMainLooper());
  1. Handler负责处理消息队列。要想从消息队列中获取消息,需要使用Handler的公共方法之一post()或sendMessage()。通常,它们的使用方式相似。
mHandler.post(new Runnable() {
    @Override
    public void run() {
        // UI更新代码
    }
});

或者:

Message message = Message.obtain();
message.what = MSG_ID;
message.arg1 = arg1;
message.arg2 = arg2;
mHandler.sendMessage(message);
  1. 当消息被加入到消息队列后,Handler会根据先进先出(FIFO)的原则将它们按顺序从队列中取出,然后调用associated Handler对象的handleMessage()方法处理它们。定制Handler子类的常见做法就是在handleMessage()方法内填充相应的处理逻辑。
@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        case MSG_ID:
            // 执行任务逻辑
            break;
        default:
            super.handleMessage(msg);
            break;
    }
}

Handler的基本用法

  1. 在主线程中使用Handler

在主线程中使用Handler,可以直接使用getMainLooper()获取主线程Looper对象,并创建Handler实例。例如,在Activity中实现在子线程中更新UI:

new Thread(new Runnable() {
    @Override
    public void run() {
        // 子线程中进行耗时操作...
        // 完成后在主线程中更新UI
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                // UI更新代码
            }
        });
    }
}).start();
  1. 在子线程中使用Handler

在子线程中更新UI,需要将当前线程的Looper对象关联给Handler实例,以便于在消息队列中执行此消息。

new Thread(new Runnable() {
    @Override
    public void run() {
         在子线程中进行耗时操作...
        Message message = mHandler.obtainMessage(MSG_ID, arg1, arg2);
        mHandler.sendMessage(message);
    }
}).start();

mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_ID:
                // UI更新代码
                return true;
            default:
                return false;
        }
    }
});
  1. Handler常见任务

3.1 延迟执行任务

使用Handler.postDelayed()方法可将消息延迟一段时间后再发送到消息队列中。例如:

mHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
        //执行延迟任务
    }
}, DELAY_MILLIS);

3.2 周期性执行任务

使用Handler.postDelayed()方法和Handler.sendMessage()方法结合,可实现周期性执行任务的功能。

mHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
        // 周期性执行任务
        mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_ID), INTERVAL);
    }
}, DELAY_MILLIS);

3.3 多任务按顺序执行

多任务按顺序串行运行,可以利用消息队列处理多个消息。

mHandler.sendMessage(Message.obtain(mHandler, TASK1_ID, arg1, arg2));
mHandler.sendMessage(Message.obtain(mHandler, TASK2_ID, arg1, arg2));
mHandler.sendMessage(Message.obtain(mHandler, TASK3_ID, arg1, arg2));

在Handler的handleMessage()方法中分别处理每个消息。

Handler调用方式的异同

使用Handler,有两种方法将消息加入消息队列中:post()方法和sendMessage()方法。两种方法的异同如下:

4.1 同异步方式

  • post()方法是同步方式。即加入消息到消息队列中后,会直接处理此消息,不必等待消息阻塞的处理程序返回。
  • sendMessage()方法是异步方式。即加入消息到消息队列中后,不会立即执行此消息,而是等待消息阻塞的处理程序返回。

4.2 方法参数

  • post()方法的参数是Runnable对象,而Runnable对象中run()方法是待执行的任务代码。
  • sendMessage()方法的参数是Message对象,而Message对象中的what字段是消息ID,arg1和arg2是可传递给处理程序的参数。

4.3 延迟时间参数

  • postDelayed()方法是将Runnable对象放入到消息队列中,延迟指定时间执行,其第二个参数为DelayMillis。而post()方法不支持延迟时间参数。
  • sendMessageDelayed()方法是将Message对象放入到消息队列中,延迟指定时间执行,其第二个参数为DelayMillis。而sendMessage()方法不支持延迟时间参数。

Handler的优缺点

5.1 优点

  • 提供了方便的UI线程更新的方法。
  • 避免了创建新线程带来的线程切换开销。
  • 通过消息队列的方式,提高了线程的响应能力。

5.2 缺点

  • Handler发送的消息会保存在消息队列中,如果一直发送大量的消息,将可能导致消息队列过长,影响应用的响应能力。
  • LiveData和RxJava等现在比较流行的框架,能够替代Handler实现更优异的异步编程和UI更新。

总结

Handler是Android系统的核心组件,在线程与消息传递中发挥着重要的作用。它可以帮助用户在不同的线程中实现消息的传递和任务的执行,并且提供了许多具有实际用途的方法,例如post()、sendMessage()、postDelayed()、sendMessageDelayed()等等。但是,Handler也有其缺点,比如常规使用可能会导致消息队列过度拥挤

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

老王学长

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

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

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

打赏作者

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

抵扣说明:

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

余额充值