线程通信、ActivityThread及Thread类是理解Android线程管理的关键。

线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用。本小节主要从以下三个方面进行分析:

  1. 《Android线程管理(一)——线程通信》
  2. 《Android线程管理(二)——ActivityThread》
  3. 《Android线程管理(三)——Thread》

一、Handler、MessageQueue、Message及Looper四者的关系

在开发Android多线程应用时,Handler、MessageQueue、Message及Looper是老生常谈的话题。但想彻底理清它们之间的关系,却需要深入的研究下它们各自的实现才行。首先,给出一张它们之间的关系图:

  • Looper依赖于MessageQueue和Thread,因为每个Thread只对应一个Looper,每个Looper只对应一个MessageQueue。
  • MessageQueue依赖于Message,每个MessageQueue对应多个Message。即Message被压入MessageQueue中,形成一个Message集合。
  • Message依赖于Handler进行处理,且每个Message最多指定一个Handler来处理。Handler依赖于MessageQueue、Looper及Callback。

从运行机制来看,Handler将Message压入MessageQueue,Looper不断从MessageQueue中取出Message(当MessageQueue为空时,进入休眠状态),其target handler则进行消息处理。因此,要彻底弄清Android的线程通信机制,需要了解以下三个问题:

  • Handler的消息分发、处理流程
  • MessageQueue的属性及操作
  • Looper的工作原理

1.1 Handler的消息分发、处理流程

Handler主要完成Message的入队(MessageQueue)和处理,下面将通过Handler的源码分析其消息分发、处理流程。首先,来看下Handler类的方法列表:

从上图中可以看出,Handler类核心的方法包括:1)构造器;2)分发消息;3)处理消息;4)post发送消息;5)send发送消息;6)remove消息和回调。

首先,从构造方法来看,构造器的多态最终通过调用如下方法实现,即将实参赋值给Handler类的内部域。

final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;public Handler(Looper looper, Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;
}

其次,消息的入队是通过post方法和send方法来实现的。

public final boolean postAtTime(Runnable r, long uptimeMillis) {return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageAtTime(msg, uptimeMillis);
}

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

两者的区别在于参数类型不同,post方法传入的实例对象实现了Runnable接口,然后在内部通过getPostMessage方法将其转换为Message,最终通过send方法发出;send方法传入的实例对象为Message类型,在实现中,将Message压入MessageQueue。

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

通过Handler将Message压入MessageQueue之后,Looper将其轮询后交由Message的target handler处理。Handler首先会对消息进行分发。首先判断Message的回调处理接口Callback是否为null,不为null则调用该Callback进行处理;否判断Handler的回调接口mCallback是否为null,不为null则调用该Callback进行处理;如果上述Callback均为null,则调用handleMessage方法处理。

public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}

handleMessage方法在Handler的子类中必须实现。即消息具体的处理交由应用软件实现。

/*** Subclasses must implement this to receive messages.*/
public void handleMessage(Message msg) {
}

回到Activity(Fragment),在Handler的子类中实现handleMessage方法。这里需要注意一个内存泄露的问题,比较下述两种实现方式,第一种直接定义Handler的实现,第二种通过静态内部类继承Handler,定义继承类的实例。

Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);// 根据msg调用Activity的方法}
};

static class MyHandler extends Handler {WeakReference<DemoActivity> mActivity;public MyHandler(DemoActivity demoActivity) {mActivity = new WeakReference<DemoActivity>(demoActivity);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);DemoActivity theActivity = mActivity.get();// 根据msg调用theActivity的方法
}

不绕弯子,直接说明为什么第一种方式会引起内存泄露,而第二种不会。

在第一种方式中,mHandler通过匿名内部类方式实例化,在Java中,内部类会强持有外部类的引用(handleMessage方法中可以直接调用Activity的方法),在外部Activity调用onDestroy()方法之后,如果Handler的MessageQueue依然有未处理的消息,那么由于Handler持有Activity的引用导致Activity无法被系统GC回收,从而引起内存泄露。

在第二种方式中,首先继承Handler定义静态内部类,由于MyHandler为静态类,即使定义在Activity的内部,也与Activity没有逻辑上的联系,即不会持有外部Activity的引用;其次,在静态类内部,定义外部Activity的弱引用,弱引用在系统资源紧张时会被系统优先回收。最后,在handleMessage()方法中,通过WeakReference的get方法获取外部Activity的引用,如果该弱引用已被回收,则get方法返回null。

struct GcSpec {/* If true, only the application heap is threatened. */bool isPartial;/* If true, the trace is run concurrently with the mutator. */bool isConcurrent;/* Toggles for the soft reference clearing policy. */bool doPreserve;/* A name for this garbage collection mode. */const char *reason;
};

这段代码定义在dalvik/vm/alloc/Heap.h中,其中doPreserve为true时,表示在执行GC的过程中,不回收软引用引用的对象;为false时,表示在执行GC的过程中,回收软引用引用的对象。

最后,使用Handler的过程中,还需要注意一点,在前面的方法列表图中已经提到。为避免Activity调用onDestroy后,Handler的MessageQueue中仍存在Message,一般会在onDestroy中调用removeCallbacksAndMessages()方法。

@Override
protected void onDestroy() {
super.onDestroy();// 清空Message队列myHandler.removeCallbacksAndMessages(null);
}

public final void removeCallbacksAndMessages(Object token) {mQueue.removeCallbacksAndMessages(this, token);
}

removeCallbacksAndMessages()方法会移除obj为token的由post发送的callback和send发送的message,当token为null时,会移除所有callback和message。

1.2 MessageQueue的属性及操作

MessageQueue,消息队列,其属性与常规队列相似,包括入队、出队等,这里简要介绍一下MessageQueue的实现。

首先,MessageQueue新建队列的工作是通过在其构造器中调用本地方法nativeInit实现的。nativeInit会创建NativeMessageQueue对象,然后赋值给MessageQueue成员变量mPtr。mPtr是int类型数据,代表NativeMessageQueue的内存指针。

MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();
}

其次,Message入队的通过enqueueMessage方法实现。首先检查message是否符合入队要求(是否正在使用,target handler是否为null),符合要求后通过设置prev.next = msg队列的指针完成入队操作。

boolean enqueueMessage(Message msg, long when);

再次,出队是通过next()方法完成的。涉及到同步、锁等问题,这里不详细展开了。

再次,删除元素有两个实现。即分别通过p.callback == r和p.what == what来进行消息识别。

void removeMessages(Handler h, int what, Object object);
void removeMessages(Handler h, Runnable r, Object object);

最后,销毁队列和创建队列一样,是通过本地函数完成的。传入的参数为MessageQueue的内存指针。

private native static void nativeDestroy(int ptr);

1.3 Looper的工作原理

Looper是线程通信的关键,正是因为Looper,整个线程通信机制才真正实现“通”。

在应用开发过程中,一般当主线程需要传递消息给用户自定义线程时,会在自定义线程中定义Handler进行消息处理,并在Handler实现的前后分别调用Looper的prepare()方法和loop()方法。大致实现如下:

new Thread(new Runnable() {private Handler mHandler;@Overridepublic void run() {Looper.prepare();mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};Looper.loop();}
});

这里重点说明prepare()方法和loop()方法,实际项目中不建议定义匿名线程。

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

可以看出,prepare方法的重点是sThreadLocal变量,sThreadLocal变量是什么呢?

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal实现了线程本地存储。简单看一下它的类注解文档,ThreadLocal是一种特殊的全局变量,全局性在于它存储于自己所在线程相关的数据,而其他线程无法访问。

/*** Implements a thread-local storage, that is, a variable for which each thread* has its own value. All threads share the same {@code ThreadLocal} object,* but each sees a different value when accessing it, and changes made by one* thread do not affect the other threads. The implementation supports* {@code null} values.** @see java.lang.Thread* @author Bob Lee*/
public class ThreadLocal<T> {
}

回到prepare方法中,sThreadLocal添加了一个针对当前线程的Looper对象。并且prepare方法只能调用一次,否则会抛出运行时异常。

初始化完毕之后,Handler通过post和send方法如何保证消息投递到Looper所持有的MessageQueue中呢?其实,MessageQueue是Handler和Looper的桥梁。在前面Handler章节中提到Handler的初始化方法,Handler的mLooper对象是通过Looper的静态方法myLooper()获取的,而myLooper()是通过调用sThreadLocal.get()来得到的,即Handler的mLooper就是当前线程的Looper对象,Handler的mQueue就是mLooper.mQueue。

……
mLooper = Looper.myLooper();
if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
……

public static Looper myLooper() {return sThreadLocal.get();
}

