安卓源码学习之Handler执行流程
来源:互联网 发布:python中数据清洗的库 编辑:程序博客网 时间:2024/05/22 13:16
学安卓也有一段时间了,一直都是做项目或者看别人的博客,自己从来没有写一片像样的文章来记录自己所学到的知识,现在发现如果把所学到得知识记录下来对知识也是一种温习,并且会掌握得更加牢固。
今天记录我所掌握的Handler消息机制执行流程,平时我们都会使用到Handler对象来进行延迟消息的处理或者结合线程来更新UI控件,但是对里面的原理也不是很了解,所以我今天看了一下源码,把看到的东西记录下来。
首先我们使用Handler肯定的new一个它的对象,那new的过程当中它都做了些什么事呢?于是乎我打开了源码:
//这里是它的午餐构造方法,它总共有几个构造方法,最终都是调用的这个构造//方法下面的构造方法public Handler() { this(null, false); } //最终调用的是这个构造方法public Handler(Callback callback, boolean async) { ... mLooper = Looper.myLooper();//这里可以看到获取了Looper对象 if (mLooper == null) { //如果looper对象为空,则抛出异常,这里先给出一点疑问,就是,为甚 //么Looper.myLooper()方法可能会为空呢 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//获取消息队列 mCallback = callback; mAsynchronous = async; }
当然,获取到Handler对象以后肯定是要使用它的,我们通过调用它的sendMessage(msg)方法或者其他sendXXX方法来发送消息,于是乎我们得有一个消息对象啊,我们可以这么做:
Message msg=new Message()或者Message msg=handler.obtainMessage();大家都知道下面这个方法效率更加高,这是为什么呢请看源码:
public final Message obtainMessage(){ return Message.obtain(this);}//最终handler.obtinMessage()会调用Message对象的这个方法:public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; } } return new Message();}//从上面可以看到,如果使用obtinMessage()方法会先往消息池里面取出,如果去不到则实例化一个返回,这就是为什么obtinMessage比newMessage效率高的原因!
好了,获取到Message对象以后肯定得发送消息啊,于是我们跟踪一下handler.sendMessage(Message msg)方法源码:
public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0);}public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}//最终调用了这个方法public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; ... //可以看到这里会对消息进行排序 return enqueueMessage(queue, msg, uptimeMillis); }
可以看到发送消息最终会调用enqueueMessage(queue, msg, uptimeMillis)方法,我们进入这个方法一看究竟:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; ... //这里调用了MessageQueue的enqueueMessage方法 return queue.enqueueMessage(msg, uptimeMillis);}根据MessageQueue的enqueueMessage方法:boolean enqueueMessage(Message msg, long when) { ... synchronized (this) { ... msg.when = when; Message p = mMessages; boolean needWake; //这里判断是否发送消息的延迟时间when 是否为0,如果不是0的话 //则将其加入到队列的前面 if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { 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.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; } //从上面的代码可以分析出如果出入的延迟时间为0则优先级最高//这里是从消息队列中取出消息以后底层调用的方法,好像从这里以后这个方法是系//底层调用的,查了一下资料貌似是一个linux管道设置的方法 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //这里调用了handleMessage方法 handleMessage(msg); } }
//于是调用了发送消息以后就会调用handlerMessage(Message msg)方法进行我们自己的代码处理了。
具体的流程图可以用如下图来表示:
引入上面我提出的问题,就是在Handler的无参构造方法里:
public Handler(Callback callback, boolean async) { ... mLooper = Looper.myLooper();//这里可以看到获取了Looper对象 if (mLooper == null) { //如果looper对象为空,则抛出异常,这里先给出一点疑问,就是,为甚 //么Looper.myLooper()方法可能会为空呢 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//获取消息队列 mCallback = callback; mAsynchronous = async; }
我提出的为什么Looper.myLooper();可能获取的Looper对象为空?首先想一下我们有时候在子线程中创建Handler对象的时候会发生什么情况呢?当然是报错了,这个报错的信息就是上面源码中的提示信息:”Can’t create handler inside thread that has not called Looper.prepare()”,那为什么主线程中new 一个Handler对象就不会报错呢?那是因为在主线程创建的时候系统就会跟着创建一个Looper对象,而子线程中不会主动创建Looper对象,当我们在子线程中先这样写:
Looper.prepare();....Looper.loop();
就不会报错了,这里贴出主线程创建Looper对象的源码:
public static void main(String[] args) { ... Looper.prepareMainLooper();//创建主线程的Looper对象 ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop();//循环取出消息
所以我们在主线程中new Handler不会报错,在子线程中需要加上Looper.prepare()和Looper.loop()方法的原因了。
具体的流程就分析完毕,如果不对的地方麻烦看的朋友指出一下,我写博客是为了把自己学习的东西记录下来,同时锻炼一下自己的表达能力,希望如果有不对的地方麻烦老师指点一下,谢谢!
- 安卓源码学习之Handler执行流程
- 安卓学习笔记之Handler
- 源码学习之Handler
- 安卓handler学习
- 安卓学习-Handler
- 安卓学习-Handler
- 安卓学习之路之Handler的简单实用
- 安卓开发-Handler学习
- 安卓Handler入门学习
- 安卓之handler机制
- 下载安卓源码流程
- 安卓handler消息机制源码解析
- angularjs源码分析之:angularjs执行流程
- angularjs源码分析之:angularjs执行流程
- springmvc----源码分析之springmvc执行流程
- 安卓源码学习
- android 学习之 Handler 源码初析
- 安卓Handler机制学习总结
- Tyvj p3070 动态排名 (动态区间第K大)
- 《剑指Offer》学习笔记--面试题52:构建乘积数组
- OpenSSL库的RSA使用
- 博客从wordpress迁移到Github.io、GitCafe
- 在VS2010配置并运行PBC库程序
- 安卓源码学习之Handler执行流程
- C++primer5.5-5.7 自增自减、箭头、条件操作符
- 通过中间服务器做代理(端口转发)远程连接数据库
- IOS常用第三方框架 --- PullToRefresh 下拉刷新
- error the @annotation pointcut expression is only supported at Java 5 compliance level or above 异常解决
- Ubuntu 下 Byobu 终端安装
- 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
- mysql 的存储引擎介绍
- 4412用户层调用驱动控制led灯