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(Runnabler)

  postAtFrontOfQueue(Runnabler)

  postAtTime(Runnabler,Objecttoken, long uptimeMillis)

  postAtTime(Runnabler, long uptimeMillis)

  postDelayed(Runnabler, long delayMillis)

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

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

  sendMessage方法有:

  sendEmptyMessage(int what)

  sendEmptyMessageAtTime(int what, long uptimeMillis)

  sendEmptyMessageDelayed(int what, long delayMillis)

  sendMessage(Messagemsg)

  sendMessageAtFrontOfQueue(Messagemsg)

  sendMessageAtTime(Messagemsg, long uptimeMillis)

  sendMessageDelayed(Messagemsg, 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线程通信:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

  还有就是通过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版本的。

更多相关文章

  1. [置顶] Android(安卓)studio build.gradle 各种错误解决总结
  2. 【DiskLruCache完全解析】Android(安卓)AdapterView图片硬盘缓存
  3. Androidの短信拦截方法详解
  4. Android(安卓)你对Handler了解多少?(使用详解篇)
  5. Android搜索过滤
  6. android前端和java后端通过RSA加密方式传递数据时出现javax.cryp
  7. Android(安卓)kotlin之对象和类(2)
  8. 五、android中解析xml
  9. Android(安卓)Scroll 详解

随机推荐

  1. 第四周Android实习笔记
  2. 裁判文书android app逆向
  3. Mono for Android V1.0 正式发布啦!
  4. 从linux看Android之一--init进程
  5. Android Scripting Environment -ASE
  6. Android(安卓)make 中变量记录
  7. Android——修改开机画面
  8. eclipse的dx问题
  9. Android 笔记
  10. Settings.System 和 SystemProperties