Android 消息机制

Message

Message 是线程之间传递的消息,可以携带少量数据,用于在不同线程之间交换数据。

public 字段含义注释
int what用户定义的消息标记
int arg1内置可存放的第一个整型数据
int arg2内置可存放的第二个整型数据
Object obj内置可存放的一个对象数据
Messenger replyTo可回应的消息的发信人

构造方法推荐使用 Message.obtain(),因为静态工厂有一个可重复利用的对象池。

MessageQueue

消息队列:主要用于存放所有通过 Handler 发送的消息,消息会一直存在于消息队列中,等待被处理。每个线程只会有一个消息队列。

底层为单链表,主要包含两个操作:

  1. enqueueMessage(Message,long):插入消息和执行时间。设置消息的 when 为输入的 long 参数,根据执行时间将消息存放在单链表的合适位置。
  2. next():无限循环的方法,如果队列中没有消息,就一直阻塞,当新消息到来时,就返回这条消息并从单链表中删除。

Looper

每个线程中的消息队列的管家,Looper 的 loop() 方法会进入无限循环中,每当发现消息队列中存在一个消息,就会将它取出,最终传递到 Handler 的 handleMessage 方法中。

static public 方法含义
prepare()为当前线程创建一个 Looper 对象
loop()当前线程的 Looper 开启消息循环

Looper 的 loop() 方法是一个死循环,从 MessageQueuenext() 方法中获取新消息,没有就一直阻塞,唯一跳出循环的方式是 MessageQueue 的 next() 方法返回 null。当 Looper 的 quitquitSafely 方法被调用时, Looper 就会调用 MessageQueue 的 quit 或 quitSafely 方法,使得 MessageQueue 的 next 方法返回 null。

获取消息后,Looper 就会处理这条消息:msg.tartget.dispatchMessage(msg)。这样 Handler 发送的消息最终又交到它的 dispatchMessage 方法。

在子线程中调用 Toast 时,会报错 Can't toast on a thread that has not called Looper.prepare()

解决办法:

Looper.prepare(); // 为当前线程创建一个Looper
Toast.makeText(Page1Activity.this, "content", Toast.LENGTH_SHORT).show();
Looper.loop(); // 开启消息循环

Looper 的 prerare 方法,会通过 ThreadLocal 来检查当前线程是否已经有 Looper 对象了,有的话就会抛出异常,因为一个线程只能有一个 Looper 对象,没有的话就使用 ThreadLocal 给当前的线程添加一个 Looper 对象。

private 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));
}

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Looper 的 loop() 方法,会使用当前线程的 Looper 对象来开启消息循环。

Handler 发送消息原理

Handler 是消息处理器,主要用于发送和处理消息。

public 方法含义
Message obtainMessage()返回一个消息对象,设置其 target 为当前 Handler 实例
sendMessage(Message msg)用此方法,将其他线程消息的消息发送给 Handler
handleMessage(Message msg)在创建 Handler 的线程中处理收到的消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

发送消息通过 send 的一系列方法实现,Handler 的 sendMessage 方法会调用 enqueueMessage(queue, msg, uptimeMillis) 方法,设置消息的 target 为当前 Handler 对象,然后将消息插入到消息队列中。

MessageQueue 的 next 方法返回这个消息给 Looper,最终由Handler 的 dispatchMessage 方法来处理。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

dispatchMessage 方法首先检查消息是否定义了回调函数 callback,定义了的话就调用 handleCallback(msg),否则再检查 Handler 是否定义 mCallback,定义了的话就调用 mCallback.handleMessage(msg),没有定义 mCallback 或调用 mCallback.handleMessage(msg) 失败才会调用 Handler 的 handleMessage(msg)

优先级消息处理函数
1msg.callback.run() 来处理
2Handler 的 mCallback.handleMessage(msg)
3Handler 的 handleMessage(msg)

runOnUiThread 实现原理

在非 UI 线程中执行此方法时,会用消息机制来处理 Runnable 对象。Runnable 对象会作为消息的 callback 字段被包装成一个消息 msg,相当于执行了 mHandler.sendMessage(msg)

// android.app.Activity
@Override
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

// android.os.Handler
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

活动的主线程是 ActivityThread,mHandler 是每个 Activity 在主线程中创建的消息处理器,因此 Runnable 最终在主线程中运行。

// android.app.Activity
final Handler mHandler = new Handler();


参考资料

  1. 《Android 开发艺术探索》 任玉刚著
  2. 《第一行代码 Andriod》 郭霖著
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值