美文网首页
Android 消息机制源码解读

Android 消息机制源码解读

作者: 梦飞成2012 | 来源:发表于2017-12-06 21:05 被阅读6次

Android消息机制,对每个开发来说,都不可避免的和它打交道,比如在子线程中更新UI、倒计时功能等等都需要使用到消息。
从开发的角度来讲,Handler是Android消息机制的上层接口,我们只需要和Handler打交道就足够了。
Handler的使用:

// 处理消息
Handler handler = new Handler(){
            @overider
            public void handleMessage(Message msg) {
                    // do somethings you want to do!
                    super.handleMessage(msg);
            }
} 

// 发送消息
Message msg = Message.obtain();
msg.seData(bundle);
msg.setTarget(mHandler);
msg.sendToTarget();
// 或者使用mHandler.sendMessage(msg);  

Handler的使用很简单,我们就不在此举例赘述了。但是想更娴熟的使用Handler,驾驭消息机制,我们还需要从源码层面来讲整个机制的运作,读懂Handler和Looper、MessageQueue、Message的关系。
请看下图:

消息机制的闭环.png

下面我们从源码一步步的分析消息运行机制:

一、Handler:

1.1Handler的构造方法

handler的构造方法如下:

 public Handler() {
        this(null, false);
    }

  
    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {
        this(looper, null, false);
    }


    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }


    public Handler(boolean async) {
        this(null, async);
    }

    public Handler(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();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

从Handler的构造方法,我们可能看出,Handler里面保存了Looper、MessageQueue的引用。

1.2消息的发送

发送消息有两种方式:

  • 1、msg.sendToTarget();
  • 2、mHandler.sendMessage(msg);

先看1方式:

public void sendToTarget() {
    target.sendMessage(this);
}

最终还是调用的handler的sendMessage方法。
再看2方式都干了什么事情。

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}

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

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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //将要发送的Message绑上该Handler的标签
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //将消息将入Handler构造函数中初始化的MessageQueue队列
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看到sendMessage就是做了一个入队列的操作(即enqueueMessage)
消息发送完成 ,接下来我们看看消息是如何取出来的,再看这个之前,我们先看看Looper的相关设置:

二、Looper:

Looper,在消息机制中扮演者消息处理角色,即从MessageQueue中不断的取消息,然后分配给对应的Handler去handleMessage。

2.1 Looper的构造方法
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

这是一个私有的构造方法。我们可看到,在构造方法中,每个Looper都和一个MessageQueue绑定。

Looper里面的主要引用如下:

  • static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  • private static Looper sMainLooper; // guarded by Looper.class
  • final MessageQueue mQueue;
  • final Thread mThread;
    这几个成员变量比较好理解,MessageQueue是Looper操作的消息队列,mThread是当前线程的对象,sMainLooper是主线程的Looper对象引用。
    sThreadLocal 这个对象比较特殊,主要作用将Looper和线程关联,这样外部就可以直接通过Looper拿到当前线程的Looper对象了,这样可以保证Handler创建的时候与当前线程的Looper关联。
    关于 ThreadLocal<T>的简介和用法可以看这篇文章Android 多线程系列----ThreadLocal的使用和原理
2.2 Looper的获取

Looper没有public的构造方法,只能使用Looper.myLooper来获取实例,在获取对象前需要调用Looper.prepare()方法。(注:在UI线程,ActivityThread已经帮忙做了这些事情了,所以我们创建Handler不需要操作Looper)
下面看Looper的重要方法的源码:


public static void prepare() {
    prepare(true);
}
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
     //Looper类会创建一个新的Looper对象,并放入全局的sThreadLocal中。
    sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
public static Looper myLooper() {
    return sThreadLocal.get();
}

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

public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

通过上面代码,我们可以得到以下结论:

  • 1、每个线程中,只能有一个Looper,因为多次调用Prepare会报异常
  • 2、每个线程也只要一个MessageQueue,一个线程中,所有的消息公用一个队里
  • 3、Looper.myLooper提供给外部实例,通过ThreadLocal实现的
2.3Looper的loop,获取消息

消息循环的启动,需要调用Looper的loop方法。
这个方法的作用就是不断的从MessageQueue中获取消息,然后处理,我们先看看源码:

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    //获取当前线程的Looper对象实例
    final Looper me = myLooper();
    //调用loop()方法之前必须在当前线程通过Looper.prepare()方法创建Looper对象实例。
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //获取Looper实例的中的消息队列
    final MessageQueue queue = me.mQueue;
    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    //开启循环队列-不断
    for (;;) {
        //从队列中取出消息
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        //将消息分发给注册它的handler
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }
        msg.recycleUnchecked();
    }
}

通过上面方法,我们看到,loop方法启动了一个死循环,不断通过MessageQueue的next方法(该方法是阻塞的),不断获取消息,然后通过Message的target调用dispatchMessage分发处理消息,然后回收消息对象。

三、消息的处理:

Handler的dispatchMessage方法源码:

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

可以看到消息分发处理很简单:

  • 1、如果Message有callback则调用Message的Callback
  • 2、否则,如果Handler创建时候设置了Callback,调用Callback的handleMessage,如果返回true结束
  • 3、否则, 调用Handler的handleMessage方法,完成消息的处理。

四、消息机制的闭环实现:

消息机制实现,有一些前提,如下:

  • 1、线程直接内存是共享的(不同线程可以使用Hanler发消息,加入消息队列)

  • 2、每个线程只有一个Looper和MessageQueue

  • 3、Handler和Looper绑定,保证了不论哪个线程,Handler只会向一个指定对了发送消息,在取消息的时候,处理也就只会在当前Looper线程了。

  • 1、创建Looper和Message对象

  • 2、创建Handler对象,通过Looper.loop启动消息循环

  • 3、Handler在子线程发送消息,将消息加入到MessageQueue

  • 4、Looper在loop方法中(主线程),取出消息并处理,完成了消息在不同线程间的切换。

消息机制的闭环可以如下总结:

消息机制的闭环.png

参考资料:

1、又一年对Android消息机制(Handler&Looper)的思考
2、Android消息机制的原理剖析—闭环总结
3、Android消息机制(一):概述设计架构

相关文章

网友评论

      本文标题:Android 消息机制源码解读

      本文链接:https://www.haomeiwen.com/subject/idvabxtx.html