zoukankan      html  css  js  c++  java
  • Android 源码分析(四) Handler 异步消息机制

    一.Handler 使用方法:

     Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 1:
                        Log.i(TAG,"UI thread sendEmptyMessage...1");
                        break;
                    case 2:
                        Log.i(TAG,"thread sendEmptyMessage...2");
                        break;
                    case 3:
                        Log.i(TAG,"thread sendMessage...3");
                        break;
                    case 4:
                        Log.i(TAG,"UI thread sendMessage...4");
                        break;
                }
            }
        };
        
        
        handler.sendEmptyMessage(1);
    
        handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    handler.sendEmptyMessage(2);
    
                    Message msg = new Message();
                    msg.what = 3;
                    handler.sendMessage(msg);
                }
            }, 1000);
    Message msg2
    = new Message(); msg2.what = 4; handler.sendMessage(msg2);

    输出结果:

    二.源码分析

    从handler.sendMessage(msg)进去看看源码怎么调用的.
    //Handler.java
            
            //Looper.prepare()方法初始化了一个Looper对象并关联在一个MessageQueue对象,并且一个线程中只有一个Looper对象,只有一个MessageQueue对象。
            //Handler的构造方法则在Handler内部维护了当前线程的Looper对象
        public Handler(Callback callback, boolean async) {
            ......
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            ......
        }
            
        // if we can get rid of this method, the handler need not remember its loop
        // we could instead export a getMessageQueue() method... 
        public final Looper getLooper() {
            return mLooper;
        }
    
        /**
         * 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.
         *  
         * @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.
         */
        public final boolean sendMessage(Message msg)
        {
            //参数 0 代表,无延迟.
            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.
         */
        public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
        
        /**
         * Enqueue a message into the message queue after all pending messages
         * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
         * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
         * Time spent in deep sleep will add an additional delay to execution.
         * You will receive it in {@link #handleMessage}, in the thread attached
         * to this handler.
         * 
         * @param uptimeMillis The absolute time at which the message should be
         *         delivered, using the
         *         {@link android.os.SystemClock#uptimeMillis} time-base.
         *         
         * @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.
         */
        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;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
        //msg.target就是Handler对象本身;而这里的queue对象就是我们的Handler内部维护的Looper对象关联的MessageQueue对象.
    
    
    //MessageQueue.java
        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;
        }
    //Looper.java
        /**
         * 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();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;
    
            // 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();
            final long ident = Binder.clearCallingIdentity();
    
            //Looper.loop()方法里起了一个死循环,不断的判断MessageQueue中的消息是否为空,如果为空则直接return掉,然后执行queue.next()方法:
            for (;;) {
                Message msg = queue.next(); // might block
                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
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
    
                final long traceTag = me.mTraceTag;
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
                final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                final long end;
                try {
                    //msg.target就是Handler对象本身
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                if (slowDispatchThresholdMs > 0) {
                    final long time = end - start;
                    if (time > slowDispatchThresholdMs) {
                        Slog.w(TAG, "Dispatch took " + time + "ms on "
                                + Thread.currentThread().getName() + ", h=" +
                                msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                    }
                }
    
                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) {
                    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();
            }
        }

    又回到Handler.java

    //Handle.java
        /**
         * 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);
            }
        }
    
        private static void handleCallback(Message message) {
            message.callback.run();
        }
        

    补充: UI线程 调用 Loop.

        //ActivityThread.java
        public static void main(String[] args) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
            SamplingProfilerIntegration.start();
    
            // CloseGuard defaults to true and can be quite spammy.  We
            // disable it here, but selectively enable it later (via
            // StrictMode) on debug builds, but using DropBox, not logs.
            CloseGuard.setEnabled(false);
    
            Environment.initForCurrentUser();
    
            // Set the reporter for event logging in libcore
            EventLogger.setReporter(new EventLoggingReporter());
    
            // Make sure TrustedCertificateStore looks in the right place for CA certificates
            final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
            TrustedCertificateStore.setDefaultUserDirectory(configDir);
    
            Process.setArgV0("<pre-initialized>");
    
            Looper.prepareMainLooper();
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
    
            // End of event ActivityThreadMain.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            //主线程启动就调用了loop().
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

    三.总结:

    一.Handler,Looper,MessageQue三者关系        

      1.Looper:相当于消息的载体

           1) 它的内部有一个消息队列,也就是MessageQueue,Handler发送的所有消息都会走向这个消息队里。

           2) 它的Looper.loop方法是一个死循环,不断的从消息队列MessageQueue中取出消息。如果有消息存在就处理该消息,否则就阻塞。

        2.MessageQue:就是一个消息队列,可以向其中添加消息并处理消息。

        3.Handler其实就是发送消息处理消息的封装。它与Looper相关联,也就是说在Handler的内部可以找到Looper,找到了Looper就找到了相应的消息队列。因此Handler发送的消息都会走向MessageQueue。
          

     二.为什么不能在子线程中更新UI    

            如果在子线程更新UI,那么就会出现这样子的一种况:其中一个子线程更新界面还没有完成,另外一个子线程就又去更新UI了,这样子会造成子UI界面错乱。那么你可能会说,我们可以实行加锁机制啊,让更新UI的代码不能并发执行。如果每一个子线程都加锁,那么 毫无疑问程序的性能将会大大下降。因此主要的原因总结起来就是两点:

        (1)为了防止界面错乱

        (2)为了防止程序性能下降

         因此android不允许在子线程中更新UI。为了解决这个这个问题,android就设计出这样子的一套消息处理机制,让我们尽管在子线程中发送更新UI的消息,而不用去关心多线程问题。在主线程的消息队里中采取轮询更新处理。

     
  • 相关阅读:
    页面元素定位-CSS元素基本定位
    自动化测试理论知识以及相关框架
    flask自学
    关于测试报告的一些理解
    jenkins、k8s、docker学习
    常用装饰器
    正则
    pycharm设置展示运行结果行数以及python一些好用的模块
    python相关知识点3,线程锁、单例模式
    python相关知识点2(各种内置方法)
  • 原文地址:https://www.cnblogs.com/bugzone/p/handler.html
Copyright © 2011-2022 走看看