handler的源码分析
来源:互联网 发布:js中时间格式化字符串 编辑:程序博客网 时间:2024/05/17 01:50
package com.linh.handlercodeanalysisl;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.provider.Settings;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;import butterknife.BindView;import butterknife.ButterKnife;public class HandlerCodeAnalysisActivity extends AppCompatActivity { @BindView(R.id.tv_edit) TextView tvEdit; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { tvEdit.setText((String) msg.obj); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_code_analysis); ButterKnife.bind(this); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Message message1 = Message.obtain(); message1.obj = "这是源码"; handler.sendMessage(message1); Message message2 = Message.obtain(); message2.obj = "这是源码分析"; handler.sendMessageDelayed(message2,1000); Message message3 = Message.obtain(); message3.obj = "这是源码时间分析"; handler.sendMessageDelayed(message3,500); } }).start(); }}//现象,在1000毫秒之后显示的ui由helloworld变成了这是源码分析
MessageQueue消息队列
Handler.sendMessage ->sendMessageDelayed->Message.enqueueMessage
boolean enqueueMessage(Message msg, long when) { // 判断有没有 target if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } // 有没有在使用 if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } // 对当前消息队列加锁。 synchronized (this) { // 判断消息队列是否弃用(通常因为线程已死) if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } // 标记消息正在使用中 msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; // 第一次添加数据到队列中,或者当前 msg 的时间小于 mMessages 的时间 if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. // 把当前 msg 添加到链表的第一个 msg.next = p; mMessages = msg; needWake = mBlocked; } else { // 不是第一次添加数据,并且 msg 的时间 大于 mMessages(头指针) 的时间 // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { // 不断的遍历找到合适的位置 prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } // 把当前 msg 插入到列表中 msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
第一次进来的时候Mseeage p= null;
msg.next = p--->msg.next = null;
mMessage = msg;
目前为止没有看到handler去调用handleMessage()的方法,目前只是看到messequeue吧消息给了mMessage
总结,handler.sendMeassage其实将message加入到消息队列,队列采用链表的方式,按照when进行时间排序
为什么消息队列要采用链表的方式存储来存储message?
链表的要实现手拉手的数据结构,那么链表中的每一个元素肯定知道他的上一个节点元素和下一个节点元素分别是谁,在java中,弱化了指针的概念,因此,每一个节点元素持有上一个节点元素和下一个节点元素的对象的引用。也就是说,每一个节点对象应该有3个属性:1,value值; 2,上一个节点的引用; 3,下一个节点的引用。这三个属性是链表数据结构的核心,有了这三个属性,基本雏形就已经完成了。接下来,我们在每次添加或者删除或者插入一个新的节点的时候,只需要做2件事情:1,将前一个节点的next引用改变;2,将后一个节点的last引用改变。所以同数组相比,链表增删比较快
Loop消息循环
new Thread(){ @Override public void run() { Looper.prepare(); Handler handler = new Handler(); Looper.loop(); }}.start();
子线程中使用handler必须先要调用Looper.prepare()。不然会报错,我们在主线程中并没有调用Loop.prepare()。为什么不报错
因为我们应用启动的时候,activityThread的入口函数main()方法中已经帮我们写了这行代码
Looper.prepareMainLooper()//准备循环
public static void main(String[] args) { // ... 省略部分代码 Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");}
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}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));}public static @Nullable Looper myLooper() { return sThreadLocal.get();}
sThreadLocal.set(new Looper(quiteAllowed));
Thread t = Thread.currentThread();//从线程中获取ThreadLocalMap,一个线程一个ThreadLocalMapThreadLocalMap map = getMap(t);if(map != null){map.set(this,value)}
总结其实就是保证Thread线程中有且只有一个tlooper对象
这些代码相对就比较简单了,主要还是 ThreadLocal 的 set 方法,用来保证一个线程只有一个 Looper 对象,这样就保证了线程的安全。接下来看一下 Looper.loop() 这行:
public static void loop() { final Looper me = myLooper(); 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; } try { // 通过 target 去 dispatchMessage 而 target 就是绑定的 Handler msg.target.dispatchMessage(msg); } finally { // 消息回收循环利用 msg.recycleUnchecked(); } }}
阅读全文
0 0
- Handler的源码分析
- Handler的源码分析
- handler的源码分析
- handler机制的源码分析
- Handler消息机制的源码分析
- android中Handler的源码分析
- Handler Looper Message的源码分析
- android中Handler源码的分析文章
- handler ,Looper的机制,分析源码。
- 从源码的角度分析Handler机
- Handler消息机制的源码分析
- Handler 源码分析
- Looper、Handler源码分析
- Handler源码分析
- [Android源码]Handler分析
- handler 源码分析
- Android Handler源码分析
- Android Handler源码分析
- 小白学数据结构——五、查找(哈希表&布隆过滤器)
- hadoop live nodes: 0 的问题
- 【备忘】2017Spark 2.0大型项目实战:移动电商app交互式数据分析
- PlayTheBall游戏制作二
- 实验3-栈和队列——深度优先搜索——走迷宫(2449)
- handler的源码分析
- C++ explicit作用
- error: failed to push some refs to 'git@github.com:jack-don/elema_vue.git' hint: Updates were reject
- s3cmd --acl-public sync ./uploads/ s3://桶/uploads/
- 代码整洁之道读书笔记(一) -- 有意义的命名
- 食堂菜谱管理c++
- ElasticSearch5.X搜索条件的聚合(六)
- Spark独到见解--1、什么是Spark?及和MR的区别
- [编程题] 独立的小易