Android之异步线程原理

来源:互联网 发布:阿里云邮企业版下载 编辑:程序博客网 时间:2024/04/30 04:49

基础介绍

异步消息处理线程是指,线程在启动后会进入一个无线循环体中,没循环一次,从内部的消息队列中取出一个一个消息,并回调相应的消息处理函数,执行完一个消息后则继续循环。如果消息队列为空,线程会暂停,知道消息队列中有新的消息。
异步消息处理线程本质上仍然是一个线程,只不过这种线程的执行代码设置成如上所述的逻辑而已。在android中实现异步线程主要涉及到如下几个类:ThreadLocal,Looper,MessageQueue,Handler,Message,接下来我们一一介绍这几个类,解析Android的异步线程机制。
先上一张框架图:
这里写图片描述

ThreadLocal

ThreadLocal并不是Android的sdk中的类,而是java.lang中的一个类,该类的作用是为线程创建一个基于线程的变量存储,我们可以称之为线程局部存储。ThreadLocal可以使对象达到线程隔离的目的,它为每一个线程维护自己的变量拷贝,通过其中的set方法将变量绑定到线程上。ThreadLocal提供了一种解决多线程同步的问题解决方案,通过为每一份变量进行拷贝,这样的话,每个线程操作的都是属于自己的变量,而不是共同的一个变量,因此也就不需要同步锁了。举个栗子,我们创建出一个变量,而这个变量会被两个线程操作。一般情况下,我们会给这个变量加锁,通过这种方式来解决同步的问题。但是当我们使用ThreaLoca时,我们就可以通过ThreadLoca来为每一个线程做一个拷贝,而且这个拷贝是跟线程绑定在一起的,也就是说每个线程可以更改自己的变量而不影响另外一个线程。这样也就不需要锁了。ThreadLocal在set时会自动绑定到当前的线程,而不需要自己去绑定。代码这里就不写了,知道中心思想就行。有人可能会问,这跟Android有什么关系呢。这就要说到我们的Looper了,因为在Android的异步线程中,ThreadLocal绑定的这个线程变量就是Looper的一个对象。

Looper

Looper有什么用呢,要实现异步线程,必须要Looper,因为Looper是用来产生一个MessageQueue。我们可以通过查看源代码知道,在Looper类中有一个成员变量mQueue(MessageQueue类的实例),该变量用于保存Looper中MessageQueue。
Looper通过静态方法Looper.prepare()方法来创建出一个MessageQueue对象。注意,Looper.prepare()方法在一个线程中只能执行一次。

prepare()方法

查看源代码:

 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));    }

以上为android的源代码,执行了两次prepare时,就会抛出一个异常。这个其实很自然就可以想到,因为前面提到,Looper是用来创建MessageQueue的。一个线程只能有一个MessageQueue,因此自然只能有一个Looper对象。看完prepare()源代码好像并没有发现它创建出了MessageQueue的一个实例啊,对的,这里是看不到,但是。我们来看看在创建一个新的Looper对象时所做的工作:
我们找到Looper的构造函数:

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

我们可以发现,Looper的这个构造函数是私有的,而且Looper只有这一个构造函数。所以我们可以看出我们不能在其他的类中new出一个Looper对象。但是,这不是重点,重点是Looper在这里创建出了一个MessageQueue对象。

loop()方法

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.recycle();        }    }

函数myLooper用来返回当前线程的Looper对象,通过一个for(;;)来执行循环。在for循环内部,通过MessageQueue的next方法来去取出其中的Message,这里有一点需要注意,就是取出来的message,最后会调用msg.target.dispatchMessage(msg);来处理消息,该方法在Handler中,因为msg.target是一个Handler对象。这个放到Handler中说

MessageQueue

MessageQueue是用来处理消息队列的。该类中有几个方法,一个是next()方法,用于取出队列中下一个元素的。另外一个是enqueueMessage方法,用于向消息队里中添加一个元素。该方法会在Handler中的sendMessage中使用到,这里稍微提一下,在Handler的sendMessage中,会调用这个方法,将Message添加到接收线程的MessageQueue中。

Handler

Handler是用于发送信息的,我们熟知的方法就是其中的sendMessage方法了,用于向消息接收线程发送消息。上面的框架图中有说到,Handler一定要在接收消息的线程中创建,只有这样的话才可以给该线程发送消息。因为创建出的Handler对象handler必须要有该线程的MessageQueue消息队列才可以给该线程发送消息。当Handler在创建时,会用到如下的构造函数:

handler的构造函数

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;    }

在创建Handler时, 会通过mLooper = Looper.myLooper();来获取当前线程的Looper对象,而Looper对象又是获取MessageQueue对象前提。Handler在调用handler.sendMessage(Message)发送message时,最终会调用到这个方法:

handler的sendMessage方法(最终会调用的方法)

    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会获得当前线程的MessageQueue对象。之后调用enqueueMessage(queue, msg, uptimeMillis);这个方法是Handler中的方法,而不是MessgaeQueue中的enqueueMessage方法,我们查看Handler中的enqueueMessage方法:

handler中的enqueueMessage

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

可以看到个方法体中的第一行代码:msg.target = this;将当前发送的message.target对象设置成当前的handler。这个在Looper.loop()方法中,调用了 msg.target.dispatchMessage(msg);方法。这个很快就会介绍。先不说这个。可以看到,handler中的enqueueMessage方法会设置message的target为当前对象,之后会调用MessageQueue对象的enqueueMessage(该方法在MessageQueue中介绍过,是用于向消息队列添加消息的方法)方法,将message添加到MessageQueue对象中。
好了,我 们现在来说一下handler的dispatchMessage(msg)方法。我们看一下这个方法的源代码,该方法会在looper的loop方法中使用的,当获取每个消息时,会调用方法 msg.target.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);        }    }

该方法中先判断消息本身是否有回调函数,有的话则调用消息的回调函数,如果没有,再判断创建这个Handler时有没有传递Callback接口,注意,这个 接口的名字就是Callback,Handler可以通过构造函数来给这个mCallback赋值,如果这个mCallback传递了值的话 就会调用这个方法,否则调用Handler本身的handleMessage方法。这个方法在Handler中是空的,需要在继承Handler的类中,重写该方法。也就是说重写的该方法实际上是优先级最低的。
后面两种比较常见,但是前面一种,也就是消息本身携带了接口的一般通过Message.obtain(Handler h, Runnable callback)方法来设置。
好了 Android的异步线程说到这里就拆不多了。

0 0