Android 消息机制

本文深入探讨了Android中的消息机制,包括Handler、Message、MessageQueue、ThreadLocal和Looper的工作原理。Handler用于线程间通信,避免主线程阻塞,Message作为数据载体,MessageQueue管理消息队列,Looper不断从队列中取出消息并分发。文章还解答了子线程能否创建Handler及Handler与MessageQueue的关系等常见问题。
摘要由CSDN通过智能技术生成

一. 背景

​ 在开发中我们常会有这种需求:进入一个界面,请求一个接口,然后将接口的数据更新至UI上显示。在Android中,为了避免阻塞主线程,网络请求这种耗时操作需要放在子线程中,但在Android中,UI控件不是线程安全的,系统不允许在子线程中去更新UI。

​ 因此现在的情况是,网络请求需要在子线程中,但是UI的更新需要放在主线程中,那么要如何在子线程获取接口数据后,通知主线更新UI呢?由此引出本文的主角 Handler

二. 介绍

Handler主要用于线程之间的通信问题。

以背景中的问题为例子,界面中在子线程中请求网络数据,然后在子线程中更新UI。

首先在主线程中创建一个Handler,并且执行收到消息的操作

 private Handler mHandler = new Handler() {
   
        @Override
        public void handleMessage(@NonNull Message msg) {
   
            //收到消息后的操作

            switch (msg.what) {
   
                case MSG_UPDATE_TV:
                    mTv.setText(msg.obj.toString());
                    break;
                default:
                    break;
            }
        }
    };

开启一个子线程模拟网络耗时操作,获取数据后通过Handler将消息发送至主线程

new Thread(new Runnable() {
   
  @Override
  public void run() {
   
    // 模拟网络请求
    SystemClock.sleep(3000);

    Message message = Message.obtain();
    message.what = MSG_UPDATE_TV;
    message.obj = "接口数据";
    mHandler.sendMessage(message);
  }
}).start();

看到这里想必有同学心中就有个疑问了,为什么通过Handler可以将数据从子线程中发送至主线程中?

三. 源码分析

要分析Handler的原理,就要介绍一下与它息息相关的几个类

职责
Handler 用于发送与处理消息
Message 线程间通信消息的载体
MessageQueue 消息队列
Looper 用于不断的从队列中取消息,然后进行分发
ThreadLocal 存储线程作用域的局部变量
3.1 Message

通过Handler实现线程间的数据传输,首先要依靠Message,顾名思义,也就是消息的载体。

Message message = Message.obtain();
message.what = MSG_UPDATE_TV;
message.obj = "接口数据";

Message用于携带线程间通信的一些数据

public final class Message implements Parcelable {
   
    
    // 一般用于标识消息类型
    public int what;

    // 传递int类型数据
    public int arg1;

    // 传递int类型数据
    public int arg2;

    // 传递普通对象,
    public Object obj;

    // Bundle数据
    Bundle data;

    // 保存发送当前消息的Handler,也是处理该消息的Handler
    Handler target;
    ...
}

关于Message对象的创建,不建议使用new直接创建Message对象,系统存在Message的消息池,可实现消息对象的复用,避免大量创建消息对象带来的开销问题。

  • 消息以链表的方式组成消息池
  • 消息池存在消息的最大缓存个数,最大消息个数为50个
  • 每次从消息池中获取消息时,若消息池中不存在消息,则新建消息,否则取出消息链表表头
  • 回收消息时,若当前消息池中消息缓存个数小于最大消息数,则将消息加入消息池中
public final class Message implements Parcelable {
   
    //消息池以链表的形式存储
    Message next;

    public static final Object sPoolSync = new Object();
    private static Message sPool;
    //当前消息池内存在的消息数
    private static int sPoolSize = 0;

    //消息池中最大的消息数
    private static final int MAX_POOL_SIZE = 50;

    //获取消息
    public static Message obtain() {
   
        synchronized (sPoolSync) {
   
            if (sPool != null) {
   
                //消息池中存在消息,取出消息头
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    // 消息回收
    void recycleUnchecked() {
   
        ... 
        synchronized (sPoolSync) {
   
            if (sPoolSize < MAX_POOL_SIZE) {
   
                //回收消息时,若当前消息池内数目小于最大数,则将消息放入消息池
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
}
3.2 Handler

Handler的Api可以分成三类:发送消息、处理消息、移除消息

  • 发送消息

    发送的数据可以是Runnable或者其他的数据类型,无论发送什么数据,最终数据会被封装成Messages对象

    调用enqueueMessage将消息加入到消息队列中。

  • 处理消息

    消息最终由发送该Message的Handler进行处理(待会会分析),消息的处理调用Handler的dispatchMessage方法,该方法中执行了处理消息的优先级,优先级从高到底为使用postXX发送的Runnable消息 > 在Handler构造方法传入的Callback消息体 > 使用sendXXMessage发送的消息

  • 移除消息

    通过removeXX来移除消息队列中未处理的Message,注意只能是还未进行处理的消息

public class Handler {
   


    public Handler(@Nullable Callback callback, boolean async) {
   
        if (FIND_POTENTIAL_LEAKS) {
   
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
   
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        // 若创建Handler时looper不存在,则会发生崩溃,这也是在子线程中不能直接创建Hander的原因
        if (mLooper == null) {
   
            throw new RuntimeException(
                "Can't create handler inside thread " 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值