关于Android线程通信思考

 

一、对android主线程的理解

对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。
对于主线程,保证能一直存活的方法就是死循环
主线程的死循环一直运行是不是特别消耗CPU资源:
    主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
    主线程的MessageQueue没有消息或者要处理的消息没到时间,便阻塞在loop的queue.next()中的nativePollOnce()方法里,
    主线程会释放CPU资源进入休眠状态
    往pipe管道写端写入数据来唤醒主线程工作
复制代码

二、android中的线程间通信之--消息机制

消息通信是Android系统中使用相当普遍的一种线程间通信方式。
既然是线程间的通信,就一定存在共享的对象,一定需要处理线程间的同步。
复制代码

Android消息机制.jpeg

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。
可用于执行后台耗时的任务,当任务执行完成后
会自动停止;
可自动创建子线程来执行任务
posted @ 2021-04-07 16:55  新感觉  阅读(165)  评论(0编辑  收藏  举报