zoukankan      html  css  js  c++  java
  • Handler和Message以及Looper之间的三角关系

    说到Handler想必大家都经常用到,在非UI线程更新UI那可是利器,用起来也非常容易上手

    从使用上来说,我们只需要关注sendMessage和handleMessage即可

    所以我们先从Handler和Message来说起,先看一小段代码

        public static final int UPDATE_TEXT_VIEW = 0;
        public TextView mResultTextView = null;
    
        // new 一个 Handler 对象, 以内部类的方式重写 handleMessage 这个函数
        public Handler mMyHandler = new Handler() {
            // ③ 处理消息
            public void handleMessage(android.os.Message msg) {
                switch (msg.what) {
                    case UPDATE_TEXT_VIEW:
                        /* 这里可更新 ui */
                        mResultTextView.setText(String.valueOf(msg.arg1));
                        break;
    
                    default:
                        /* do nothing */
                        break;
                }
            };
        };
    
        // ==================================================================
        // 函数名: calc
        // 日期: 2015-08-30
        // 功能: 计算入参并显示在 UI 上,然后后续以每秒 +1 在 UI 上更新
        // 输入参数: int a
        // int b
        // 返回值: 无
        // 修改记录:千里草新增函数
        // ==================================================================
        public void calc(int a, int b) {
            int c = a + b;
            final Message msg = new Message();
            msg.what = UPDATE_TEXT_VIEW;
            msg.arg1 = c;
            // ① 发送消息
            mMyHandler.sendMessage(msg);
            // 这里更新 UI 线程的TextView OK
            // mResultTextView.setText(String.valueOf(msg.arg1));
    
            // 启动一个新的线程来每秒刷新 TextView
            new Thread() {
                public void run() {
                    while (true) {
                        msg.arg1++;
                        // ② 发送消息
                        mMyHandler.sendMessage(msg);
    
                        // mResultTextView.setText(String.valueOf(msg.arg1)); 这里会报错
                        try {
                            sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
    
                };
            }.start();
        }

    代码中有①,②两处发送消息,. 分别是从UI线程和从子线程发送的消息, 在③处两个消息都可以被准确的接收到.

    在接受消息函数handleMessage 里我们可以做任何想做的事情,包括更新UI…

    从使用来说,大家根据这个例子,想必已经可以简单使用Handler来处理多线程之间的交互了…

    此处需要注意的是在使用Handler时,必须需要重写handleMessage 才能达到我们想达到的目的...(从逻辑上来说,没有接受消息的地方,发送的消息有何意义呢,是吧….从原理上..我们接下来会谈到……)

    谈到Handler,我们还可以用它执行一个Runnable多线程…例如下面的代码..

    mMyHandler.post(new Runnable() {
                
                @Override
                public void run() {
                    // 这里需要注意的是,calc 是在子线程里被调用,所以calc 就无法操作 UI 了.
                    calc(1, 2);
                }
            });

    Handler可真神奇,既可以发消息也可以执行某个任务线程.

    为了更深层次的了解它,只能去看看它的源码了.let’s go!

    先看看是如何post一个Runnable线程的

    public final boolean post(Runnable r)
        {
           //这里实际上是发送了一个Message
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
        
        //组装一个只带Runnable的Message
        private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }
        
        //发送消息
        public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }

    哎哟喂,这Handler小丫头片子,还假装两幅面孔呢, 经查看源码,发现它实际上是发送了一个Message消息,然而这个消息仅仅携带了一个Runnable对象.

    现在来看只需要分析Handler和Message了..我们来看看最终Handler是如何发送消息的..我们继续看源码,非得把他们扒个精光不可大笑

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
        {
            boolean sent = false;
            //消息队列,这里存储Handler发送的消息
            MessageQueue queue = mQueue;
            if (queue != null) {
                //Message与Handler绑定
                msg.target = this;
                //消息进队列
                sent = queue.enqueueMessage(msg, uptimeMillis);
            }
            else {
                RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
            }
            return sent;
        }

    从消息最终被发出的函数来看,这里只做了两个事情:

    1.Message与Handler绑定

    2.将Message放进消息队列里

    消息进队列之后,Handler发送消息的任务算是完美的完成了,接下来我们该介绍介绍Looper了,如果没有Looper,Handler和Message之间的爱情可以说是不完整的.

    我们先来看看一个Looper是如何使用的吧

    class LooperThread extends Thread {
                public Handler mHandler;
    
                public void run() {
                    // 为当前线程准备一个 Looper
                    Looper.prepare();
    
                    mHandler = new Handler() {
                        public void handleMessage(Message msg) {
                            // 处理消息
                        }
                    };
                    // Looper 开始工作
                      Looper.loop();
                }
            }
        //继续看源码,prepare执行之后sThreadLocal绑定一个Looper对象
        //sThreadLocal 能且只能绑定一个Looper对象
        public static void prepare() {
            prepare(true);
        }
    
        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));
        }
        
        /**
         * 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();
    
            //无条件的for循环,来遍历Messagequeue
            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
                Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                //Message绑定的Handler来开始处理消息.. 下面贴上dispatchMessage,函数,大家继续往下看
                  msg.target.dispatchMessage(msg);
    
                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.recycle();
            }
        }
        /**
         * Callback interface you can use when instantiating a Handler to avoid
         * having to implement your own subclass of Handler.
         */
        public interface Callback {
            public boolean handleMessage(Message msg);
        }
        
        /**
         * Subclasses must implement this to receive messages.
         * 子类必须完成handleMessage这个函数来接收消息
         */
        public void handleMessage(Message msg) {
        }
        
        /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            //收到消息后,先处理Runnable 对象callback.
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                //如果这里有回调接口,那么就直接调用该接口,不再继续调用Handler的handleMessage函数来处理消息
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }

    从上述加了中文注释的代码中能看出,Handler发送的Message通过Looper分发给了各自的Handler.

    所以上面说的在使用Handler时,必须需要重写handleMessage 才能达到我们想达到的目的...也不完全正确,

    我们也可以实现一个CallBack接口来处理消息,用Google的原话是

        /**
         * Callback interface you can use when instantiating a Handler to avoid
         * having to implement your own subclass of Handler.
         */
        public interface Callback {
            public boolean handleMessage(Message msg);
        }

    再贴最后一段代码来彻彻底底说明Handler Looper之间的关系, 那就是Handler的 构造函数

    /**
         * Default constructor associates this handler with the queue for the
         * current thread.
         *
         * If there isn't one, this handler won't be able to receive messages.
         */
        public Handler() {
            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
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            //与当前线程的消息队列绑定
            mQueue = mLooper.mQueue;
            mCallback = null;
        }

    一句话总结来说,Handler初始化时绑定了线程的MessageQueue,当前线程的Looper来依次分发MessageQueue里的消息.

    MessageQueue的消息会根据Message的target绑定的Handler被Looper分发到各自的Handler里去处理.

    例如Activity里有两个HandlerA和HandlerB, HandlerA发送的消息绝对不可能被HandlerB处理..

    先暂时写到这里吧,后续补上流程图和类图

    上述只是个人的很片面的理解,希望大家补充和指出不足的地方…

    2015年9月4日 04:10:05 千里草

  • 相关阅读:
    判断二叉树是否是完全二叉树
    二叉搜索树的第k个节点
    二叉树的深度
    二叉搜索树与双向链表
    二叉搜索树的后序遍历序列
    poj 2192 (DP)
    poj 1159 (DP LCS)
    poj 1934(LCS)
    《编程之美》读书笔记 -- 1.2中国象棋问题
    poj 1050(DP)
  • 原文地址:https://www.cnblogs.com/darin726/p/4781181.html
Copyright © 2011-2022 走看看