Android消息机制 & Android线程间通信机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chenbaige/article/details/79443372

前言:由于Android系统本身决定了其自身的单线程模型结构。在日常的开发过程中,我们又不能把所有的工作都交给主线程去处理(会造成UI卡顿现象)。因此,适当的创建子线程去处理一些耗时任务是非常关键的。同时Android中非UI线程不能对UI组件进行操作,因此,熟练的掌握并应用线程间消息通信是很有必要的。接下来,我们从Android线程间通信机制和Android消息机制两个方面对以上内容进行介绍。

一.Android线程间通信机制

Android的线程间通信主要是在非UI线程对UI线程的消息传递,并且修改UI界面的操作。Android中常见的子线程更新UI的方式:

  1. handler.post(Runnable r)
  2. runOnUiThread(Runnable r)
  3. view.post(Runnable r)
  4. Handler+Message+MessageQueue+Looper

接下来我们依次看一下这几种调用方法的内部实现原理。

- handler.post(Runnable r)内部实现原理:

//Handler.post()方法:
 public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
//Handler.sendMessageDelayed()方法:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
//Handler.sendMessageAtTime()方法:
 public boolean 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 false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

调用链:Handler.post->sendMessageDelayed->sendMessageAtTime
从上面的调用链可以看出,Handler.post方法最终调用了Handler.sendMessageAtTime方法,而通过sendMessageAtTime的实现逻辑可以看出,最终还是通过enqueueMessage方法将Message插入到消息队列,进行轮询处理。因此,底层还是Handler消息机制。

- runOnUiThread(Runnable r)内部实现原理:

 public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

runOnUiThread内部实现机制到这里基本就可以结束了,因为它的实现原理一目了然。简单的判断当前线程是否是UI线程,如果是,就直接执行Runnable方法。如果不是,就通过Handler.post方法处理这个Runnable。因此,底层还是Handler消息机制。。

- view.post(Runnable r)内部实现原理

public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

和runOnUiThread内部实现相似,view.post方法也是通过Handler.post方法处理这个Runnable。

从上面的分析可以看出,所有的自线程更新UI组件的方式都是依靠Handler消息机制实现的。因此,接下来我们将详细介绍Android的消息机制-Handler消息机制。

二.Android消息机制(Handler消息机制)

Handler消息机制的运行需要底层的MessageQueue和Looper来支撑,它的主要作用是将一个任务放到某个指定的线程中去执行。接下来我们详细介绍Handler、Message、MessageQueue、Looper的作用和联合工作机制。

MessageQueue:中文意思是消息队列。顾名思义,它的内部存储了一组消息,以队列的形式对外提供添加和删除操作。可能很多小伙伴从它的字面意思,就认为它就是一个队列的数据结构。其实并不是,它的内部是采用单链表的数据结构来存储消息列表,最初的存在形式就是一个空的头节点(mMessage)。

Message:中文含义为消息。很明显,它就是我们上面说到的MessageQueue中存储的实体。上面说到,MessageQueue是以单向链表的形式存储数据,每一个Message都是其中的一个节点。因此开始时,队列为空,即没有需要处理的消息。当有消息到来时,就添加节点到队列尾部(添加新节点到链表尾部,记录Next域)。
Message数据结构:

public final class Message implements Parcelable {

    public int what;

    public int arg1;

    public int arg2;

    public Object obj;

    public Messenger replyTo;

    //记录下一个节点域
    android.os.Message next;
}

上面的Message结构只是简单罗列了一些我们常用的字段,其中实现链表最关键的字段就是next字段,它是实现消息队列的核心。我们创建Message对象有多种方式,但是建议通过Message的obtain方法去获取Message对象,而不是直接创建新的Message对象。想了解更多关于obtain方法内部实现原理,请移步:Handler消息机制之深入理解Message.obtain()

Looper:中文含义为循环,在这里即为消息循环。由于MessageQueue只是一个消息的存储单元,它不能去处理消息,而Looper正好弥补了这个缺陷。Looper会以无限循环的方式去查找是否有新的消息,如果有消息,就取出来处理,否则就一直等待。除此之外,它还有一个特殊的概念:ThreadLocal(存储和获取当前线程的Looper),在这里不作过多阐述,想要了解ThreadLocal是如何实现线程私有化存储机制的,可以移步 ThreadLocal内部实现机制详解 查看详情。
Handler:主要功能是切换到指定线程去处理Looper轮询查找到的新消息。

Looper工作原理:

Looper是消息机制中的消息循环角色,它会不断轮询查找MessageQueue中是否有新的消息需要处理。一旦发现需要处理的新消息,就立刻取出消息交给Handler进行处理;否则就一直阻塞在那里。

我们都知道,Looper、Thread、MessageQueue一定是绑定在一起的,因为他的作用就是轮询处理当前线程的消息队列,从它的构造器就可以看出:

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

Looper类常用方法:

  • Looper.prepare()方法:为当前线程创建一个Looper
