zoukankan      html  css  js  c++  java
  • Android源码分析笔记--Handler机制

    Handler机制#


    Handler机制实际就是实现一个 异步消息循环处理器

    Handler的真正意义: 异步处理

    Handler机制的整体表述:

    消息处理线程:

    在Handler机制中,异步消息处理线程启动后,该线程在Looper.loop()的影响下会进入无线循环。

    获取消息:

    在loop()方法的循环中,每循环一次,就从MessageQueue消息队列中取出一个消息。

    没有消息的时:

    如果消息队列没有消息。那么异步消息处理线程就会进入阻塞等待。

    处理消息:

    该线程中会回调Handler的handleMessage去处理取出的消息。

    消息的来源:

    消息来源于在消息处理线程中产生的Handler对象。 每个消息处理线程都可以有多个Handler(当然也可以没有)。Handler对象通过sendMessage方法去把Message放入MessageQueue队列。


    源码分析

    Looper

    1.Handler机制的开始:Looper.prepare()

    android.os.Looper

         /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      * Looper.prepare()把当前的线程初始化为一个循环器。
      * 这个Looper.prepare()给了当前线程一个创建 持有当前Looper引用的Handler对象的能力。
      * 当然,上述这一切必须发生在Looper.loop()前(因为在执行这句话的时候,调用该语句的当前线程就会进入无限循环中)
      * 注意事项:综上可知,loop要在最后调用。因为loop()之后的语句不会被执行到。
      * 可以采用quit()方法退出loop()这个无限循环。
      */
    public static void prepare() {
        prepare(true);
    }
    
    /*
    *
    *sThreadLocal是ThreadLocal<Looper>的实例对象。这个对象会利用当前线程名作为key,去存储当前线程的Looper对象。
    *该方法确保了当前线程只有唯一的一个Looper对象。如果多次调用prepare()则程序异常! 
    */
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
    /*
    *Looper对象的创建伴随MessageQueue的生成,结合上面的prepare()方法,意味着一个线程对应一个Looper对象,一个MessageQueue对象
    */
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    

    结合上述代码可以看出:

    Looper.prepare()为当前线程配备了一个Looper对象,一个MessageQueue对象


    2.为当前线程 生成对应的Handler对象(一定要在当前线程初始化)

    举个例子:

    	/**
     * Des:注意: 这里的代码把MyThread变成一个遵循Handler机制的  异步消息循环处理器。
     * Created by mahe 
     * Email: madahecoder@163.com
     */
    
    public class MyThread extends Thread {
        private static final String TAG = "MyThread";
    
    
        public Handler mH;
    
    	//注意:error!下面的注释是错误的初始化方法!因为调用MyThread的线程不是我们MyThread的代码要执行的线程!
    	//假如MyThread在MainActivity中调用new Thread().start(); 那么下面写的Handler其实是与主线程的异步消息处理器相绑定的!
    	//mH = new Handler() {
        //        @Override
        //        public void handleMessage(Message msg) {
        //            Log.d(TAG, "handleMessage:  start [msg]");
    	//
    	//            }
    	//        };
            
        @Override
        public void run() {
            
            Looper.prepare();
    
    		//初始化必须在本线程内执行
            mH = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    Log.d(TAG, "handleMessage:  start [msg]");
    
                }
            };
            Log.d(TAG, "run:  start []" + mH.getLooper().getThread());
            Log.d(TAG, "thread loop start");
            Looper.loop();
    
    
            Log.d(TAG, "thread loop");
        }
    
        public void send() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.d(TAG, "run: send Msg");
                    mH.sendMessage(new Message());
                }
            }).start();
        }
    }
    

    下面进入Handler看看做了什么:

    android.os.Handler

    /**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     * 默认构造器把生成的Handler对象和调用new Handler()时,Handler所在的当前线程的Looper进行了绑定。
     * 如果当前线程没有Looper,那么这个handler就不能够接受消息。并会抛异常!
     */
    public Handler() {
        this(null, false);
    }
    
    /**
     * Use the {@link Looper} for the current thread with the specified callback interface
     * and set whether the handler should be asynchronous.
     *
     * Handlers are synchronous by default unless this constructor is used to make
     * one that is strictly asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with respect to synchronous messages.  Asynchronous messages are not subject to
     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
     *
     * @param callback The callback interface in which to handle messages, or null.
     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
     *
     * @hide
     */
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {//这里FIND_POTENTIAL_LEAKS是常量false,所以内部代码忽略掉
            ...
        }
    
        mLooper = Looper.myLooper();//这里是重点
        if (mLooper == null) {//如果没有拿到looper对象那就抛异常
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    android.os.Looper

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     * 这里就根据当前线程的线程名拿到关联的唯一的Looper对象实例
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    

    总结:

    new Handler()把当前线程的Looper实例对象与Handler实例对象绑定


    3.当前线程进入无限循环,等待消息来临,并分发掉:Looper.loop()

    android.os.Looper

    /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         * 让消息队列运作起来!
         */
        public static void loop() {
            final Looper me = myLooper();//拿到当前线程唯一的Looper实例对象
            if (me == null) {//再次印证prepare()要先调用
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;//拿到Looper唯一对应的MessageQueue实例对象
    
            // Make sure the identity of this thread is that of the local process,
            // and keep track of what that identity token actually is.
            Binder.clearCallingIdentity();//清掉UID PID这些身份信息,确保当前线程是在本地进程进行后面的语句调用
            final long ident = Binder.clearCallingIdentity();
    
            for (;;) {
                Message msg = queue.next(); // might block,这里是从MQ中取消息,当然队列没消息的时候就会阻塞
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                // This must be in a local variable, in case a UI event sets the logger
                Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                msg.target.dispatchMessage(msg);//这里就是消息的分发,这里的target就是Handler,看到这里,并不知道target实例哪里来的,那就从Message的产生和发送去看
    
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
    
                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted.
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {//如果在分发消息前和分发消息后身份信息不一致那就出问题了,意味着要崩溃
    				/*
    				*	What a Terrible Failure: Report a condition that should never happen. The error will always
    				*	be logged at level ASSERT with the call stack. Depending on system configuration, a report *	may be added to the android.os.DropBoxManager and/or the process may be terminated 
    				*	immediately with an error dialog.
    				*/
                    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.recycleUnchecked();
            }
        }
    

    4.Handler发送Message到MQ的过程:

    android.os.Handler

    /**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     *  把消息放到消息队列的队尾,然后会在初始化这个handler的依附线程中的handleMessage方法中拿到消息
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     *         true 成功放进了消息队列
     *         flase 没有放进消息队列,主要原因是维护消息队列的looper正在退出中
     */
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    
    /**
         * Enqueue a message into the message queue after all pending messages
         * before (current time + delayMillis). You will receive it in
         * {@link #handleMessage}, in the thread attached to this handler.
         *  延迟发送消息
         * @return Returns true if the message was successfully placed in to the 
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.  Note that a
         *         result of true does not mean the message will be processed -- if
         *         the looper is quit before the delivery time of the message
         *         occurs then the message will be dropped.
         *         注意:true 放入MQ成功,但是并不意味着消息会被MQ分发出去。
         *         例如:looper调用了quit进行退出操作。然后handler这边延迟10s去发送消息
    		 *     那这里也会返回true,handler把消息发送了出去但是MQ会把这个消息会被丢弃。
         */
        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);
    }
    
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//在这里即将发给MQ前把msg的目标target指定为自身
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);//Handler实例把msg放进了消息队列
    }
    

    在MessageQueue中查看处理过程:

    android.os.MessageQueue

    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) {//一般情况下,放入MQ的动作实在其他线程做的异步操作,所以要加同步锁
            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) {//从这里可以看出: MQ里面的消息之间是采用单项链表的结构串在一起
    			//p就是previous。表示之前的消息。 
    			//如果p == null,说明我们传进来的msg是头一个。 如果when == 0 表示立即处理。 when < p.when表示插在p消息之前处理。
    			//以上情况都让本msg优先处理,也就是放在链表的头位置,mMessage就是链表头的位置
                // 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指向null
                    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;//这里的两句代码就是把当前的msg作为最后一个元素放进链表中。
            }
    
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    

    循环的作用是让最终prev指向链表中的最后一个元素,让p指向null。这里的两句代码就是把当前的msg作为最后一个元素放进链表中。
    可以画图看一下,会更加明显!

    这样msg就放进了MQ的单向链表中!


    6.回到Loop循环去看如何从MQ取出msg并分发

    android.os.Looper
    //精简后的核心逻辑是这样的
    public static void loop() {
    final Looper me = myLooper();

        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();
        }
    }
    

    进入queue.next()分析:

    android.os.MessageQueue

    //代码比较长,但并不是特别复杂
    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
    
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {//没有消息的时候nextPollTimeoutMillis为-1
                Binder.flushPendingCommands();
            }
    
            nativePollOnce(ptr,  nextPollTimeoutMillis);//没有消息的时候代码会阻塞在这里
    
            synchronized (this) {//MQ是有可能被其他线程访问的,所以要加锁
                // 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;//返回msg
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
    
                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
    
                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {//当没有消息时进去
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {//如果没有闲置处理器在跑那就置位mBlocked,进行等待
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
    
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
    
            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
    
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
    
            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;
    
            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }
    

    android.os.Looper

     //精简后的核心逻辑是这样的
     public static void loop() {
        final Looper me = myLooper();
        
        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,然后通过Handler分发出去
    
            msg.recycleUnchecked();
        }
    }
    

    android.os.Handler

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    这里进行分发处理。注意到,如果msg如果有回调处理,那么就不会交给其他的回调。

    回调优先级 msg.callback > mCallback > Handler.handlerMessage

  • 相关阅读:
    EasyUi TreeGrid封装
    Ionic项目中使用极光推送
    Win7搭建NodeJs开发环境
    NET 平台下的插件化开发内核
    访问数据库时如何解决并发问题
    async & await 的前世今生
    Linux环境编程相关的文章
    C# 5.0 Async函数的提示和技巧
    python算法题
    如何从数组中随机取出多个不重复的项
  • 原文地址:https://www.cnblogs.com/zharma/p/8328415.html
Copyright © 2011-2022 走看看