设为首页 加入收藏

TOP

Android线程间异步通信机制源码分析(二)
2015-08-31 21:23:16 来源: 作者: 【 】 浏览:44
Tags:Android 线程 异步 通信 机制 源码 分析
ptimeMillis):在指定时间将一条消息加入队列尾部
?sendMessageDelayed(Message msg, long delayMillis): 在经过指定时间延迟后,将一条消息加入队列尾部



? ? ? ? 通过这些方法,handler将message对象交给了消息队列messageQueue。looper从消息队列中取出message后,会调用message所持有的handlerdispatch方法,分发给handler来处理该消息:


public void dispatchMessage(Message msg) {
? ? ? ? if (msg.callback != null) {// 如果message对象持有回调对象,则执行它
? ? ? ? ? ? handleCallback(msg);
? ? ? ? } else {
? ? ? ? ? ? if (mCallback != null) {// 如果当前handler持有消息处理的回调函数,则执行它
? ? ? ? ? ? ? ? if (mCallback.handleMessage(msg)) {
? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? handleMessage(msg);// 如果都没有,则执行用户自定义的消息处理方法
? ? ? ? }
? ? }


管家Looper和信箱MessageQueue


? ? ? ? 从上面的分析我们可以了解,message对象是通过handler间接的加入到消息队列中的,然后消息队列将message对象组织成一个队列提供给looper分发。如果只看名字,我们会猜想这个类中使用队列这种数据结构来组织message,然而并不是这样的。MessageQueue将message对象按时间顺序组织成一个链表的形式来管理。


? ? ? ? 在创建handler对象的时候,我们通过looper.myLooper方法从threadLocal中获取到与当前线程一一对应的looper对象。那么当前线程是何时创建了整个looper对象并将其放入threadLocal呢?在Android的UI线程中,早已自动替我们创建好了looper;而如果是我们自己创建的线程,那么就需要调用prepare方法来创建出looper对象:


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


? ? ? ? 在创建looper对象的时候也创建了它要轮询的消息队列,并获取了当前线程的引用:


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


? ? ? ? 由于线程和looper对象是一一对应的关系,所以我们有时候判断当前线程是否为UI线程的时候,会调用getMainLooper方法来判断:


public static Looper getMainLooper() {
? ? ? ? synchronized (Looper.class) {
? ? ? ? ? ? return sMainLooper;
? ? ? ? }
}


? ? ? ? looper对象持有它所轮询的消息队列的对象,通过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,有可能会阻塞
? ? ? ? ? ? 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);
? ? ? ? ? ? }
? ? ? ? ? ? // 把从队列中取到的message交给handler来处理
? ? ? ? ? ? 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();
? ? ? ? }
? ? }


到此为止,在子线程中创建的message对象就被处理好了。下面我们需要谈论一下handler的泄露问题以及解决方法。


Handler泄露问题


? ? ? ? 谈论整个问题之前,我们需要了解JAVA的GC机制,这里就不做详细介绍了。当我们在activity中创建一个handler对象时,往往会继承一个匿名内部类,里面复写了handler的handleMessage方法。这时,该匿名内部类就会持有当前activity的对象引用。同时,持有该handler的子线程对象往往会进行一些耗时的操作,创建message对象并把handler对象的引用赋给它。此时如果用户关闭了activity,那么此activity对象是应该被系统回收的。但是,由于子线程的耗时操作,并且它和未处理的message对象都持有handler的引用,而handler又持有activity的引用,这就会导致该activity无法回收,出现内存泄露。


? ? ? ? 目前主要有两种解决方案:
?第一种,在activity关闭时,停掉该子线程,然后调用handler的removeCallbacks方法,把消息队列中的message删除掉。
?第二种,让匿名内部类作为一个静态内部类出现,这样就不持有activity的对象引用了,activity就可以被回收掉了。但是,不持有activity的引用,怎么操作其中的对象呢?只好自己声明一个弱引用了:


static class myHandler extends Handler {
? ? WeakReference mActivityReference;


? ? myHandler(Activity activity) {
? ? ? ? mActivityReference= new WeakReference(activity);
? ? }


? ? @Override
? ? public void handleMessage(Message msg) {
? ? //消息处理......
? ? }
}


? ? ? ? 弱引用在垃圾回收的时候会被忽略,所以可以被安全回收。个人比较倾向于第二种写法,比较简单。


总结


? ? ? ? 本文简单介绍了Android系统线程之间异步通信的机制,从源码的角度简单谈论了线程通信时的基本工作。其中未详细深入到messageQueue的具体管理操作,只是简单提及了message对象的回收,具体细节有空再补上。


首页 上一页 1 2 下一页 尾页 2/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇从AIDL开始谈Android进程间Binder.. 下一篇Android LayoutInflater源码解析

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: