Android 异步消息处理机制Looper、Handler、Message三者关系

来源:互联网 发布:淘宝招牌图片免费下载 编辑:程序博客网 时间:2024/06/16 22:24

Looper,Handler,Message三者是我们常常用来再子线程跟新UI的,我们把Message发给Handler,然后,handler调用HandlerMessage()方法,我们在这个方法里面更新UI。那么Looper呢,又是什么,下面我来给大家介绍一下三者的关系。

首先要说明的是,每个线程最多只有一个Looper,在线程里面调用Looper.prepare()就是为这个线程设置了一个Looper,所以在子线程,我们使用这个三者前,一定要调用Looper.prepare()方法,而主线程其实已经替我们调用过这个方法了,所以我们不必重复调用。

另外我们还要主动调用Looper.loop()方法,下面说一下为什么。

Looper里面包含一个MessageQueue,这个就是一个消息队列,handler发送的消息Message都会到这个队列里面来,Looper.loop()不断从MessageQueue取出Message,没有则阻塞,有则调用Message.target的dispatchMessage(msg)方法,这个方法Message也传了过去。dispatchMessage(msg)方法里面又调用handleMessage()方法,这方法就是我们的具体实现。

那么通过刚才的描述也很清楚了,Message.target其实就是Handler。


OK,那么我再来总结一下三者之间的引用关系

Looper:拥有一个MessageQueue,里面包含所有Message

Message:每个Message都持有一个Handler引用

Handler:handler持有Looper

也就是说任意一个可以获取其他两个的引用,从而相互关联。


前面的可能表达得不是很好,下面通过源码来说明

先看Looper的静态方法prepare()

public static void prepare() {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());    }
这个方法是我们必须要主动调用的,sThreadLocal是一个java类库提供的一个类,用于不同线程中维护同一个变量。这个比较难解释,也就是说一个变量,本来应该是多个线程共享的,所以我们多线程操作的时候,要考虑同步的问题,但是使用了ThreadLocal以后,系统为每个线程分配一个空间来存储这个变量,也就是说每个线程只要维护好自己的变量就可以了,这个变量也就不是共享的了,从而避免同步的问题。

我们可以看到,prepare()通过sThreadLocal.get()方法,只需要存在一个Looper实例,如果重复prepare()就会抛出异常(类似单例模式,不同的是重复创建的结果是抛出异常)。

然后如果没有Looper实例,就去new一个,并且保存在sThreadLocal中。

下面看new Looper()方法

private Looper() {        mQueue = new MessageQueue();        mRun = true;        mThread = Thread.currentThread();    }
可以看到这个构造方法是私有的(所以说像单例模式),并且创建了一个消息队列mQueue,还保存了创建这个Looper的线程的引用(每个线程都只有一个Looper)

prepare()以后,我们就要主动调用静态的loop()方法,从mQueue消息队列中不断取出消息了

下面看loop()方法

public static void loop() {        Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        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();                while (true) {            Message msg = queue.next(); // might block            if (msg != null) {                if (msg.target == null) {                    // No target is a magic identifier for the quit message.                    return;                }                long wallStart = 0;                long threadStart = 0;                // 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);                    wallStart = SystemClock.currentTimeMicro();                    threadStart = SystemClock.currentThreadTimeMicro();                }                msg.target.dispatchMessage(msg);                if (logging != null) {                    long wallTime = SystemClock.currentTimeMicro() - wallStart;                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);                    if (logging instanceof Profiler) {                        ((Profiler) logging).profile(msg, wallStart, wallTime,                                threadStart, threadTime);                    }                }                // 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.recycle();            }        }    }
myLooper()方法就是一个get方法,用于获取looper对象,前几行代码说明没有prepare(),是不可以调用loop()的

然后调用进入了一个死循环,queue.next()不断从队列中获取message对象,一旦获取到messgae,就调用msg.target.dispatchMessage(msg)方法。

ok,loop()基本上就做了这些事情,那么有几个疑问,message是什么时候添加到queue里面的,然后msg.target对象是什么,dispatchMessage(msg)方法又做了什么呢。


接下来我们看handler对象源码,就看解决这些问题。

首先是它的构造函数

public Handler() {        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 = null;    }

从构造函数可以看出,handler获取了Looper对象的引用,并且获取了mQeueu消息队列的引用

那么我们这么把msg添加到队列中的呢,一般我们都是调用handler的sendMessage()方法来发送消息的,但是这些方法最后都要调用同一个方法,sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis)    {        boolean sent = false;        MessageQueue queue = mQueue;        if (queue != null) {            msg.target = this;            sent = queue.enqueueMessage(msg, uptimeMillis);        }        else {            RuntimeException e = new RuntimeException(                this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);        }        return sent;    }

在这个方法我们可以看到,我们设置msg的target为handler本身,然后把msg加入queue消息队列。

原来我们sendMessage的时候就把msg放到消息队列里面了。


最后我们再看一遍Loop()方法里面的

msg.target.dispatchMessage(msg);
也就是说调用了handler的dispatchMessage()方法,所以总的来说是这样的,handler把msg加入队列,loop不断取出msg,然后调用handler的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);        }    }

这个方法里面,我们调用了handleMessage方法,其实就是一个回调,这个方法里面,我自己写对msg的处理


从上面源码总结基本上可以理清楚Looper、Handler、Message三者关系的关系了,另外再提一下在子线程中进行UI操作的其他方法:

1. Handler的post()方法

2. View的post()方法

3. Activity的runOnUiThread()方法

我们先来看下Handler中的post()方法,代码如下所示:
public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }

原来这里还是调用了sendMessageDelayed()方法去发送一条消息啊,并且还使用了getPostMessage()方法将Runnable对象转换成了一条消息,我们来看下这个方法的源码:

private final Message getPostMessage(Runnable r) {    Message m = Message.obtain();    m.callback = r;    return m;}

注意,这里讲callback设置为runnable对象里面,还记得我们的dispatchMessage()方法吗

public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }
里面首先对callback是否为空进行了判断,不为空,要调用handleCallback()

private final void handleCallback(Message message) {        message.callback.run();    }
这里主动调用了runnable的run()方法,所以这不是开启子线程的操作哦!(注意,主动调用run()方法是不会开启线程的)


然后再来看一下View中的post()方法,代码如下所示:

public boolean post(Runnable action) {    Handler handler;    if (mAttachInfo != null) {        handler = mAttachInfo.mHandler;    } else {        ViewRoot.getRunQueue().post(action);        return true;    }    return handler.post(action);}

原来就是调用了Handler中的post()方法

最后再来看一下Activity中的runOnUiThread()方法,代码如下所示:

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

如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,否则就直接调用Runnable对象的run()方法。

1 0
原创粉丝点击