Android正在使用Handler实现信息发布机制(一)
上一篇文章,我们谈到了电话Handler的sendMessage方法,最后,我们将进入一个电话 sendMessageAtTime方法,例如下列:
    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);
    }
在这里。我们看到了MessageQueue和enqueueMessage等变量跟方法,我们能够想到,在Handler的实现的机制中,一定存在着一个消息队列。而它存放了我们创建的众多消息(Message)对象。
从这里,我们就会開始去探寻隐藏在 Handler对象后面的那一些我们想知道的实现机制了。
首先,我们还是从 Handler的创建開始说起,在上一篇文章,我们是通过 new Handler的方法来创建的,代码例如以下:
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_ID_1:
                Log.v("Test", "Toast called from Handler.sendMessage()");
                break;
            case MSG_ID_2:
                String str = (String) msg.obj;
                Log.v("Test", str);
                break;
            }
        }
    };
显然。我们要去看一下Handler的构造函数,例如以下:
    public Handler() {
        this(null, false);
    }
    public Handler(Callback callback) {
        this(callback, false);
    }
    public Handler(Looper looper) {
        this(looper, null, false);
    }
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
    public Handler(boolean async) {
        this(null, async);
    }
    public Handler(Callback callback, boolean async) {
        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 = callback;
        mAsynchronous = async;
    }
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
我们能够看到。真正实现的构造函数。事实上仅仅有以下两个。例如以下:
    public Handler(Callback callback, boolean async) {
        ....
    }
    public Handler(Looper looper, Callback callback, boolean async) {
        ...
    }
这两个的区别就在于是否有參数Looper。而Looper是一个线程相关的对象。
何谓线程相关的变量?就是线程间不能共享的对象,仅仅在本线程内有作用的对象。
那么Looper对象的作用是什么?
从我个人的理解,Looper类就是对MessageQueue的封装。它主要做的是两件事:
1)构造Looper对象,初始化MessageQueue。我们能够从其构造函数看到:
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
显然。MessageQueue正是在创建Looper的时候初始化的。
我们还注意到。这个构造函数是private的。而它则是被Looper.prepare方法调用的,例如以下:
    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));
    }
能够看到,Loop对象被创建之后,会被放到ThreadLocal变量中,而ThreadLocal正是线程局部变量,这说明了关于Looper的一个特性:
每个线程中都仅仅能有一个Looper对象。
2)调用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;
       ....
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
           ...
            msg.target.dispatchMessage(msg);
           ....
            msg.recycle();
        }
    }
