Android 异步消息处理机制

Android 异步消息处理机制

  Handle是android提供的一个异步消息处理机制,通过handler我们可以方便地处理异步消息,实现线程间的通信。Android异步消息机制由Handler、Loop以及MessageQueue组成,其中MessageQueue负责储存线程中的消息;Loop是一个无限循环,负责不停的从MessageQueue取出消息,并交给handler去处理;Handler负责消息的发送和处理。

handler

Handler的实现方式主要有以下三种:

  • 重写Handler类的handleMessage方法
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //do something
            super.handleMessage(msg);
        }
    };
  • 提供Handle的callback接口,并实现其handleMessage方法
    Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //do something
            return true;
        }
    });
  • 构造带有runnable参数的message对象,当消息发出时会回调message的runnable的run方法
    Handler handler = new Handler();
    Message message = Message.obtain(handler, new Runnable() {
        @Override
        public void run() {
              //do something  
        }
    });
    message.sendToTarget();

  以上三种方式都可以实现消息的异步处理,Handler应提供了多种接口去发送消息。

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean sendMessage(Message msg)
public final boolean sendMessageDelayed(Message msg, long delayMillis)

  从源码可以知道post方法实际上还是调用相对于的sendmessage方法来实现的。当我们在主线程中这样创建handler对象时没有问题,但是当我们在非主线程中直接new Handler()时会抛出异常“Can’t create handler inside thread that has not called Looper.prepare()”。这是因为主线程中已经默认帮我们创建好了Loop,而子线程中没有为我们创建Loop,需要我们手动去创建。在子线程中需要调用Loop.prepare()创建loop,调用Loop.loop()开启循环。
  我们通过源码来看下主线程中是如何创建Loop的,android的主线程在代码ActivityThread类中。我们来看一下ActivityThread类的main方法。

public static void main(String[] args) {
    ...........................
    Looper.prepareMainLooper();
    ...........................
    Looper.loop();
}

  通过ActivityThread源码,我们可以知道在主线程中通过Looper.prepareMainLooper()来创建Loop。Loop负责创建消息队列,并从消息队列中获取消息,将消息交给handler处理。下面我们将从源码的角度来分析下Loop。

Loop

  我们先来看一下Loop的构造函数

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

  在Loop的构造函数中,它创建了线程的MessageQueue。Loop的构造函数是private的,所以我们不能在外部创建Loop对象。那么我们是怎样为线程创建Loop对象来管理线程的MessageQueue呢?接着我们看一下主线程的中创建Loop的方法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();
    }
}

  prepareMainLooper()方法专门用来为主线程创建Loop,在prepareMainLooper()方法中调用了prepare(false)来创建Loop。我们来看一下prepare()源码。

    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");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

  不带参数的prepare方法会调用prepare(true),而prepareMainLooper()调用的是prepare(false), 从Loop的私有构造函数中我们可以看出,true和false的区别是指创建的消息队列是否允许退出。创建的Loop对象被放到一个叫做sThreadLocal的ThreadLocal对象中,每次调用prepare时都是先从sThreadLocal中取出,一旦sThreadLocal取出的值不为null,就会抛出异常Only one Looper may be created per thread。
  通过以上的分析,我们可以知道一个线程只会有一个Loop,同时也只会有一个MessageQueue。因为Loop只会创建一次,而MessageQueue又是在Loop中创建的。
  我们已经知道了Loop的创建,下面我们来看一下Loop是怎样循环从MessageQueue中获取消息的并交给Handler处理的。我们来看一下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.");
        }
        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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            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方法是一个无限循环,它通过queue.next()方法来从消息队列中取出消息,next()方法是一个阻塞方法,一旦next()返回的是null,则说明消息队列退出了,loop循环也就结束了。当取出message后是交给谁去处理呢?我们在loop()方法的源码中可以看到有这么一句msg.target.dispatchMessage(msg);从这边可以看到原来是交给了msg的target去处理了。那么这个target是什么呢,其实这个target就是和当前loop绑定的handler。下面我们从源码的角度去分析下消息从发送到处理的全过程。我们知道handler发送消息的入口有很多,我们就拿其中sendEmptyMessage来分析,原理都是类似的,只是消息有点区别而已。
  我们先来看一下sendEmptyMessage的源码 

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

  代码很简单,直接调用了sendEmptyMessageDelayed,我们再来看一下sendEmptyMessageDelayed的源码。

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

  代码也很简单,生成了一个Message对象,然后调用sendMessageDelayed来处理,我们再来看一下sendMessageDelayed的源码。

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

  这里调用了sendMessageAtTime来处理,我们继续看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);
    }

  这里调用了enqueueMessage方法来处理,我们继续看enqueueMessage的源码

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

  看到这里我们就明白了,前面loop()方法里的msg.target.dispatchMessage(msg),这个target就是当前的handler对象,因此我们就知道loop()取出来的消息是交给handler去处理的。enqueueMessage方法将消息加入消息队列中,然后通过next()方法读出,从前面的分析就知道消息读出来后就交给handler的dispatchMessage方法处理。我们来看一下dispatchMessage方法的源码。

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

  从源码我们就可以知道为什么前文中给出了三种handler写法,它们分别是不同的消息处理方式。消息优先给消息对象的callback去处理,它是一个runnable。如果消息对象没有callback,并且handler实现了一个callback接口,那么就交给过callback接口的handleMessage方法去处理。如果callback接口的handleMessage方法返回true,那么消息处理结束,否则消息还要被handler的handleMessage去处理,至此,消息从发送到处理的流程就走完了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值