Android中的消息机制-源码分析
这个话题相信大家都不陌生,本人权当是一次比较系统的梳理。
Handler的几种消息发送方式
在Android中最常用的消息通信机制,即子线程和UI线程之间的通信,我们一般是基于Handler机制实现的,那么下面来看一看Handler的几种发送消息的方式。
1.sendMessage(Message message)
最常用的一种方式,下面我们一层层走下去。最后走到了sendMessageAtTime这个方法。我们见到了MessageQueue,相信大家也都知道,这就是传说中的那个消息队列,那么这个队列到底在哪初始化的呢?
假如我们使用的是new Handler()这个构造函数, 那么最终是调用Handler(Callback callback, boolean async)这个构造函数,可以看到这么行代码 mQueue = mLooper.mQueue,瞬间明朗,原来是Looper中的。(可参见Looper)
2.post(Runnable)
当我们在子线程中执行完之后,然后只想在执行一段代码块,二不需进行一些判断等操作时,我们可以使用该方法。sendMessageDelayed(getPostMessage(r), 0); 按照我们传统的理解,Handler发送消息,Looper取消息,那么现在这里居然没有消息,很奇怪哈。
private static Message getPostMessage(Runnable r, Object token) { Message m = Message.obtain(); m.obj = token; m.callback = r; return m; }
你自己虽然没有取消息,但是系统已经帮你做了,并且还将你的Runnable放到了消息中
后面的流程就和普通的发送消息类似了,走到了enqueueMessage这步。
他有一行重要的代码msg.target = this;
3.在Handler的构造方法中自带CallBack
public Handler(Looper looper, Callback callback) { this(looper, callback, false); }
最后是这样的,mCallback = callback;
再看Looper
让我们一起来细细的研究一下Looper类中的几个方法。
大家都知道UI线程和我们自己线程的区别,这对待Looper上也是有差别的呢。prepareMainLooper方法说的很清楚了,在UI线程启动的时候,系统会主动为你准备好一个MainLooper,这也是我们为什么只需在子线程中将消息发送就可以了,而无需操心主线程中的Looper,但是,如果是我们往子线程中发送消息呢?那么我们就必须调用prepare方法了,他会执行这么一句代码sThreadLocal.set(new Looper(quitAllowed));(可参见ThreadLocal)
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
MessageQueue有了。
下面再看看Looper的loop()方法,当我们在子线程中调用完prepare()后,便调用该方法,相当于开启轮询器,重要的方法在这里 msg.target.dispatchMessage(msg);
那么这里msg.target就是刚才发送消息的Handler啊,是时候看看dispatchMessage了
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
我们会先检查callback这个变量,如果我们是用过post方法处理的,那么就是有这个callback的,其实就是你放进去的Runnable,最后会走到这一步message.callback.run();
然后会检查有没有CallBack,通过方法3处理消息就会有这个CallBack,如果没有,才走handleMessage方法。
ThreadLocal是个什么玩意儿呢
上面的方法源码如下:
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }
1.拿到当前线程
2.拿到当前线程中的ThreadLocal.Values对象
3.将value放进去,键值即为当前的ThreadLocal
那么,我们可以这样理解,在每一个线程中都维护了一个ThreadLocal.Values对象,他相当于一个
集合,用于保存一些引用,那么当我们调用Looper.prepare()时,实际上是new出了一个Looper,然后保存到了ThreadLocal中的Values中。
更多相关文章
- [置顶] Android点击Button实现功能的几种方法
- Android架构实例分析之注册hello HAL的JNI方法表
- 经典Android试题及答案
- android 程序中运行main方法
- [Android] 单独编译生成boot.img时mkbootfs: No such file or di
- Android提供的系统服务之--WindowManager(窗口管理服务)
- ContentProvider详解
- Android(安卓)button 性能探讨
- 安卓(Android)实现选择时间功能