Android 消息机制原理及源码学习

2017-11-30  本文已影响0人  JokerHerry

Handler是我们常用的东西,为什么呢?因为他可以将我们所需要的反馈事件切换到handler所在的线程中运行,从而实现在子线程中实现耗时间的操作,然后将返回的结果通知到主线程,然后实现更新UI的过程。今天我们就从实现原理以及源码的角度分析一下,Android的消息机制,看看handler是怎样实现线程切换的。

消息机制的实现

基本实现方法

我们首先来实现一个最基本的通过handler的消息传递。

public void showDialog(String str) {
    Message message = new Message();
    message.what = START_DIALOG;
    Bundle bundle = new Bundle();
    bundle.putString("str",str);
    message.setData(bundle);
    handler.sendMessage(message);
}

Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case STOP_DIALOG:
                    DialogUtil.getIntance().closeDialog();
                    break;
                case START_DIALOG:
                    DialogUtil.getIntance().waitDialog(BaseActivity.this).show();
                    break;
                default:
                    break;
            }
        }
    };

在实例中,我们通过实现了一个handler,然后通过调用showDialog,达到显示dialog的目的。

但是在完成这一过程中,不仅仅只是一个Handler就完成了所有的操作,而且handler只是一个用于帮助我们快速时间的工具。

消息机制组成元素

消息的传递我们一共可以分为3个部分,Handler,MessageQueue,Looper。
Messag只是起我们作为传递过程中,传递参数的作用。

Message:线程之间的消息传递的信息载体,存放少量信息。可以传递的信息有int(what arg1 arg2) Object (obj).

Handler:事件的发出者和处理者,消息机制的辅助处理类。主要通过Handler.sendMessage()发出信息,和Handler.handleMessage()处理信息。

MessageQueue:一个实现了先进先出效果的单链表,并不是我们所谓的队列数据结构。主要用于存放所有通过Handler.sendemessage发送的消息。即向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)。会一直存在消息队列中,等待被处理。每个线程只有一个MessageQueue对象。

Looper:在(Looper.loop)中不断循环执行,从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。即将事件交给所对应的Handler处理。

然后我们就可以理解到,消息传递就是将消息先放到每个线程中的MessageQueue中,然后通过Looper将MessageQueue中的Message发送到所对应的Handler中去。让Handler去处理这些事件。

从而达到将一个线程中的事件传递到Handler所在的线程中去。

消息机制模型

然后我们再用图解的方式,看一下之间的实现关系。


消息机制流程

我们按照流程图,一步一步分析消息的传递。
首先在新线程中,我们实现了执行了耗时间操作,现在需要通知主线程更改UI。

注意:
1.每个线程中只有一个Looper实例。而MessageQueue是在Looper中实例的,所以MessageQueue也只有一个。
2.实际上每个线程中的Looper都是要我们自己实现的,只不过因为在Activity中,在ActivityThread中就已经帮我们Looper.prepareMainLooper();Looper.loop();了,所以我们才能直接使用Handler,但在新开的线程中,我们就要自己通过这两句代码开始新线程中的Looper。

以上是对消息机制的实现以及消息传递的基本流程。下面我们将通过源码的方式来理解内部的实现原理。

源码以及理解

我们还是按照上面的流程图,一个个分析。


消息机制流程
发送消息
    //申明默认的Handler
    public Handler() {
        this(null, false);
    }
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
        ......
        //必须先执行Looper.prepare(),才能获取Looper对象,否则为null.
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
       
        mQueue = mLooper.mQueue;   //从这我们看出MessageQueue是在Looper中实例的
        mCallback = callback;      //回调方法
        mAsynchronous = async;     //设置消息是否为异步处理方式
    }

但不管是send还是post发送信息,最终都会调用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);
    }

并且就连子线程中调用Activity中的runOnUiThread()中更新UI,其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。

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

在看看sendMessageAtTime最后调用的enqueueMessage

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    
        //target就是发送message的那个handler的引用
        msg.target = this;
        if (mAsynchronous) {
            //是否异步执行
            msg.setAsynchronous(true);
        }
        //将消息放置在消息队列中
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看出,Handler通过对Looper中的MessageQueue的引用,将得到的信息放到消息队列中。

传递消息

MessageQueue

    boolean enqueueMessage(Message msg, long when) {
        //target就是发送message的那个handler的引用
        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.");
        }

        synchronized (this) {
            if (mQuitting) {    //正在退出时,回收msg,加入到消息池
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                //p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
            //将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
            //消息队头存在barrier,并且同时Message是队列中最早的异步消息。
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
获取消息

分发消息
上一篇下一篇

猜你喜欢

热点阅读