zoukankan      html  css  js  c++  java
  • Android Handler消息机制源码解析

    好记性不如烂笔头,今天来分析一下Handler的源码实现

    Handler机制是Android系统的基础,是多线程之间切换的基础。下面我们分析一下Handler的源码实现。

    Handler消息机制有4个类合作完成,分别是Handler,MessageQueue,Looper,Message
    Handler : 获取消息,发送消息,以及处理消息的类
    MessageQueue:消息队列,先进先出
    Looper : 消息的循环和分发
    Message : 消息实体类,分发消息和处理消息的就是这个类

    主要工作原理就是:
    Looper 类里面有一个无限循环,不停的从MessageQueue队列中取出消息,然后把消息分发给Handler进行处理

    先看看在子线程中发消息,去在主线程中更新,我们就在主线程中打印一句话。

    第一步:
    在MainActivity中有一个属性uiHandler,如下:

        Handler uiHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if(msg.what == 100){
                    Log.d("TAG","我是线程1 msg.what=" + msg.what + " msg.obj=" + msg.obj.toString());
                }else if(msg.what == 200){
                    Log.d("TAG","我是线程2 msg.what=" + msg.what + " msg.obj=" + msg.obj.toString());
                }
            }
        };
    

    创建一个Handler实例,重写了handleMessage方法。根据message中what的标识来区别不同线程发来的数据并打印

    第二步:
    在按钮的点击事件中开2个线程,分别在每个线程中使用 uiHandler获取消息,并发送消息。如下

    
            findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //线程1
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            //1 获取消息
                            Message message = uiHandler.obtainMessage();
                            message.what = 100;
                            message.obj = "hello,world";
                            
                            //2 分发消息
                            uiHandler.sendMessage(message);
                        }
                    }).start();
    
                    //线程2
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            //1 获取消息
                            Message message = uiHandler.obtainMessage();
                            message.what = 200;
                            message.obj = "hello,android";
                            
                            //2 分发消息
                            uiHandler.sendMessage(message);
                        }
                    }).start();
                }
            });
    

    使用很简单,两步就完成了从子线程把数据发送到主线程并在主线程中处理
    我们来先分析Handler的源码

    Handler 的源码分析

    Handler的构造函数

       public Handler() {
            this(null, false);
        }
    

    调用了第两个参数的构造函数,如下

     public Handler(Callback callback, boolean async) {
            //FIND_POTENTIAL_LEAKS 为 false, 不走这块
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
    
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    主要是下面几句:
    mLooper = Looper.myLooper(); 调用Looper的静态方法获取一个Looper
    如果 mLooper == null ,就会抛出异常
    Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()";
    说明我们的线程中如果没有一个looper的话,直接 new Handler() 是会抛出这个异常的。必须首先调用 Looper.prepare(),这个等下讲Looper的源码时就会清楚了。

    接下来,把 mLooper中的 mQueue赋值给Handler中的 mQueue,callback是传出来的值,为null
    这样我们的Handler里面就保存了一个Looper变量,一个MessageQueue消息队列.

    接下来就是 Message message = uiHandler.obtainMessage();

    obtainMessage()的源码如下:

      public final Message obtainMessage()
        {
            //注意传的是一个 this, 其实就是 Handler本身
            return Message.obtain(this);
        }
    

    又调用了Message.obtain(this);方法,源码如下:

    public static Message obtain(Handler h) {
            //1 调用obtain()获取一个Message实例m
            Message m = obtain();
            
            //2 关键的这句,把 h 赋值给了消息的 target,这个target肯定也是Handler了
            m.target = h;
    
            //3 返回 m
            return m;
        }
    

    这样,获取的消息里面就保存了 Handler 的实例。
    我们随便看一下 obtain() 方法是如何获取消息的。如下

      public static Message obtain() {
            //sPoolSync同步对象用的
            synchronized (sPoolSync) {
                //sPool是Message类型,静态变量
                if (sPool != null) {
                    //就是个单链表,把表头返回,sPool再指向下一个
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            
            //如果sPool为空,则直接 new 一个
            return new Message();
        }
    

    obtain()获取消息就是个享元设计模式,享元设计模式用大白话说就是:
    池中有,就从池中返回一个,如果没有,则新创建一个,放入池中,并返回。

    使用这种模式可以节省过多的创建对象。复用空闲的对象,节省内存。

    最后一句发送消息uiHandler.sendMessage(message);源码如下:

     public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }
    

    sendMessageDelayed(msg, 0) 源码如下

       public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    

    又调用了sendMessageAtTime() 源码如下:

       public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            // Handler中的mQueue,就是前面从Looper.get
            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);
        }
    

    调用enqueueMessage()

        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            //注意这句,如果我们发送的消息不是 uiHandler.obtainMessage()获取的,而是直接 new Message()的,这个时候target为null
            //在这里,又把this 给重新赋值给了target了,保证不管怎么获取的Message,里面的target一定是发送消息的Handler实例
            msg.target = this;
            
            // mAsynchronous默认为false,不会走这个
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    最后调用queue.enqueueMessage(msg, uptimeMillis)源码如下:

        boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    // Inserted within the middle of the queue.  Usually we don't have to wake
                    // up the event queue unless there is a barrier at the head of the queue
                    // and the message is the earliest asynchronous message in the queue.
                    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; // invariant: p == prev.next
                    prev.next = msg;
                }
    
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    

    enqueue单词的英文意思就是 排队,入队的意思。所以enqueueMessage()就是把消息进入插入单链表中,上面的源码可以看出,主要是按照时间的顺序把msg插入到由单链表中的第一个位置中,接下来我们就需要从消息队列中取出msg并分了处理了。这时候就调用Looper.loop()方法了。

    Looper.loop()的源码我简化了一下,把主要的流程留下,方法如下:

      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;
    
    
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
            
                msg.target.dispatchMessage(msg);
               
    
                msg.recycleUnchecked();
            }
        }
    

    可以看到,loop()方法就是在无限循环中不停的从queue中拿出下一个消息
    然后调用 msg.target.dispatchMessage(msg) , 上文我们分析过,Message的target保存的就是发送的Handler实例,这我们的这个demo中,就是uiHandler对象。

    说白了就是不停的从消息队列中拿出一个消息,然后发分给Handler的dispatchMessage()方法处理。

    Handler的dispatchMessage()方法源码如下:

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

    可以看到,一个消息分发给dispatchMessage()之后
    1 首先看看消息的callback是否为null,如果不为null,就交给消息的handleCallback()方法处理,如果为null

    2 再看看Handler自己的mCallback是否为null,如果不为null,就交给mCallback.handleMessage(msg)进行处理,并且如果返回true,消息就不往下分发了,如果返回false

    3 就交给Handler的handleMessage()方法进行处理。

    有三层拦截,注意,有好多插件化在拦截替换activity的时候,就是通过反射,把自己实例的Handler实例赋值通过hook赋值给了ActivityThread相关的变量中,并且mCallback不为空,返回了false,这样不影响系统正常的流程,也能达到拦截的目的。说多了。

    前面分析了handler处理消息的机制,也提到了Looper类的作用,下面我们看看Looper的源码分析

    Looper源码分析

    我们知道,APP进程的也就是我们应用的入口是ActivityThread.main()函数。
    对这块不熟悉的需要自己私下补课了。

    ActivityThread.main()的源码同样经过简化,如下:

    文件位于 /frameworks/base/core/java/android/app/ActivityThread.java

    public static void main(String[] args) {
        //1 创建一个looper
        Looper.prepareMainLooper();
            
        //2 创建一个ActivityThread实例并调用attach()方法
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
    
        //3 消息循环
        Looper.loop();
    }
    

    可以看到,主线程中第一句就是创建一个looper,并调用了Looper.loop()进行消息循环,因为线程只有有了一个looper,才能消息循环,才能不停的从消息队列中取出消息,分发消息,并处理消息。没有消息的时候就阻塞在那,等待消息的到来并处理,这样的我们的app就是通过这种消息驱动的方式运行起来了。

    我们来看下 Looper.prepareMainLooper() 的源码,如下

        public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    

    第一句,调用了prepare(false)
    源码如下:

        private static void prepare(boolean quitAllowed) {
            //1 查看当前线程中是否有looper存在,有就抛个异常
            //这表明,一个线程只能有一个looper存在 
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            
            //2 创建一个Looper并存放在sThreadLocal中
            sThreadLocal.set(new Looper(quitAllowed));
        }
    

    sThreadLocal的定义如下
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    是一个静态的变量。整个APP进程中只有一个sThreadLocal,sThreadLocal是线程独有的,每个线程都调用sThreadLocal保存,关于sThreadLocal的原理,其实就是类似HashMap(当然和HashMap是有区别的),也是key,value保存数据,只不过key就是sThreadLocal本身 ,但是映射的数组却是每个线程中独有的,这样就保证了sThreadLocal保存的数据每个线程独有一份,关于ThreadLocal的源码分析,后面几章会讲。

    既然Looper和线程有关,那么我们来看下Looper类的定义,源码如下:

    /**
      * Class used to run a message loop for a thread.  Threads by default do
      * not have a message loop associated with them; to create one, call
      * {@link #prepare} in the thread that is to run the loop, and then
      * {@link #loop} to have it process messages until the loop is stopped.
      *
      * <p>Most interaction with a message loop is through the
      * {@link Handler} class.
      *
      * <p>This is a typical example of the implementation of a Looper thread,
      * using the separation of {@link #prepare} and {@link #loop} to create an
      * initial Handler to communicate with the Looper.
      *
      * <pre>
      *  class LooperThread extends Thread {
      *      public Handler mHandler;
      *
      *      public void run() {
      *          Looper.prepare();
      *
      *          mHandler = new Handler() {
      *              public void handleMessage(Message msg) {
      *                  // process incoming messages here
      *              }
      *          };
      *
      *          Looper.loop();
      *      }
      *  }</pre>
      */
    public final class Looper {
        .......
    }
    

    我们看上面的注释

      * <pre>
      *  class LooperThread extends Thread {
      *      public Handler mHandler;
      *
      *      public void run() {
      *          Looper.prepare();
      *
      *          mHandler = new Handler() {
      *              public void handleMessage(Message msg) {
      *                  // process incoming messages here
      *              }
      *          };
      *
      *          Looper.loop();
      *      }
      *  }</pre>
    

    这就是经典的Looper的用法 ,可以在一个线程中开始处调用 Looper.prepare();
    然后在最后调用 Looper.loop();进行消息循环,可以把其它线程中的Handler实传进来,这样,一个Looper线程就有了,可以很方便的切换线程了。
    下章节我们来自己设计一个Looper线程,做一些后台任务。

    Handler的消息机制源码就分析到这了

  • 相关阅读:
    idea创建Javaweb项目
    装配bean,基于xml
    IoC(控制反转)和DI(依赖注入)
    jar包介绍
    使用的基本方法
    struts2的常量
    jar包
    调用函数,模块
    Vue中的指令(听博主说总结的很好)
    React的核心概念
  • 原文地址:https://www.cnblogs.com/start1225/p/10010121.html
Copyright © 2011-2022 走看看