Android之异步线程原理
2015-06-01 19:07
357 查看
基础介绍
异步消息处理线程是指,线程在启动后会进入一个无线循环体中,没循环一次,从内部的消息队列中取出一个一个消息,并回调相应的消息处理函数,执行完一个消息后则继续循环。如果消息队列为空,线程会暂停,知道消息队列中有新的消息。异步消息处理线程本质上仍然是一个线程,只不过这种线程的执行代码设置成如上所述的逻辑而已。在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的异步线程说到这里就拆不多了。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories