关于Android线程通信思考
一、对android主线程的理解
对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。
对于主线程,保证能一直存活的方法就是死循环
主线程的死循环一直运行是不是特别消耗CPU资源:
主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
主线程的MessageQueue没有消息或者要处理的消息没到时间,便阻塞在loop的queue.next()中的nativePollOnce()方法里,
主线程会释放CPU资源进入休眠状态
往pipe管道写端写入数据来唤醒主线程工作
复制代码
二、android中的线程间通信之--消息机制
消息通信是Android系统中使用相当普遍的一种线程间通信方式。
既然是线程间的通信,就一定存在共享的对象,一定需要处理线程间的同步。
复制代码
1、关于Handler
Handler用于发送和处理Message和Runnable对象(两者统称为消息); Handler既是消息的生产者,也是消息的消费者; Handler会发送消息到MessageQueue中,通过Looper遍历MessageQueue、执行到该消息时,会回调Message的处理函数,即Handler类的handleMessage(Message msg)。
(1) Handler类的几个重要的成员变量
//通过Handler的Constructor的参数获得,如果Constructor没有指定Looper,
//则使用当前线程的Looper(通过Looper.myLooper())。
final Looper mLooper;
//MessageQueue的获取依赖于mLooper,mQueue=mLooper。
final MessageQueue mQueue;
//通过Constructor传入,如果Constructor中没有指定,则为null。
final Callback mCallback;
//通过Constructor传入,表示是否异步发送,如果Constructor没有指定,则默认false。
final boolean mAsynchronous;
复制代码
(2)Handler提供的发送消息的接口:
public final boolean sendMessage(Message msg);
public final boolean sendEmptyMessage(int what);
public final boolean sendEmptyMessageDelayed(int what, long delayMillis);
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis);
public final boolean sendMessageDelayed(Message msg, long delayMillis);
public boolean sendMessageAtTime(Message msg, long uptimeMillis);
所有的发送消息接口最终都会调用:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis);
最终都是插入到MessageQueue队列中。
复制代码
2、关于Looper
MessageQueue的管家,发现队列中有message,不断的将消息从MessageQueue中取出来,回调到Hander的handleMessage方法
一个Looper对象持有一个MQ
复制代码
1. Looper类源码分析
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//存放线程变量,一个线程一个Looper对象
private static Looper sMainLooper;
final MessageQueue mQueue;//持有消息队列
final Thread mThread;
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");//必须保证一个线程一个Looper对象
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // 可能会阻塞在这个地方
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);//通过message对象中持有的handler,分发消息
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
//...
}
msg.recycleUnchecked();
}
}
}
复制代码
2. 对Looper类的思考
Looper类不停的从MessageQueue中取消息,并通过msg.target.dispatchMessage(msg)分发给源Handler,源Handler就是该消息的发送者,直到MessageQueue为空为止。
如果MessageQueue为空,则looper()方法阻塞在queue.next()处。
Handler对象的handleMessage()在哪个线程执行,取决于该Handler对象所绑定的Looper属于哪个线程。
复制代码
3、MessageQueue
存放handler发送的消息,一个线程一个
提供equeueMessage()和next()方法
MessageQueue维护了一个Message的单链表,对Message的进、出进行管理。
只要Handler对象是在本线程内创建的,就可以往MessageQueue中发送消息,因此进、出时都需要同步。
在MessageQueue中将Message按照时间戳(msg.when)进行排序。
MessageQueue会根据post delay的时间排序放入到链表中,链表头的时间小,尾部时间最大。
好处:能保证时间Delay最长的不会阻塞时间短的。
当每次post message的时候会进入到MessageQueue的next()方法,会根据其delay时间和链表头的比较,
如果更短则,放入链表头,并且看时间是否有delay,
如果有,则block,等待时间到来唤醒执行,否则将唤醒立即执行。
复制代码
4、一个线程有几个handler,几个Looper
由于使用了ThreadLocal机制,所以注定了一个线程只能有一个Looper,但Handler可以new无数个。
因为Handler在Activity可以new,在Service里面也可以new,而Activity全部都跑在了主线程里面,这就证明了主线程中可以有多个Handler。
Looper初始化的地方Looper.prepare()
private static void prepare(boolean quitAllowed) {
//如果这个线程已经存在Looper报异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 不存在,创建一个Looper设置到sThreadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
Looper有一个MessageQueue,可以处理来自多个Handler的Message;
MessageQueue有一组待处理的Message,这些Message可来自不同的Handler;
Message中记录了负责发送和处理消息的Handler;
Handler中有Looper和MessageQueue;
复制代码
三、Android线程通信之--其他类型
AsyncTask:
底层:
封装了线程池和Handler,便于执行后台任务以及在子线程中进行UI操作
组成:
两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler
其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正地执行任务,InternalHandler用于将执行环境从线程池切换到主线程。
分析:
sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程创建。
由于静态成员会在加载类的时候进行初始化,因此这就变相要求AsyncTask的类必须在主线程中加载,否则同一个进程中的AsyncTask都将无法正常工作。
复制代码
HandlerThread:
一种具有消息循环的线程,其内部可使用 Handler。
复制代码
IntentService:
是一种异步、会自动停止的服务;
内部采用 HandlerThread。
可用于执行后台耗时的任务,当任务执行完成后
会自动停止;
可自动创建子线程来执行任务