码迷,mamicode.com
首页 > 移动开发 > 详细

Android之异步线程原理

时间:2015-06-01 20:33:05      阅读:142      评论:0      收藏:0      [点我收藏+]

标签:android   异步   

基础介绍

异步消息处理线程是指,线程在启动后会进入一个无线循环体中,没循环一次,从内部的消息队列中取出一个一个消息,并回调相应的消息处理函数,执行完一个消息后则继续循环。如果消息队列为空,线程会暂停,知道消息队列中有新的消息。
异步消息处理线程本质上仍然是一个线程,只不过这种线程的执行代码设置成如上所述的逻辑而已。在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的异步线程说到这里就拆不多了。

Android之异步线程原理

标签:android   异步   

原文地址:http://blog.csdn.net/sujz12345/article/details/46314997

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!