美文网首页
Android Handler分析

Android Handler分析

作者: SDY_0656 | 来源:发表于2017-10-26 17:30 被阅读0次
    我们经常说的Handler机制是一套包含Handler,Looper,MessageQueue的异步消息处理系统,今天就分析一下这套机制的原理。
    

    1.相关概念
    1)Message
    定义:消息,理解为线程间通讯的数据单元(Handler接受和处理的消息对象。)
    2)Message Queue
    定义:消息队列
    作用:用来存放通过Handler发过来的消息,按照先进先出执行
    提供enqueueMessage 方法,将消息根据时间放置到队列中;
    提供next方法,从队列中获取消息,没有消息的时候阻塞。
    3)Handler
    定义:Handler是Message的主要处理者
    作用:负责将Message添加到消息队列&处理Looper分派过来的Message
    提供sendMessage方法,将消息放置到队列中
    提供handleMessage方法,定义个各种消息的处理方式;
    4)Looper
    定义:循环器,扮演Message Queue和Handler之间桥梁的角色
    作用:主要负责消息循环:循环取出Message Queue的Message;消息派发:将取出的Message交付给相应的Handler。
    Looper.prepare():实例化Looper对象;为当前线程生成一个消息队列;
    Looper.loop() :循环从消息队列中获取消息,交给Handler处理;此时线程处于无限循环中,不停的从MessageQueue中获取Message 消息 ;如果没有消息就阻塞。

        几个需要注意的地方:
        1)每个线程中只能拥有一个Looper,但是一个Looper可以和多个线程的Handler绑定起来,也就是说很多个线程可以往一个Looper所持有的MessageQueue中发送消息。这就给我们提供了线程之间通信的可能。
        2)Handler在创建的时候可以显示指定Looper,这样在Handler在调用sendMessage()投递消息的时候会将消息添加到指定的Looper里面的MessageQueue。如果不指定Looper,Handler默认绑定的是创建它的线程的Looper。
      下面是Handler机制运行图:
    
    944365-783bc46b43828acd.png
      工作流程图解释:
      异步通信传递机制步骤主要包括异步通信的准备、消息发送、消息循环和消息处理
      1)异步通信的准备
       包括Looper对象的创建&实例化、MessageQueue队列的创建和Handler的实例化。
      2)消息发送
      Handler将消息发送到消息队列中
      3)消息循环
      Looper执行Looper.loop()进入消息循环,在这个循环过程中,不断从该Message Queue取出消息,并将取出的消息派发给创建该消息的Handler
      4)消息处理
      调用该Handler的dispatchMessage(msg)方法,即回调handleMessage(msg)处理消息。
    

    2.工作流程详解
    (1)Looper
    Looper主要负责:
    1)自身的创建&创建Message Queue
    2)消息循环(消息取出、派发)
    对应职责我们来看下相应的源码:
    1)自身的创建&创建Message Queue:prepare()方法

    public static final void prepare() {
    //判断sThreadLocal是否为null,否则抛出异常
    //即Looper.prepare()方法不能被调用两次
    //也就是说,一个线程中只能对应一个Looper实例
         if (sThreadLocal.get() != null) {
             throw new RuntimeException("Only one Looper may be created per thread");
         }
    //sThreadLocal是一个ThreadLocal对象,用于在一个线程中存储变量
    //实例化Looper对象并存放在ThreadLocal
    //这说明Looper是存放在Thread线程里的
         sThreadLocal.set(new Looper(true));
    }
    //再来看下Looper的构造方法
    private Looper(boolean quitAllowed) {
    //创建了一个MessageQueue(消息队列)
    //这说明,当创建一个Looper实例时,会自动创建一个与之配对的MessageQueue(消息队列)
         mQueue = new MessageQueue(quitAllowed);
         mRun = true;
         mThread = Thread.currentThread();
    }
    
        2)消息循环:loop()方法
            ```
    

    public static void loop() {
    //myLooper()方法作用是返回sThreadLocal存储的Looper实例,如果me为null,loop()则抛出异常
    //也就是说loop方法的执行必须在prepare方法之后运行
    //也就是说,消息循环必须要先在线程当中创建Looper实例
    final Looper me = myLooper();
    if (me == null) {
    throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //获取looper实例中的mQueue(消息队列)
    final MessageQueue queue = me.mQueue;

        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
    

    //进入消息循环
    for (;;) {
    //next()方法用于取出消息队列里的消息
    //如果取出的消息为空,则线程阻塞
    Message msg = queue.next(); // might block
    if (msg == null) {

                return;
            }
    
    
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
    

    //消息派发:把消息派发给msg的target属性,然后用dispatchMessage方法去处理
    //Msg的target其实就是handler对象,下面会继续分析
    msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
    
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }
    

    //释放消息占据的资源
    msg.recycle();
    }
    }
    ```
    总结一下Looper的作用:
    1)实例化本身、与当前线程绑定、创建与之相应的MessageQueue:prepare()方法,一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
    2)消息循环(消息取出、消息派发):loop()方法
    不断从MessageQueue中去取消息,派发给消息的target属性的Handler,然后调用相应Handler的dispatchMessage()方法进行消息处理。

    (2)Handler
    Handler主要负责:
    1)在子线程发送消息给MessageQueue
    2)处理Looper派发过来的消息
    Handler是需要和线程绑定在一起的,在初始化Handler的时候一般通过指定Looper对象从而绑定相应线程,即给Handler指定Looper对象=绑定到了Looper对象所在的线程中,Handler的消息处理回调会在那个线程中执行。一般有两种方法创建:
    1)通过Loop.myLooper()得到当前线程的Looper对象/通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。
    2)不指定Looper对象,那么这个Handler绑定到了创建这个线程的线程上,消息处理回调也就在创建线程中执行。
    首先看一下Handler的构造方法

    public Handler() {
           this(null, false);
    }
    public Handler(Callback callback, boolean async) {
           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());
               }
           }
    //通过Looper.myLooper()获取了当前线程保存的Looper实例,如果线程没有Looper实例那么会抛出异常
    //这说明在一个没有创建Looper的线程中是无法创建一个Handler对象的
    //所以说我们在子线程中创建一个Handler时首先需要创建Looper,并且开启消息循环才能够使用这个Handler。
           mLooper = Looper.myLooper();
           if (mLooper == null) {
               throw new RuntimeException(
                   "Can't create handler inside thread that has not called Looper.prepare()");
           }
    //获取了这个Looper实例中保存的MessageQueue(消息队列)
    //这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了
    
           mQueue = mLooper.mQueue;
           mCallback = callback;
           mAsynchronous = async;
       }
    
      上述说明:当Handler初始化时,可通过构造方法自动关联Looper和相应的MessageQueue。
       Handler发送消息有send和post两种,下面分别看一下这两种方法:
    
            1)send的发送方法:sendMessage()
    
    public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }
    
    //我们往下扒
       public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
            Message msg = Message.obtain();
            msg.what = what;
            return sendMessageDelayed(msg, delayMillis);
        }
    
    
     public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    
    
     public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    //直接获取MessageQueue
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
    //调用了enqueueMessage方法
            return enqueueMessage(queue, msg, uptimeMillis);
        }
    
    //调用sendMessage方法其实最后是调用了enqueueMessage方法
     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //为msg.target赋值为this,也就是把当前的handler作为msg的target属性
    //如果大家还记得Looper的loop()方法会取出每个msg然后执行msg.target.dispatchMessage(msg)去处理消息,其实就是派发给相应的Handler
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
    //最终调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
          2)Post的发送方法:sendMessage()
      ```
    

    showhandler.post(new Runnable() {
    @Override
    public void run() {
    String line = "\n";
    StringBuffer text = new StringBuffer(show.getText());
    text.append(line).append("angelababy:Yes,I do");
    show.setText(text);
    }

         相比send方法,post方法最大的不同在于,更新UI操作可直接在重写的run方法定义。
        其实Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:
    

    public final boolean post(Runnable r)
    {
    return sendMessageDelayed(getPostMessage(r), 0);
    }

    private static Message getPostMessage(Runnable r) {
    //创建了一个Message对象
    //创建Message对象可以new,也可以使用Message.obtain()方法;
    //但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。
    Message m = Message.obtain();
    //将我们创建的Runable对象作为callback属性,赋值给了此message.
    m.callback = r;
    //创建了一个Message对象
    return m;
    }

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

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    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);
    }

            从上面的源码发现了吧?和send中的handler.sendMessage是一样的。调用了sendMessageAtTime,然后调用了enqueueMessage方法,给msg.target赋值为handler,最终Handler将消息加入MessagQueue。
            但是细心的你会发现,在使用Post方法时会将我们创建的Runable对象作为callback属性赋值给了此message,那么msg的callback和target都有值,那么会执行哪个呢?我们已知回调发送消息的方法是:dispatchMessage()
    

    public void dispatchMessage(Message msg) {
    //一开始就会进行判断
    //如果msg.callback属性不为null,则执行callback回调,也就是我们的Runnable对象
    if (msg.callback != null) {
    handleCallback(msg);
    } else {
    if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
    return;
    }
    }
    handleMessage(msg);
    }
    }

          可以看到dispathMessage()方法里调用了 handleMessage()方法,但handleMessage()是一个空方法。
          因为Handler发送消息过来是希望进行一定的处理,至于怎么处理消息是该Handler最终控制的,所以我们在创建handler时需要通过复写handleMessage()方法从而实现我们需要的消息处理方式,然后根据msg.what标识进行消息处理。
           需要注意的地方:
    在一个Android应用启动的时候,会创建一个主线程,即ActivityThread(也叫UI线程),在ActivityThread中有一个静态的main方法:应用程序的入口点。
    

    //一个进程会默认生成一个主线程
    public static void main(String[] args) {
    ......
    //主线程生成时自动通过prepareMainLooper方法为主线程创建一个Looper
    //prepare()方法是用于在子线程中创建一个Looper对象,在子线程中是可以退出消息循环的:调用消息队列的quit方法
    //Looper生成时会自动生成与之配套的消息队列
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    ......
    //loop()方法开启消息循环
    //主线程的消息循环是不允许被退出的
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
    }

       (3)MessageQueue
              即消息队列,用于存放Handler发送过来的消息,为了提高插入删除的效率,采用单链表的方式实现。下面分别看一下MessageQueue的入队和出队操作。
             1)MessageQueue入队
    

    boolean enqueueMessage(Message msg, long when) {

    ......
    
    synchronized (this) {
    
        ......
    
        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;
    

    }

            消息的入队(插入)过程
             首先判断消息队列里有没有消息,没有的话则将当前插入的消息作为队头,并且这时消息队列如果处于等待状态的话则将其唤醒。
            若是在中间插入,则根据Message创建的时间进行插入。
            
            2)MessageQueue出队
    

    Message next() {

    ......
    
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
    

    // nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,这时候消息队列处于等待状态。   
    nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
    
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
    

    //按照我们设置的时间取出消息
    if (msg != null) {
    if (now < msg.when) {
    // Next message is not ready. Set a timeout to wake up when it is ready.
    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
    } else {
    // Got a message.
    mBlocked = false;
    if (prevMsg != null) {
    prevMsg.next = msg.next;
    } else {
    mMessages = msg.next;
    }
    msg.next = null;
    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
    msg.markInUse();
    return msg;
    }
    } else {
    // 如果消息队列中没有消息,将nextPollTimeoutMillis设为-1,下次循环消息队列则处于等待状态
    nextPollTimeoutMillis = -1;
    }

    //退出消息队列,返回null,这时候Looper中的消息循环也会终止。
    if (mQuitting) {
    dispose();
    return null;
    }

            ......
        }
    
        .....
    }
    

    }

    
    参考文章:http://www.jianshu.com/p/9fe944ee02f7
    
     
    
    

    相关文章

      网友评论

          本文标题:Android Handler分析

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