转载于:https://www.cnblogs.com/luozhijun/p/6886063.html

Android线程管理(一)相关推荐

  1. android 管理类,android 线程管理类

    1. 延时任务以及取消延时的应用场景:实时搜索,文字内容变化时候不发送请求,当停止变化指定时间后再发送请求 2. 使用方法: // 回到主线程 MainThreadHelper.runOnUIthre ...

  2. Android 线程管理

    Android程序的主线程(UI线程)无法跑有时间耗费的代码,所以一个好习惯是将各种与UI无关的操作,比如网络通信(包括http.ftp.mail.socket).文件操作.数值计算等放在子线程中.为 ...

  3. Android 线程池管理工具类

    转自Android 线程池 public class AppExecutors {private static final String TAG = "AppExecutors"; ...

  4. Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8570428 通过前面几篇文章的学习,我们知道了 ...

  5. Android 内存管理 amp;Memory Leak amp; OOM 分析

    1.Android 流程管理&内存 Android主要应用在嵌入式设备其中.而嵌入式设备因为一些众所周知的条件限制,通常都不会有非常高的配置,特别是内存是比較有限的. 假设我们编写的代 码其中 ...

  6. Android 线程池概念及使用

    一:使用线程池的原因 在android开发中经常会使用多线程异步来处理相关任务,而如果用传统的newThread来创建一个子线程进行处理,会造成一些严重的问题: 在任务众多的情况下,系统要为每一个任务 ...

  7. Android线程池封装库

    目录介绍 1.遇到的问题和需求 1.1 遇到的问题有哪些 1.2 遇到的需求 1.3 多线程通过实现Runnable弊端 1.4 为什么要用线程池 2.封装库具有的功能 2.1 常用的功能 3.封装库 ...

  8. Android内存管理

    Android是一个基于Linux实现的操作系统.但对于Linux内核来说,Android也仅仅只是一个运行在内核之上的应用程序,与其他运行在内核之上的应用程序没有任何区别.所以Android需要一套 ...

  9. android线程间通信的几种方法_Android 技能图谱学习路线

    Java基础 Java Object类方法 HashMap原理,Hash冲突,并发集合,线程安全集合及实现原理 HashMap 和 HashTable 区别 HashCode 作用,如何重载hashC ...

最新文章

  1. ROS限速教程全集很实用
  2. python进行图片的定位切割_python3 实现对图片进行局部切割的方法
  3. Linux截取文件名的后几位,Linux字符串截取和处理命令 cut、printf、awk、sed、sort、wc...
  4. CDQZ_Training 2012-05-24 聪明的打字员
  5. json、pickle
  6. Linux curl命令简介
  7. IE6 某些bug修复
  8. 以下不是python内置函数的是_Python内置函数
  9. VirtualBox安装Windows XP图文教程
  10. Pygame教程系列四:播放音频篇
  11. 数学——每日一题7 1.14 利用定积分的定义求极限
  12. Linux 快速搭建免费邮件服务器
  13. 少爷的云台山漂流记:山崖水畔的狂欢
  14. AE - Saber插件(简单使用方法)
  15. mumu按键精灵_什么安卓模拟器可实现操作录制?MuMu模拟器成为你的按键精灵_MuMu安卓模拟器/MuMu手游助手...
  16. 如何看待 Google 最新的系统 Fuchsia?
  17. NetTerm 使用简介
  18. oracle vbo4582,4582是什么意思
  19. SCT2601TVBR、LMR16006XDDCR、MP2456GJ-Z参数
  20. FileZilla使用ssh服务和FTP服务连接Ubuntu

热门文章

  1. python中md5_Python的md5是什么意思
  2. m.pythontab.com_Python expandtabs()函数的使用
  3. 移动端网站建设——一如既往地简洁大方
  4. 企业官网建设注重这四点错不了!
  5. python的100个案例_Python实例100个(基于最新Python3.7版本)
  6. 吉大c 语言程序设计奥鹏作业,吉大19秋学期《C语言程序设计》在线作业一【满分答案】...
  7. 易语言录入数据到access里重复提示_一招搞定:excel数据共享协同
  8. 白盒测试有哪些方法_QA测试开发常考的面试题+答案汇总(持续更新)
  9. 首次使用gradle出现Could not find method leftShift() for arguments解决办法
  10. Android 手写Binder 教你理解android中的进程间通信