//判断当前线程是否有Looper,没有就创建一个Looper,并且放到当前线程的ThreadLocal中。
//ThreadLocal:每个线程都有,并且是线程私有的,用于存储线程私有数据
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));
    }
  • Looper.prepareMainLooper()方法:和prepare方法类似,只是它专用于给主线程创建Looper。

  • Looper.loop()方法:用于消息轮询,以下是loop方法部分重要代码解析。

 public static void loop() {
        //获取线程的Looper对象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获取Looper需要轮训的消息队列
        final MessageQueue queue = me.mQueue;
        //开启无限的消息轮询(死循环),唯一跳出循环的方法就是下面msg为空,即MessageQueue调用quit方法退出 Looper。当消息队列被标记为退出状态时,next会返回null。
        for (;;) {
            //取出需要处理的消息,next是一个阻塞方法,当获取不到要处理的消息时,就会阻塞在这里。当消息队列标记为退出状态是时,不会阻塞,会返回null。接下来,判断到msg为null,就会退出循环。
            Message msg = queue.next(); // might block
            //如果上面next方法没有阻塞,msg就不可能为空。只有在Looper退出后(调用quit方法),msg才会为空。
            if (msg == null) {
                return;
            }

            try {
                //msg.target其实是一个Handler对象,这句代码就是处理消息的关键代码,将Message交给Handler去处理
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //参数复位操作
            msg.recycleUnchecked();
        }
    }
  • Looper.quit()方法:退出Looper方法,无论消息是否处理完,直接退出。当Looper退出后,Handler发送消息会失败。
  • Looper.quitSafely()方法:退出Looper方法,要等所有消息处理完后,才会退出。

Handler工作原理:
简介:Handler的主要任务是发送一条消息和获取并处理一条消息。接下来我们总结一下Handler发送消息和处理消息的一些常用方法,并且分析其实现原理。

  • Handler.sendMessage(Message msg)方法:下面是sendMessage方法调用逻辑链。
 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

在sendMessage内部调用sendMessageDelayed方法:

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

在sendMessageDelayed内部调用sendMessageAtTime方法:

public boolean 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 false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

通过sendMessageAtTime方法逻辑可以看出,最终还是调用enqueueMessage方法将Message插入到消息队列。接下来的逻辑就不用多说了吧。Looper轮询就该上场了…

Handler调用链:sendMessage->sendMessageDelayed->sendMessageAtTime->Looper轮询Handler处理消息。

  • Handler.post(Runnable r)方法:post方法上面已经详细分析了,调用的也是sendMessageDelayed方法去处理Runnable中的Message。

    上面是两个重要的发送消息的方法,接下来看一下当Looper轮询检测到新消息时的处理方法。上面提到,当Looper调用loop方法轮询的时候,检测到新消息会有这么一行代码:msg.target.dispatchMessage(msg),这就是消息分发处理的方法,下面重点讲解这个方法。

    Handler.dispatchMessage(Message msg)方法:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
其中的msg.callback实在创建Message的时候声明的,声明方式如下:
//在创建Message的时候匿名创建的runable对象,一般用不到
  Message message = Message.obtain(handler, new Runnable() {
            @Override
            public void run() {

            }
        });
  • msg.callback不为空的情况:首先检测Message的callback是否为null,不为null就通过handleCallback方法处理消息。msg.callback是一个runable对象,就是我们通过post方法传递进来的那个runable。
//直接调用Runable的run方法,处理逻辑。即调用上面创建Message时自己实现的run方法
private static void handleCallback(Message message) {
        message.callback.run();
    }
  • msg.callback为空的情况:检测mCallback是否为null。不为null,就调用mCallback的handleMessage方法处理这条消息;当mCallback为null的时候,直接调用Handler中的handleMessage方法处理消息。其实Handler中的handleMessage方法是一个空实现的方法体,也就是说当mCallback也为null的时候,就不对这条消息进行处理了。接下来再说略显复杂的mCallback不为空的情况。

    mCallback是一个接口,看接口里面的声明方法public boolean handleMessage(Message msg),这不就是我们创建Handler的时候处理消息的方法嘛。别急,看一下在哪里给mCallback赋值的。没错,就是在Handler的构造器里面对它进行的赋值操作。因此,mCallback也是我们在创建Handler的时候自己定义的,处理消息的逻辑也就是我们自定义实现的。

 //这就是我们创建Handler对象时常用方法,这里匿名创建的Callback就是上面提到的Handler中的mCallback。
 Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                //处理Message
                return false;
            }
        });

总结:Handler发送消息和处理消息的方法都介绍完了,想必大家都很清楚其中的实现思路了。最后,我们总结一下整个Handler消息处理机制的流程:

  1. 通过handler.post(runable)或handler.sendMessage(msg)方法发送消息。(其中调用sendMessageDelayed->sendMessageAtTime等一系列方法)。
  2. 通过上面的方法,将Message添加至MessageQueue消息队列。
  3. Looper通过loop方法对MessageQueue进行轮询,没有新消息就阻塞。
  4. 检测到有新消息,调用dispatchMessage(msg)方法处理消息。
  5. 调用message.callback.run()或mCallback.handleMessage()方法完成对消息的处理。

结语:希望通过本文的分析,让小伙伴们对Handler消息处理机制能有更深刻的理解。如有不对的地方,望大家指出;如果对您有帮助,望多多支持。

猜你喜欢

转载自blog.csdn.net/chenbaige/article/details/79443372