zoukankan      html  css  js  c++  java
  • 关于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。
    可用于执行后台耗时的任务,当任务执行完成后
    会自动停止;
    可自动创建子线程来执行任务
  • 相关阅读:
    java的运行机制及初步相关配置(jdk)
    观察者模式
    Shiro的 rememberMe 功能使用指导(为什么rememberMe设置了没作用?)
    MyBatis—实现关联表查询
    Mybatis解决字段名与实体类属性名不相同的冲突
    Mybatis简化sql书写,别名的使用
    十八.模块
    十七.偏函数
    十六.装饰器
    十五.匿名函数
  • 原文地址:https://www.cnblogs.com/xgjblog/p/14628520.html
Copyright © 2011-2022 走看看