对于Android的异步消息处理机制,大家一定都不陌生,异步消息处理机制一个常见的应用场景就是在子线程中更新UI,我们都知道,Android的UI是线程不安全的,如果在子线程中直接更新UI,便会导致程序崩溃。对于该问题,常见的解决方法是,在UI线程新建一个Handler并覆写其handleMessage方法,在子线程中获取Message对象,通过Message对象的arg,obj字段以及setData()方法携带一些数据,之后利用UI线程的Handler将消息发送出去,最后便可以在handleMessage方法中获取到刚刚发送的消息并进行相应的处理了,示例代码如下:

public class MainActivity extends ActionBarActivity {    private Handler handler1=new Handler(){        @Override        public void handleMessage(Message msg){            //to do sth        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        new Thread(new Runnable(){            @Override            public void run() {                Message msg=Message.obtain();                msg.what=1;                Bundle bundle = new Bundle();                  bundle.putString("info", "info");                  msg.setData(bundle);                  handler1.sendMessage(msg);            }        }).start();    }}

这种方法相信大家都已经用得很熟了,注意,此时我们是在UI线程创建Handler的,那么现在我们尝试在子线程创建Handler,看看与之前的UI线程创建Handler有何区别,示例代码如下:

public class MainActivity extends ActionBarActivity {    private Handler handler2=null;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        new Thread(new Runnable(){            @Override            public void run() {                handler2=new Handler();            }        }).start();     }}

结果,系统竟然报错了,错误提示信息为Can't create handler inside thread that has not called Looper.prepare() 。意思是,不能在一个没有调用Looper.prepare()的线程内创建Handler。那么我们依据系统的意思,在创建Handler之前调用Looper.prepare()试试看:

new Thread(new Runnable(){    @Override    public void run() {        Looper.prepare();        handler2=new Handler();    }}).start(); 

果不其然,这回终于不报错了。但是,仅仅解决问题是不够的,我们更应该去探究问题背后所隐藏的原理,只有这样我们的能力才会有一个质的提升,所以下面,我将带领大家从源代码级别深入地探究Android的异步消息处理机制

OK,话不多说,让我们赶快进入这美妙的探索之旅吧~~~

前面已经讲到,在子线程中创建Handler时,如果事先不调用一下Looper.prepare(),系统便会报错。那么我们为什么一定要先去调一下Looper.prepare()呢?看来,只有Handler的构造函数以及Looper.prepare()的源代码能够告诉我们答案了,我们先从Handler的构造函数看起,Handler的构造函数源代码如下:

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

我们从mLooper = Looper.myLooper()这行代码看起,如果拿到的mLooper对象为空的话,便会抛出一个运行时异常,提示信息正是刚刚的“Can't create handler inside thread that has not called Looper.prepare()”,那么mLooper对象何时为空呢,这就要去看Looper.myLooper()中的代码了,Looper.myLooper()的代码如下:

public static final Looper myLooper() {      return (Looper)sThreadLocal.get();  }  

显然,这个方法是从sThreadLocal对象中拿出当前的Looper对象,如果拿不到的话则返回null。现在我们可以大胆猜测下在什么地方给sThreadLocal对象设置Looper的了,没错,就是在Looper.prepare()方法中,我们赶紧去看一下Looper.prepare()方法的源码:

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.prepare()中,会先去尝试获取sThreadLocal中的Looper对象,如果当前能够获取到Looper对象,则抛出运行时异常“Only one Looper may be created per thread”,这也说明了每个线程最多只能有一个Looper对象。如果获取不到Looper对象,则给sThreadLocal设置Looper对象。
说到这里,很多朋友可能都会有疑惑:为什么主线程中没有调用Looper.prepare(),却依然能够正常地创建Handler呢?
这是因为程序在启动时,系统已经自动帮我们调用了Looper.prepare()了,具体可以参照ActivityThread中的main()方法,这里就不去详述了,感兴趣的朋友可以去自行查阅。
我们继续去分析Handler中的源代码:

  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; 

可以看到,在利用Looper.myLooper()获取到Looper对象并赋值给Handler的成员变量mLooper之后,又将 mLooper的mQueue赋值给Handler的成员变量mQueue,由此可见,Handler中拥有着Looper和MessageQueue两个成员变量,Handler的构造函数的主要目的就是初始化这两个成员变量,同时,一个Looper对应着一个MessageQueue。
分析完Handler的构造函数以及Looper.prepare()的源代码,我们再来研究一下消息的发送流程,先温习一下消息发送的代码:

Message msg=Message.obtain();msg.what=1;Bundle bundle = new Bundle();  bundle.putString("info", "info");  msg.setData(bundle);  handler1.sendMessage(msg);

看到这里,我们不禁要问:Handler到底将消息发到哪里?为什么之后在handleMessage中又可以收到之前发送的消息?
我们知道,Handler有很多发送Message的方法,其中,除了sendMessageAtFrontOfQueue方法,其他方法都会辗转调用到sendMessageAtTime方法,sendMessageAtTime方法的源代码如下:

public boolean sendMessageAtTime(Message msg, long uptimeMillis)  {      boolean sent = false;      MessageQueue queue = mQueue;      if (queue != null) {          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;  }  

可以看到,sendMessageAtTime方法有两个参数,第一个参数为msg,代表着我们发送的消息,第二个参数为uptimeMillis,代表着发送消息的时间,其值为自系统开机到当前时间的毫秒数加上延迟时间,如果不是调用的sendMessageDelayed方法,则延迟时间为0.之后,将当前Handler的MessageQueue对象(即mQueue)取出,判断MessageQueue对象是否为空,若不为空,则将当前消息的target属性指向当前发送消息的Handler对象(即this),最后,调用我们MessageQueue对象的enqueueMessage方法让消息进入消息队列中。
这里要稍微解释下MessageQueue,顾名思义,MessageQueue就是一个消息队列,其内部会以一个队列的形式存储我们发送的消息,并提供了消息的入队和出队方法。
接下来就要分析较为关键的enqueueMessage方法了,enqueueMessage方法会将消息放入消息队列中,并按照消息发送的时间对消息进行排序,enqueueMessage方法的源代码如下:

final boolean enqueueMessage(Message msg, long when) {      if (msg.when != 0) {          throw new AndroidRuntimeException(msg + " This message is already in use.");      }      if (msg.target == null && !mQuitAllowed) {          throw new RuntimeException("Main thread not allowed to quit");      }      synchronized (this) {          if (mQuiting) {              RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");              Log.w("MessageQueue", e.getMessage(), e);              return false;          } else if (msg.target == null) {              mQuiting = true;          }          msg.when = when;          Message p = mMessages;          if (p == null || when == 0 || when < p.when) {              msg.next = p;              mMessages = msg;              this.notify();          } else {              Message prev = null;              while (p != null && p.when <= when) {                  prev = p;                  p = p.next;              }              msg.next = prev.next;              prev.next = msg;              this.notify();          }      }      return true;  }  

enqueueMessage方法有两个参数,第一个是入队的消息,第二个是该消息发送的时间。前面提到的sendMessageAtFrontOfQueue方法也会调用到enqueueMessage方法,但传入的消息发送时间为0。在enqueueMessage方法的内部,会将当前入队消息的when字段设置为传入的消息发送时间,取出当前的队头消息并赋给变量p,之后判断如果当前队头消息为空或消息发送时间为0或消息发送时间小于队头消息的时间,则将当前入队消息的next指针指向队头消息,之后将队头消息重新赋值为入队消息,从而完成了将入队消息插入到消息队列头部的操作。如果不满足上述的三种情况,则按照消息发送时间的先后顺序寻找入队消息在队列中的插入位置,之后将入队消息插入即可。
上面是对消息的入队操作的分析,那么出队操作在哪里呢?我们就要去分析一下Looper.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) {                  return;              }              if (me.mLogging!= null) me.mLogging.println(                      ">>>>> Dispatching to " + msg.target + " "                      + msg.callback + ": " + msg.what                      );              msg.target.dispatchMessage(msg);              if (me.mLogging!= null) me.mLogging.println(                      "<<<<< Finished to    " + msg.target + " "                      + msg.callback);              msg.recycle();          }      }  }  

在Looper.loop()中,会进入一个死循环,不断调用当前MessageQueue的next()方法取出下一条待处理的消息,如果当前没有待处理的消息则阻塞。之后,当消息不为空且消息的target字段不为空的话,调用消息的target字段的dispatchMessage方法,注意,此时消息的target字段就是当初发送这条消息的Handler对象。
接下来,我们进入到Handler的dispatchMessage方法的源代码中:

public void dispatchMessage(Message msg) {      if (msg.callback != null) {          handleCallback(msg);      } else {          if (mCallback != null) {              if (mCallback.handleMessage(msg)) {                  return;              }          }          handleMessage(msg);      }  }  

在Handler的dispatchMessage方法中,首先会去判断消息的callback字段是否为空,若不为空,则调用handleCallback方法对消息进行处理,若为空,则再去判断Handler的mCallback字段是否为空(Handler的无参构造函数中mCallback被设置为空),若不为空,则调用mCallback的handleMessage方法,若mCallback字段为空,则直接调用 handleMessage方法。
至此,我们已经从源代码级别回答了上面提出的两个问题:Handler到底将消息发到哪里?为什么之后在handleMessage中又可以收到之前发送的消息?相信大家一定都有很深的理解了吧_。
下面介绍一个Android异步消息处理线程的最标准的写法,此写法引自Android官方文档:

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

现在,我们来思考一个非常关键的问题:handleMessage方法何时运行在主线程中,何时运行在子线程中?
有不少朋友可能会说,handleMessage方法的定义位于主线程中,其就会在主线程中执行,handleMessage方法的定义位于子线程中,其就会在子线程中执行。
事实真的是这样吗?
其实不然,我先举个反例。HandlerThread就是一个典型的反例。我们在主线程中定义Handler并覆写其handleMessage方法,在定义Handler时,我们传入一个已经启动的HandlerThread对象的Looper作为参数,那么,我们此时的handleMessage方法是运行在子线程中的,但此时我们handleMessage方法的定义是位于主线程中的。
这是怎么回事呢?明明handleMessage方法的定义是位于主线程中的,怎么会运行在子线程里面呢?看来还得再次分析一下我们的源码,不过这一次,我们需要对源码进行逆向分析。
首先,我们知道,handleMessage方法是在dispatchMessage方法中被调用的,而dispatchMessage方法又是在Looper.loop()中调用的,也就是说,如果Looper.loop()运行在主线程,handleMessage方法也会运行在主线程,如果Looper.loop()运行在子线程,handleMessage方法也会运行在子线程。那么我们的Looper.loop()到底是运行在主线程,还是在子线程呢?其实,这就要看我们定义的Handler用的是哪个线程的Looper了,如果我们定义的Handler用的是主线程的Looper,那么它使用的MessageQueue自然也是主线程Looper对应的MessageQueue,通过该Handler发送的消息会进入该MessageQueue中,之后会在主线程的Looper对应的Looper.loop()方法中不断取出该MessageQueue中的消息进行处理,注意,此时我们主线程的Looper对应的Looper.loop()方法也是运行在主线程中的,所以此时我们的handleMessage方法也是运行在主线程中的。定义的Handler用的是子线程的Looper的分析过程同上。总结一下,如果创建Handler时用的是主线程的Looper,则相应的handleMessage方法会运行在主线程中,如果创建Handler时用的是子线程的Looper,则相应的handleMessage方法会运行在子线程中。
看到这里,相信大家已经对Android的异步消息处理机制有了一个非常深入的理解了,如果对文中内容有疑惑的话请在博客下方留言,我会尽可能地解答,谢谢大家,望大家多多支持!

参考:http://blog.csdn.net/guolin_blog/article/details/9991569

更多相关文章

  1. Android(安卓)数据存数---SQLite数据库
  2. 解析 Android(安卓)异步消息机制,深入了解 Handler、Looper、Mess
  3. Android之Service与IntentService的比较
  4. android 在子线程中使用Toast等功能
  5. Android中网络框架简单封装的实例方法
  6. Android中SparseArray性能优化的使用方法
  7. Android(安卓)Bitmap 缓存策略
  8. Android(安卓)消息传递机制分析
  9. 浅析Android事件分发

随机推荐

  1. Android(安卓)使用Get方式实现断点下载(
  2. android fragment lifecycle
  3. Android解决ImageView setRotation....等
  4. android中图片的拖拉和缩放
  5. Android自动测试之MonkeyRunner之monkeyr
  6. Android(安卓)RecyclerView在滑动的时候
  7. android分页查询垃圾短信数据库信息
  8. Android(安卓)内容提供者ContentProvider
  9. Android图形图像处理:添加涂鸦文字
  10. 让Android模拟器飞一会