从上面的代码中。我们能够看到,在一个无限循环中,会从MessageQueue中获得消息,然后通过msg.target.dispatchMessage(msg)方法调用。处理消息。最后将消息进行回收。
在这里。我们先不关心dispatchMessage方法。我们先跑一下题。看一下recycle方法里面做了什么事吧,例如以下:
private static Message sPool;
private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; /**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
} public void recycle() {
clearForRecycle(); synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
从上面的代码中。我们能够看到,Message对象本身有一个next的字段指向另外一个Message,也就是说,能够通过链表的方式将众多的Message给串起来,变成一个链表的消息池sPool。
而在这里,当调用recycle方法,就会将当前Message对象,先clearForRecycle之后。再加入到 sPool的头部中。而当我们通过Message的obtain方法的时候,我们事实上也是从sPool中拿 出一个空的Message对象。
相信看到这里,大家就了解了上一篇文章中我说。为什么建议大家使用Message.obtain方法去获取消息对象了吧。
接下来,我们再回到正题上。
从上面关于Handler的创建和关于Looper的描写叙述中,我们能够得出这样一个结论:
在每个线程中,假设我们要创建Handler,那么此线程中就必须有一个Looper对象,而这个Looper对象中又封装了一个MessageQueue对象来对Message进行管理。
所以,假设我们要在一个新线程中使用handler的话,我们就必须通过调用Loop.prepare()方法,为此线程创建一个Looper对象。官方给出的代码例如以下:
  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();
      }
  }
当然,仅仅有理论当然是不行的。我们还是得通过一个样例来看一下详细的效果,例如以下:
public class MainActivity extends ActionBarActivity {
    private static final int MSG_ID_1 = 1;
    private static final int MSG_ID_2 = 2;
    class LooperThread extends Thread {
        public Handler mHandler;
        public void run() {
            Looper.prepare();
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    Log.v("Test", "Id of LooperThread : " + Thread.currentThread().getId());
                    switch (msg.what) {
                    case MSG_ID_1:
                        Log.v("Test", "Toast called from Handler.sendMessage()");
                        break;
                    case MSG_ID_2:
                        String str = (String) msg.obj;
                        Log.v("Test", str);
                        break;
                    }
                }
            };
            Looper.loop();
        }
    }
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.v("Test", "Id of MainThread : " + Thread.currentThread().getId());
        LooperThread looperThread = new LooperThread();
        looperThread.start();
        while(looperThread.mHandler == null){
        }
        Message message = Message.obtain();
        message.what = MSG_ID_1;
        looperThread.mHandler.sendMessage(message);
        Message msg2 = Message.obtain();
        msg2.obj = "I'm String from Message 2";
        msg2.what = MSG_ID_2;
        looperThread.mHandler.sendMessage(msg2);
    }
}
相应的结果例如以下:
10-27 16:48:44.519: V/Test(20837): Id of MainThread : 1
10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 68421
10-27 16:48:44.529: V/Test(20837): Toast called from Handler.sendMessage()
10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 68421
10-27 16:48:44.529: V/Test(20837): I'm String from Message 2
那事实上在这里有一个问题,为什么我们寻常能够直接去创建Handler对象,而不须要去调用UI线程的Looper.prepare和loop等方法它?
当然,这当然是需要。这个步骤是仅Android系统帮我们做,所以默认主线程不再需要做这些初始化。
好,本文,我们了解到一个线程,Looper,Handler, MessageQueue 和 Message 等之间的一些相关,但主要用于Looper知识对象。
结束。
Android正在使用Handler实现信息发布机制(一)的更多相关文章
- Android正在使用Handler实现消息分发机制(零)
		
演讲前,AsyncTask文章.我们在最后谈到.AsyncTask它是利用Handler异步消息处理机制,操作结果.使用Message回到主线程,从而执行UI更新线程. 而在我们的日常开发工作,Han ...
 - Android正在使用Handler实现消息分发机制(两)
		
在开始这篇文章之前,.首先,我们在总结前两篇文章Handler, Looper和MessageQueue像一些关键点: 0)在创建线程Handler之前,你必须调用Looper.prepare(), ...
 - 深入源代码解析Android中的Handler,Message,MessageQueue,Looper
		
本文主要是对Handler和消息循环的实现原理进行源代码分析.假设不熟悉Handler能够參见博文< Android中Handler的使用>,里面对Android为何以引入Handler机 ...
 - Android中的Handler的机制与用法详解
		
概述: 很多android初学者对android 中的handler不是很明白,其实Google参考了Windows的消息处理机制, 在Android系统中实现了一套类似的消息处理机制.在下面介绍ha ...
 - 转:Android中的Handler的机制与用法详解
		
注:Message类的用法: message的几个参数都可以携带数据,其中arg1与arg2可以携带int类型,what是用户自定义的int型,这样接受者可以了解这个消息的信息. 说明:使用Messa ...
 - Android中的Handler,Looper,Message机制
		
Android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因 ...
 - Android消息传递之Handler消息机制
		
前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...
 - Android中利用Handler实现消息的分发机制(三)
		
在第二篇文章<Android中利用Handler实现消息的分发机制(一)>中,我们讲到主线程的Looper是Android系统在启动App的时候,已经帮我们创建好了,而假设在子线程中须要去 ...
 - Android Native -- Message/Handler/Looper机制(原理篇)
		
⌈Android Native消息队列处理系列文章⌋ Android Native -- Message/Handler/Looper机制(原理篇) Android Native -- Message ...
 
随机推荐
- MFC--自定义消息
			
在windows程序中,消息是一个重要的概念,最常见的消息一般都是以WM_开头,WM就是window message,窗口消息的缩写,通过处理标准的windows消息,我们可以改变窗口的外观,如使用W ...
 - form表单多值提交
			
$.ajax({ cache: true, type: "POST", url:ajaxCallUrl, data:$('#yourformid').serialize(),// ...
 - perl 异步请求和JS对比
			
perl 异步和js对比: /js************** $(function(){ function isPhone(str){ var regex = /[0-9]{11,11}/; ret ...
 - Codeforces Round #257 (Div. 2) B Jzzhu and Sequences
			
Jzzhu has invented a kind of sequences, they meet the following property: You are given x and y, ple ...
 - VirtualBox,Kernel driver not installed (rc=-1908)
			
http://hi.baidu.com/spt_form/item/316d6207b47b8ee03499020a VirtualBox,Kernel driver not installed (r ...
 - mysql update 有无索引对比
			
<pre name="code" class="html">mysql> desc ProductInfo; +--------------- ...
 - 贪心算法-找零钱(C#实现)
			
找零钱这个问题很清楚,无非就是始终拿可以取的最大面值来找,最后就使得张数最小了,这个实现是在假设各种面值足够多的情况下. 首先拖出一个界面来,最下面是一个listbox控件 对应的代码:问题比较简单, ...
 - ios 正則表達式替换
			
1. 不可变字符串 (content 是不可变) NSRegularExpression *regularExpression = [NSRegularExpression regularExpr ...
 - Android图片与旋转
			
拍照后的照片有时被系统旋转,纠正过程例如以下: 1.先读取图片文件被旋转的角度: /** * 通过ExifInterface类读取图片文件的被旋转角度 * @param path : 图片文件的路径 ...
 - Cocos2d-x 地图行走的实现3:A*算法
			
本文乃Siliphen原创,转载请注明出处:http://blog.csdn.net/stevenkylelee 上一节<Cocos2d-x 地图行走的实现2:SPFA算法>: http: ...