美文网首页Android开发Android开发Android技术知识
Android进阶(10)| Android的消息机制

Android进阶(10)| Android的消息机制

作者: yzbkaka | 来源:发表于2019-05-29 20:23 被阅读2次
    本节目录

    一.Android消息机制概述

    概念:Android消息机制主要是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程。

    Handler的概念:Handler是Android消息机制中最主要的运行机制,之所以Android系统会引入Handler,主要原因就是为了解决在子线程中无法访问UI的矛盾。

    Android系统为什么不允许在子线程中访问UI?
    主要原因就是Android的UI空间并不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。而如果系统对UI控件的访问加上锁机制,则会大大降低UI访问的效率。因此就引入了Handler。

    Handler的工作流程:当Handler被创建完毕后,如果它的send()方法被调用,则它首先会去调用MessageQueue的enqueueMessage()方法并且将这个方法放入消息队列中,然后Looper发现又新消息到来时,就会去处理这个消息,最终消息中的Runnable或者Handler的handleMessage()方法就会被调用。

    二.Android消息机制分析

    1.ThreadLocal的工作原理

    概念:ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只能够在指定线程中获取到存储的数据,对于其他线程来说则无法获取到数据。

    ThreadLocal的内部实现:ThreadLocal是一个泛型类,它的定义为public class ThreadLocal<T>,而它内部主要是又set()和get()方法组成。

    • set方法
    public void set(T value){
        Thread currentThread = Thread.currentThread();
        Vlaues values = values(currentThread);
        if(vlaues == null){
            values = initializeVlaue(currentThread);  //对空值进行初始化
        }
        values.put(this,value);  //存储
    }
    

    在set()方法中,首先会通过values方法来获取当前线程中ThreadLocal数据,接着会对使用put()方法对ThreadLocal的值进行存储,在localValues内部有一个数组:private Obkect[] table,ThreadLocal的值就存在这个table数组中。

    • get方法
    public T get(){
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if(values != null){
            Object[] table = values.table;
            int index = hash & values.mask;
            if(this.reference == table[index]){
                return(T) table[index+1];
            }
        }
        return(T) values.getAfterMiss(this);
    }
    

    在get()方法中的逻辑就比较清晰,它主要是取出当前线程的localValues对象,如果这个对象为null,那么返回初始值;如果不为null,那就取出它的table数组并找出ThreadLocal的reference对象在table数组中的位置。

    从set和get方法中可以看出,ThreadLocal所操作的对象都是当前线程的localValues对象的table数组,因此在不同线程中访问同一个ThreadLocal的set和get方法都仅仅是局限在它们各自线程的内部。

    2.消息队列的工作原理

    概念:消息队列在Android中指的是MessageQueue,MessageQueue主要包含两个操作:插入和读取,对应的方法分别是enqueueMessage和next。

    • enqueueMessage方法
    boolean enqueueMessage(Message msg,long when){
        ...
        synchronized(this){
            ...
            msg.markUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if(p == null || when == 0 || when < p.when){
                msg.next = p;
                needWake = mBlocked;
            }
            else{
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for( ; ;){
                    prev = p;
                    p = p.next;
                    if(p == null || when < p.when){
                        break;
                    }
                    if(needWake && p.isAsynchronous()){
                        needWake = false;
                    }
                }
                msg.next = p;
                prev.next = msg;
            }
            if(needWake){
                nativeWake(mPtr);
            }
        }
        return false;
    }
    

    在enqueueMessage()方法中主要就是进行的单链表的插入操作。

    • next方法
    Message next(){
        ...
        int pendingIdleHandleCount = -1;
        int nextPollTimeoutMillis = 0;
        for( ; ;){
            if(nextPollTimeoutMillis != 0){
                Binder.flushPendingCommands();
            }
            nativePollOnce(ptr,nextPollTimeoutMillis);
            synchronized(this){
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if(msg != null && msg.target == null){
                    do{
                        prevMsg = msg;
                        msg = msg.next;
                    }while(msg != null && !msg.isAsynchronous());
                }
                if(msg != null){
                    if(now < msg.when){
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now,Integer.MAX_VALUE);
                    }
                    else{
                        mBlocked = false;
                        if(prevMsg != null){
                            prevMsg.next = msg.next;
                        }
                        else{
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if(false) Lod.v("");
                        return msg;
                    }
                }
                else{
                    nextPollTimeoutMillis = -1;
                }
            }
        }
    }
    

    next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里,如果有新消息到来时,next方法就会返回这条消息并将其从单链表中移除。

    3.Looper的工作原理

    概念:Looper在消息机制中的主要就是扮演者消息循环的角色,它会不停的从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就会阻塞在那里。

    Looper的创建:因为Handler的工作需要Looper,没有Looper的线程就会被报错,在一个线程中创建Looper的方法如下:

    new Thread("Thread#2"){
        @Override
        public void run(){
            Looper.prepare();  //创建Looper
            Handler handler = new Handler();
            Looper.loop();  //开启Looper的消息循环
        };
    }.start();
    

    除了Looper的的perpare()的方法之外,系统还提供了prepareMainLooper()方法,该方法主要是给主线程,即ActivityThread创建Looper使用的,不过其本质也是通过prepare来实现的。

    Looper的退出:Looper提供了quit和quitSafely来退出一个Looper,二者的区别是:quit会直接退出Looper,而quitSafely只是设定一个退出标记,只有把消息队列中所有的消息处理完毕之后才会安全的退出。Looper退出后,通过Handler发送的消息会失败。在子线程中,如果手动为其创建了Looper,那么在所有的事情完成之后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待的状态。

    4.Handler的工作原理

    概念:Handler的工作主要包含消息的发送和接收过程。消息的发送可以通过post以及send的一系列方法来实现(post的一系列方法最终是通过send的以系列方法来实现的);而消息的接收和处理主要是。下面来分别介绍。

    消息的发送:Handler消息的发送的典型过程如下:

    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg,0);
    }
    
    
    public final boolean sendMessageDelayed(Message msg,long delayMills){
        if(delayMills < 0){
            delayMills = 0;
        }
        return sendMessageAtTime(msg,SystemClock.uptimeMills() + delayMills);
    }
    
    
    public boolean sendMessageAtTime(Message msg,long uptimeMills){
        MessageQueue queue = mQueue;
        if(queue == null){
            RunTimeException e = new RunTimeException(this + "sendMessageAtTime() called with no mQueue");
            Log.w("");
            return false;
        }
        return enqueueMessage(queue,msg,uptimeMills);
    }
    
    
    private boolean enqueueMessage(MessageQueue queue,Message msg,long uptimeMills){
        msg.target = this;
        if(mAsynchronous){
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg,uptimeMills);
    }
    

    Handler的发送消息过程结束之后,MessageQueue的next方法就会返回这条信息给Looper,Looper收到消息后就开始处理了,不过最终还是会交由给Handler的dispatchMessage()方法来进行,之后就进入了处理消息阶段。

    消息的处理:Handler处理消息的过程如下:

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

    首先会判断Message的callback是否为null,如果不为null,则会调用handleCallback()方法来处理消息,handleCallback方法的实现如下:

    private static void handleCallback(Message message){
        message.callback.run();
    }
    

    接着还会检查mCallback是否为null,不为null就调用mCallback的handleMessage方法来处理消息,其中Callback是一个接口。最后调用Handler的handleMessage方法来处理消息。

    三.主线程的消息循环

    过程:Android的主线程就是ActivityThread,主线程的入口方法为main,在main方法中会进行Looper和MessageQueue的创建,以及Looper的开启的操作。

    public static void main(String[] args){
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
    
        if(sMainThreadHandler == null){
            sMainThreadHandler = thread.getHandler();
        }
    
        AsyncTask.init();
    
        if(false){
            Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG,"ActivityThread"));
        }
        Looper.loop();
    }
    

    主线程的消息循环开始了以后,ActivityThread还需要一个Handler来和消息队列进行交互,这个Handler就是ActivityThread.H,它内部定义了一组消息类型,主要包含了四大组件的启动和等待等过程。

    主线程消息循环模型:主线程通过ApplicationThread和AMS进行进程间的通信,AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到主线程中执行。

    相关文章

      网友评论

        本文标题:Android进阶(10)| Android的消息机制

        本文链接:https://www.haomeiwen.com/subject/mrsktctx.html