Android 消息处理机制

Android 消息处理机制

在 Android 中,主线程不可以做耗时操作,不然就会发生 ANR 异常,所以耗时操作只能在子线程中执行。在子线程执行完的结果,想要通知主线程进行更新或者进行某些操作呢?那么就要用到 Android 的异步消息机制,消息处理机制可以实现线程之间的交互。

Android消息处理机制的工作原理

消息处理的原理就,一个死循环线程不断获取消息队列里面消息,在子线程中可以通过handler将要交互信息插入到队列中,轮训到了消息之后,获取队列里面的消息,然后将消息发送到 handler 进行处理。

Looper、Handler、MessageQueue,Message

Looper:轮询器,会通过死循环线程不断轮训队列里面的消息。

Handler:发送跟处理消息。

MessageQueue:消息队列。

Message:消息实体

源码分析

要看消息机制的源码,我们必须从我们的老朋友 ActivityThread 的 mai 函数看起。

  public static void main(String[] args) {

    .....
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

首先看下 Looper.prepareMainLooper()

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

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

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

通过 sThreadLocal 获取了一个 Loope r的实力,然后赋值给 sMainLooper。那么这个 ThreadLocal 是什么呢?

ThreadLocal作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。简单来说,就是hreadLocal在某个线程里面存放的数据只能在某个线程里面获取。比如你在主线程里面存储了Looper,那么在主线程就能获取到主线程的Looper,在其他线程是无法获取到这个数据的。

在主线程中,prepare方法在ThreadLocal中存储了Looper,通过myLooper()获取这个Looper,并且进行赋值。

接下来看看

 ActivityThread thread = new ActivityThread();
 thread.attach(false);

ActivityThread 代码非常长,这里就不细讲,大致描述一下。ActivityThread不要名字迷惑这里,它不是线程。 它是一个调度调配 activitie,broadcasts,ContentProvider还有一些资源的管理类。在attach()方法做得最主要两件事就是将ApplicationThread 放到了RuntimeInit类中的静态变量mApplicationObject中,还有就是将ApplicationThread 作为参数传递给了ActivityManagerService,让ActivityManagerService可以通过ApplicationThread的接口管理Activity。

接下来看

Looper.loop();

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

    ...
        try {
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

       ...

    }
}   

此时主要完成三件事

获取Looper,进行轮训获取消息,然后分发消息。

  • 获取Looper。通过myLooper() 获取之前存储的Looper。

    final Looper me = myLooper();
    
  • 进行轮训获取消息。

    通过for (;;) 让线程进入死循环,达到轮训的效果。通过 Message msg = queue.next() 获取队列的消息。如果没有消息获取则进入阻塞状态。直到有消息为止。

  • 分发消息

    msg.target.dispatchMessage(msg) 通过handler分发消息。艾,不对,这里哪有handler,明明就是msg.target。其实呢,这里的msg.target就是handler。他是哪里赋值的呢?下面会讲。

总结一下:

我们在主线程创建了Looer之后,通过ThreadLocal,存储了Looper,这样我们在其他主线程获取到的Looper都是唯一的。接着,我们创建了ActivityThread,在ActivityThread主要事情就是ApplicationThread传递给RuntimeInit跟ActivityManagerService,让客户端跟底层可以互相交互。最后,在Looper开启了轮训,不断获取消息,然后进行分发,没有消息就处于阻塞状态。

Handler发送消息跟处理消息

那么Handler如何发送消息跟接受消息的呢?

  • handler发送消息

我们先来看下Handler的构造函数

public Handler(Callback callback, boolean async) {

    ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    ...
}

获取了Looper,并且获取了队列。 “Can’t create handler inside thread that has not called Looper.prepare()” 这个大家一定很熟悉,如果我们在子线程中创建了handler的话,就产生这个异常,那就是就是我们在线程中没有Looer。

接下来我们看下enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 。你可能会好奇,为什么不好sendEmptyMessage,你跟下源码就知道,sendEmptyMessage,最后调用还是enqueueMessage

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

msg.target = this ,这就解释我们之前在Looper.loop() 里面msg.target.dispatchMessage(msg) 为什么是handler发送的。这里发送消息就是将消息放入了消息队列。

  • 接受消息

Looper轮训到新的消息就会 执行msg.target.dispatchMessage(msg)。

我们先来看下dispatchMessage()

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


public void handleMessage(Message msg) {

}

分发消息完,就是执行handleMessage(Message msg),这个方法是一个空实现,在平时使用的时候,我们都是复写这个方法,然后进行接受消息。

到这里,关于消息机制的全部分析完毕。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值