zoukankan      html  css  js  c++  java
  • Android的消息循环机制 Looper Handler类分析

    Looper类说明

       Looper 类用来为一个线程跑一个消息循环。

      线程在默认情况下是没有消息循环与之关联的,Thread类在run()方法中的内容执行完之后就退出了,即线程做完自己的工作之后就结束了,没有循环的概念。

      调用Looper类的 prepare() 方法可以为当前线程创建一个消息循环,调用loop() 方法使之处理信息,直到循环结束。

      大多数和消息循环的交互是通过 Handler 类进行的。

      下面是一个典型的实现:

    复制代码
      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();
          }
      }
    复制代码

    Handler类说明  

      Handler类用来发送和处理消息(Message)以及和线程的消息队列(MessageQueue)关联的Runnable对象。

      每一个Handler对象都仅和一个线程及这个线程的消息队列关联。

      一个特定线程的所有Handler对象都会收到同样的方法。(这是一个“一对多”的关系)。

      当你创建一个新的Handler对象,它会和创建它的这个线程/线程的消息队列绑定,从那个时刻开始,它将向这个消息队列传递消息和runnable对象,并且当它们从队列中出来时执行它们。

      Handler主要有两种用途:

      1.合理调度安排消息和runnable对象,使它们在将来的某个点被执行。

      2.将一个动作入队安排在非当前线程执行。

      调度消息是通过一系列的post方法和sendMessage方法。

      post方法允许你向消息队列中入队一些Runnable对象,在它们被接收到的时候会被调用,(实际上post方法也就是将runnable对象包装在消息里,然后再通过sendMessage方法实现),post方法有:

      post(Runnable r)

      postAtFrontOfQueue(Runnable r)

      postAtTime(Runnable r, Object token, long uptimeMillis)

      postAtTime(Runnable r, long uptimeMillis)

      postDelayed(Runnable r, long delayMillis)

      sendMessage方法允许你入队一个消息对象(Message),包含一个bundle数据,之后将会被Handler的handleMessage(Message)方法所处理。

      (这个需要你实现一个Handler的子类)。

      sendMessage方法有:

      sendEmptyMessage(int what)

      sendEmptyMessageAtTime(int what, long uptimeMillis)

      sendEmptyMessageDelayed(int what, long delayMillis)

      sendMessage(Message msg)

      sendMessageAtFrontOfQueue(Message msg)

      sendMessageAtTime(Message msg, long uptimeMillis)

      sendMessageDelayed(Message msg, long delayMillis)

      一个线程对应一个Looper,有一个消息队列,但是可以关联多个Handlers。

     

    UI线程和非UI线程的通信

      当你的应用进程被创建的时候,应用进程的主线程(main thread)就建立一个消息队列,操纵top级别的应用对象(比如activities、broadcast receivers等)和它们创建的任何窗口。

      因为效率的考虑,所有的View和Widget都不是线程安全的,所以相关操作强制放在同一个线程,这样就可以避免多线程带来的问题。这个线程就是主线程,也即UI线程。

      你可以创建自己的线程,通过一个Handler对象和应用的主线程通信。

      如果你将一个Handler和你的UI线程连接,处理消息的代码就将会在UI线程中执行。

      新线程和UI线程的通信是通过从你的新线程调用和主线程相关的Handler对象的post或者sendMessage方法实现的,给定的Runnable或Message将会在Handler的消息队列中,并且在合适的时间被处理。

      总的来说,共有5种方式从非UI线程和UI线程通信:

      还有就是通过Handler,或者使用AsyncTask。

      具体参见之前的博文:http://www.cnblogs.com/mengdd/p/3418780.html

    消息循环

      消息处理机制中,消息存放在一个消息队列中,而线程围绕这个队列进入一个无限循环,直到程序退出。

      如果队列中有消息,线程就会把消息取出来,并分发给相应的Handler进行处理;

      如果队列中没有消息,线程就会进入空闲等待状态,等待下一个消息的到来。

    Android的主线程循环创建

      Android程序的运行入口点可以认为是android.app.ActivityThread类的main()方法(源码2.3.3):

    复制代码
        public static final void main(String[] args) {
            // other codes...
    
            // 创建主线程循环
            Looper.prepareMainLooper();
            if (sMainThreadHandler == null) {
                sMainThreadHandler = new Handler();
            }
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
    
            // other codes...
    
            // 进入当前线程(此时是主线程)消息循环
            Looper.loop();
    
            // other codes...
    
            thread.detach();
            // other codes...
        }
    复制代码

      这个main()方法里面为程序创建了主线程循环

      Looper类中的主线程创建方法prepareMainLooper()

    复制代码
        /**
         * Initialize the current thread as a looper, marking it as an application's
         * main looper. The main looper for your application is created by the
         * Android environment, so you should never need to call this function
         * yourself. {@link #prepare()}
         */
    
        public static final void prepareMainLooper() {
            prepare();
            setMainLooper(myLooper());
            // other codes...
        }
    复制代码

      上面这个方法是专门为创建应用程序的主线程调用的,其他线程都不应该调用这个方法,而应该调用prepare()方法。

      主线程的Looper对象创建好之后会存在Looper类的成员变量mMainLooper里,通过一个get方法可以获取到:

    复制代码
        /**
         * Returns the application's main looper, which lives in the main thread of
         * the application.
         */
        public synchronized static final Looper getMainLooper() {
            return mMainLooper;
        }
    复制代码

      这样之后,程序中其他线程就可以获取主线程的消息循环对象,从而和主线程通信。

    线程创建消息循环:Looper.prepare()

      非主线程创建消息循环时,调用的是Looper类的prepare()方法,其实创建主线程的方法实质也调用了prepare方法:

    复制代码
        /**
         * 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()}.
         */
        public static final void prepare() {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException(
                        "Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper());
        }
    复制代码

      这个方法会调用Looper类的私有构造方法,创建Looper类对象。

    复制代码
        private Looper() {
            // 私有构造方法,在prepare()方法里面调用
            // 创建消息队列
            mQueue = new MessageQueue();
            mRun = true;
            // 当前线程
            mThread = Thread.currentThread();
        }
    复制代码

    进入消息循环:Looper.loop()

      不管是不是主线程,prepare之后需要调用Looper类的loop()方法,可以看作是进入消息循环:

    复制代码
    /**
         * Run the message queue in this thread. Be sure to call {@link #quit()} to
         * end the loop.
         */
        public static final void loop() {
            // 进入当前线程的消息循环
            Looper me = myLooper();
            MessageQueue queue = me.mQueue;
            while (true) {
    
                // 从队列中取出消息
                Message msg = queue.next(); // might block
    
                if (msg != null) {
                    if (msg.target == null) {
                        // No target is a magic identifier for the quit message.
                        return;
                    }
                    // other codes...
    
                    // 分发消息
                    msg.target.dispatchMessage(msg);
                    // 消息的target是Handler类型的对象
    
                    // other codes...
    
                    // 释放清理
                    msg.recycle();
                }
            }
        }
    复制代码

    消息分发和处理——Handler

      前面创建了消息循环,并且进入了这个循环,但是消息队列中的消息是如何加入和处理的呢?是通过Handler。

    Handler构造:

      Handler有几个构造重载,如果构造时不提供Looper类对象参数,会获取当前线程的Looper对象,即将当前线程的消息循环作为Handler关联的消息循环。

      前面说过,不是所有线程都有一个消息循环,所以如果当前线程没有消息循环,而构造Handler对象时又没有指定Looper对象,则会抛出一个运行时异常

            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }

      如果没有抛出异常,Handler对象构造好之后,它就关联了相应的Looper实例和消息队列实例,即完成绑定。

    消息发送:

      Handler对象的post方法和sendMessage方法本质上都是发送消息的方法(post类方法实质上是调用了sendMessage方法)。

      所谓发送消息就是把消息放入消息队列中的合适位置,并且把消息的target设置为本Handler对象

      (这里将消息加入队列,也有一些什么线程唤醒的事儿咱们不深入讨论了)。

      可以添加,也就相应地有一些移除方法。

    消息处理:

      在上面的Looper.loop()方法中,调用了消息对象target(即发送这个消息的Handler对象)的dispatchMessage()方法。

    复制代码
       /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
    
            // 首先,处理Message自己的callback,调用其run方法
            if (msg.callback != null) {
                handleCallback(msg);
            }
            else {
                // 其次,调用Handler自留的接口对象
                // 这个成员变量声明时的注释如下:
                /**
                 * Callback interface you can use when instantiating a Handler to
                 * avoid having to implement your own subclass of Handler.
                 */
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
    
                // 最后,调用handleMessage方法处理消息,Handler类中这个方法为空,子类可以重写这个方法
                handleMessage(msg);
            }
        }
    复制代码

      Handler类的handleMessage()方法默认实现为空:

        /**
         * Subclasses must implement this to receive messages.
         */
        public void handleMessage(Message msg) {
        }

      上面的代码中也解释了为什么一个消息队列可以关联很多个Handler对象,因为虽然队列只有一个,但是消息的target是当时把它加入的Handler对象。

      所以当队列中的消息处理的时候,也会找到当时送它来的Handler对象,调用其相应的dispatchMessage()方法,进而调用其中的handleMessage()方法或者mCallback成员的handleMessage()方法来进行处理。

    参考资料

      Handler:http://developer.android.com/reference/android/os/Handler.html

      Looper:http://developer.android.com/reference/android/os/Looper.html

      比较好的几个博文:

      Android应用程序线程消息循环模型分析:http://blog.csdn.net/luoshengyang/article/details/6905587

       Android应用程序消息处理机制(Looper、Handler)分析:http://blog.csdn.net/luoshengyang/article/details/6817933

      Android的消息队列模型:http://www.cnblogs.com/ghj1976/archive/2011/05/06/2038469.html

      Android中的Handler, Looper, MessageQueue和Thread:http://www.cnblogs.com/xirihanlin/archive/2011/04/11/2012746.html

      本博客其他相关博文:

      Android中的UI线程与非UI线程:http://www.cnblogs.com/mengdd/p/3418780.html

      说明:本文相关源码是Android 2.3.3版本的。

  • 相关阅读:
    I.MX6 简单电路模拟USB设备的插入
    I.MX6 查看baudrate确定是否被其他程序占用
    I.MX6 Ethernet MAC (ENET) MAC Address hacking
    I.MX6 MAC Address 导致的系统崩溃
    I.MX6 U-boot编译找不到用户目录
    I.MX6 i2c_data_write_byte ioctl error: I/O error
    I.MX6 Android shutdown shell command
    Where Are You Standing?
    byte[]和InputStream的相互转换
    jsp 获取cookie 的值的方法
  • 原文地址:https://www.cnblogs.com/xiaoxiaoshen/p/5214666.html
Copyright © 2011-2022 走看看