前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Handler真的难?看完这篇文章你就懂了!

Handler真的难?看完这篇文章你就懂了!

作者头像
Rouse
发布2023-08-31 14:18:33
2310
发布2023-08-31 14:18:33
举报
文章被收录于专栏:Android补给站Android补给站

在Android开发中,Handler是一个非常重要的组件,它可以用来实现线程之间的通信和任务调度。本篇文章将介绍Handler的使用方式和原理,帮助读者更好地理解Android开发中的线程处理。

什么是Handler?

Handler是Android中的一个消息处理器,它可以接收并处理其他线程发来的消息。简单来说,Handler就是一个用来处理消息的工具类,它可以将消息发送给其他线程,也可以接收其他线程发送的消息进行处理。

Handler的使用方式

使用Handler的基本流程为:创建Handler对象 -> 发送消息 -> 处理消息。

在使用 Handler 之前,需要了解一些相关概念:

  • 线程:是独立运行的程序段,执行的代码是一个单独的任务。
  • 消息队列:是一种存储消息的数据结构,支持先进先出的队列操作。
  • Looper:可以让线程不停地从消息队列中取出消息并处理,是线程与消息队列交互的桥梁。
  • Message:是 Android 中处理消息的基本类,可以携带一些数据,用于在 Handler 中进行处理。

创建Handler对象

在使用Handler之前,需要先创建一个Handler对象。创建Handler对象的方式有两种:

在主线程中创建Handler对象:

在主线程中创建Handler对象非常简单,只需要在主线程中创建一个Handler对象即可:

代码语言:javascript
复制
Handler handler = new Handler();

在子线程中创建Handler对象:

在子线程中创建Handler对象需要先获取到主线程的Looper对象,然后使用Looper对象来创建Handler对象:

代码语言:javascript
复制
Handler handler = new Handler(Looper.getMainLooper());

发送消息

创建Handler对象之后,就可以使用它来发送消息了。发送消息的方式有两种:

使用Handler的post()方法:

使用Handler的post()方法可以将一个Runnable对象发送到Handler所在的消息队列中。Runnable对象中的代码会在Handler所在的线程中执行。

代码语言:javascript
复制
handler.post(new Runnable() {
    @Override
    public void run() {
        // 在Handler所在的线程中执行的代码
    }
});

使用Handler的sendMessage()方法:

使用Handler的sendMessage()方法可以将一个Message对象发送到Handler所在的消息队列中。Message对象中可以携带一些数据,用于在Handler中进行处理。

代码语言:javascript
复制
Message message = new Message();
message.what = 1;
message.obj = "Hello World!";
handler.sendMessage(message);

除了基本用法,Handler还有一些高级用法,下面列举了几个常用的:

  • 使用HandlerThread创建带有消息队列的线程,避免频繁地创建线程;
  • 使用Message.obtain()来获取Message对象,避免频繁地创建对象;
  • 使用Handler的sendEmptyMessage()方法来发送空消息。

处理消息

当其他线程发送消息到Handler所在的消息队列中时,Handler就会接收到这些消息并进行处理。处理消息的方式有两种:

重写Handler的handleMessage()方法:

重写Handler的handleMessage()方法可以处理其他线程发送的消息。handleMessage()方法中的代码会在Handler所在的线程中执行。

代码语言:javascript
复制
Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                String message = (String) msg.obj;
                // 处理消息的代码
                break;
            default:
                break;
        }
    }
};

实现Handler.Callback接口:

实现Handler.Callback接口可以处理其他线程发送的消息。Callback接口中的方法会在Handler所在的线程中执行。

代码语言:javascript
复制
Handler.Callback callback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                String message = (String) msg.obj;
                // 处理消息的代码
                break;
            default:
                break;
        }
        return true;
    }
};
Handler handler = new Handler(callback);

Handler的原理

在Handler的背后,实际上是使用了消息队列和线程通信的机制。当其他线程发送消息时,消息会被加入到Handler所在的消息队列中。然后,Handler会从消息队列中取出消息进行处理。

消息队列和Looper

消息队列和Looper是 Handler 实现的基础。每个线程都有一个消息队列(Message Queue),消息队列中存储着队列的所有消息(Message)。线程通过一个 Looper 来管理它的消息队列,通过不断地从消息队列中读取消息,实现了线程的消息循环 (Message Loop) 的功能。

代码语言:javascript
复制
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // might block
        // 一旦有消息,就会返回Message对象
        msg.target.dispatchMessage(msg);
    }
}

如上所示,一个线程中的消息无限循环直到队列里没有消息为止(MessageQueue.next())。消息通过 Message.target 属性来找到它想要执行的 Handler,从而被分配到正确的线程中并且得到执行。一旦有消息,就会调用 dispatchMessage(Message) 方法进行分发。

消息分发

消息分发是Handler 的核心部分,在它的内部逻辑中,也是最为关键的部分。

在 Handler 中,消息分发的流程如下:

1.1. 发送消息

由其他线程调用 Handler 的方法向消息队列中发送消息。

代码语言:javascript
复制
Handler handler = new Handler() ;
handler.post(new Runnable(){
    @Override
    public void run() {
        // 在其他线程发送消息
    }
});

1.2. 创建 Message 对象

将需要传输的数据封装成 Message 类型的对象,然后将该对象塞入消息队列中。

代码语言:javascript
复制
Message msg = new Message();
msg.obj = "消息内容";
handler.sendMessage(msg);

1.3. 将消息加入消息队列

Handler 将消息放入消息队列中。

在 Handler 内部,新构建的消息通过 enqueueMessage() 方法被加入到 MessageQueue 相应的内存块中,并且会在该内存块的标记 next 表示下一个内存块的索引号。

代码语言:javascript
复制
public void sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return;
    }
    msg.target = this;
    queue.enqueueMessage(msg, uptimeMillis);
}

enqueueMessage() 方法的核心逻辑,就是紧接着找到消息队列中最近的一个时间戳比当前时间小的消息,将新消息插入到这个消息之后。

代码语言:javascript
复制
boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        boolean needWake;
        if (mBlocked) {
            // If the queue is blocked, then we don't need to wake
            // any waiters since there can be no waiters.
            msg.markInUse();
            needWake = false;
        } else {
            msg.markInUse();
            needWake = mMessagesForQueue.enqueueMessage(msg, when);
        }

        if (needWake) {
            nativeWake(mPtr);
        }
    }

    return true;
}

1.4. Looper 开启消息循环

Looper 不断轮询内部 MessageQueue 中的消息,获取消息后在 Handler 中进行分发处理。

在 Looper 类中,强制让当前线程创建一个 Looper 对象,并通过调用 QualityLooper 构造函数 create 方法捕获该对象(一般用于构建线程的消息循环)。接下来,通过调用 run 方法被延迟1秒钟来启动上下文中的消息循环。

代码语言:javascript
复制
public static void prepare() {
    prepare(true);
}

public static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            continue;
        }
        msg.target.dispatchMessage(msg);
    }
}

这个方法是一个无限循环方法,在每个循环中,Looper 都会从自己的消息队列中获取一个消息,如果队列为空,则一直循环等待新的消息到来,直到被调用 quit() 方法,才终止循环。在获取到消息之后,调用 msg.target.dispatchMessage(msg) 进行消息的分发处理。

1.5. 查找目标 Handler

Looper 不断轮询消息队列,获取消息后,注意到 MessageQueue.next() 方法中有这样一行代码:

代码语言:javascript
复制
msg.target.dispatchMessage(msg);

1.6. 传递 Message 对象

从消息中获取到 target 属性,它就是当前这个Message对象所属的 Handler,并执行该Handler的 handleMessage(Message) 方法。

dispatchMessage(Message) 的核心代码是判断 Message.target 是否为 null,不为 null 则将消息传递给目标 Handler,如果为 null,则直接抛出异常。

代码语言:javascript
复制
void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        // 消息带有回调方法,如果 callback 不为空,那么就直接执行
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            // 尝试将消息抛给 mCallback
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg); // 如果消息中没有 callback,那就执行 handleMessage(msg)
    }
 }

// 处理具体的 Message
public void handleMessage(Message msg) {
    switch (msg.what) {
        // 根据消息类型分发处理
        default:
            break;
    }
}

当 Handler 接收到消息时,它会回调自己的 handleMessage(Message) 方法处理消息。

handleMessage(Message) 方法中,我们可以编写各种不同的逻辑,并对当前情况下的消息进行处理。这通常包括对消息类型的检查以及消息携带的数据的解析和操作。

当我们在 handleMessage(Message) 方法中完成了所有处理后,我们就可以将数据发送回发送消息的线程,或将数据传递给其他线程进行进一步处理。

总结

本篇文章深入探讨了 Handler 的原理,主要包括了消息队列和 Looper 的相关概念,以及消息的发送和处理。除此之外,还讲了当不同线程的消息需要在 Handler 中处理时,需要用到 Looper、MessageQueue 和 Handler 这三个关键组件的协同工作。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android补给站 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是Handler?
  • Handler的使用方式
    • 创建Handler对象
      • 发送消息
        • 处理消息
        • Handler的原理
          • 消息队列和Looper
            • 消息分发
            • 总结
            相关产品与服务
            消息队列
            腾讯云消息队列 TDMQ 是分布式架构中的重要组件,提供异步通信的基础能力,通过应用解耦降低系统复杂度,提升系统可用性和可扩展性。TDMQ 产品系列提供丰富的产品形态,包含 CKafka、RocketMQ、RabbitMQ、Pulsar、CMQ 五大产品,覆盖在线和离线场景,满足金融、互联网、教育、物流、能源等不同行业和场景的需求。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档