HandlerThread源码分析
其实原本HandlerThread的分析不应该单独开一篇博客的,应该在讲消息机制的那一片中一起分析。
但当时忘记了,而且今天第一次用MarkDown写博客,有点上瘾,就再来一篇,权当滥竽充数过过手瘾。
1.为什么会有HandlerThread
在使用Handler的时候,有的时候会报异常“Can’t create handler inside thread that has not called Looper.prepare()”
为什么会这样呢?回到Handler的源码我们会发现在handler函数中,有这样一段:
public Handler(Callback callback, boolean async) {
//...
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;
//...
}
问题的原因找到了,因为我们没有关联到handler所在线程的looper,在主线程中构建Handler时默认关联MainLooper,在其他线程中我们需要先调用Looper.prepare函数,通过ThreadLocal变量将Thread与Looper关联起来,然后在当前线程中构建Handler,就会与其相关联。
问了应对这种问题,就用了HandlerThread
官方文档是这样说的:
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
创建一个拥有Looper的线程,这个looper可以被用来创建handler,调用这个线程仍需要使用start方法。
2.如何实现
构造函数如下,参数分别是线程的名字和优先级
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
对于thread,关键的run方法:
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
首先调用Looper.prepare()方法将Looper与Thred关联起来,通过synchroized方法进行looper的同步控制,等到looper能使用的时候再使用,然后通知其他阻塞在此的线程。
既然HandlerThread的设计初衷就是设置一个带有Looper的线程,那么getLooper自然是这个家伙的重头戏
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
这个方法用来返回与这个线程相关联的Looper
先检测线程是否处于Alive状态,如果没有,则直接返回NULL。
在try..catch..中的wait()与上run方法中的noytifyall()相呼应、
即当线程处于ALIVE状态但是mLooper没有初始化完毕的时候,进入等待状态,直至mLooper初始完毕调用notifyAll方法唤醒。
最后返回mLooper.
关于此处的wait方法,我理解是Thread也是Object的子类,在主线程中调用getLooper方法时,会调用HandlerThread.wait(),此时如果mLooper没有创建成功,主线程会在这个HandlerThread对象上等待,直至创建成功后,唤醒等待在这个HandlerThread对象上的主线程,如果此时已经成功,就无需进入等待步骤,直接返回mLooper。
这样避免因为获取一个不存在的对象而引发的异常。
wait方法和sleep的区别,除了在于wait方法可以被唤醒外,就是wait会释放当前对象的锁,在文中场景就是:主线程释放HandlerThread的锁,让其去完成在run中创建Looper过程。
这里也能帮助理解wait和sleep的区别。
分析android的源码的一大好处,就是可以边分析边学习,巩固以前在脑海中仅仅是一个概念的知识,不同于一般开源项目的“不靠谱”,因为android是很多大牛智慧的结晶,基本上逻辑很完善,这样可以让自己把精力放在分析和学习上。
一点点进步,一起努力。
最后,上个例子:
public class MainActivity extends AppCompatActivity {
private HandlerThread handlerThread;
private ImageView imageView,imageView1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
imageView= (ImageView) findViewById(R.id.imageView);
imageView1= (ImageView) findViewById(R.id.imageView1);
handlerThread = new HandlerThread("MainActivity");
handlerThread.start();
final Handler handler = new Handler(handlerThread.getLooper());
//点击download开始进行下载
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.post(new MyRunable(1));
handler.post(new MyRunable(2));
}
});
}
class MyRunable implements Runnable {
int pos;
public MyRunable(int pos) {
this.pos = pos;
}
@Override
public void run() {
//模拟耗时
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (pos == 1) {
imageView.post(new Runnable() {
@Override
public void run() {
imageView.setBackgroundResource(R.mipmap.ic_launcher);
}
});
} else {
imageView.post(new Runnable() {
@Override
public void run() {
imageView1.setBackgroundResource(R.mipmap.ic_launcher);
}
});
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
handlerThread.quit();//停止Looper的循环
}
}
HandlerThread源码分析的更多相关文章
- Android HandlerThread 源码分析
HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我又有一 个耗时任务需要执行,我们不得不重新创建线 ...
- IntentService使用以及源码分析
一 概述 我们知道,在Android开发中,遇到耗时的任务操作时,都是放到子线程去做,或者放到Service中去做,在Service中开一个子线程来执行耗时操作. 那么,在Service里面我们需要自 ...
- Android HandlerThread源码解析
在上一章Handler源码解析文章中,我们知道App的主线程通过Handler机制完成了一个线程的消息循环.那么我们自己也可以新建一个线程,在线程里面创建一个Looper,完成消息循环,可以做一些定时 ...
- 【转载】Android异步消息处理机制详解及源码分析
PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...
- 【Android】IntentService & HandlerThread源码解析
一.前言 在学习Service的时候,我们一定会知道IntentService:官方文档不止一次强调,Service本身是运行在主线程中的(详见:[Android]Service),而主线程中是不适合 ...
- Android源码分析-消息队列和Looper
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17361775 前言 上周对Android中的事件派发机制进行了分析,这次博主 ...
- Android异步消息传递机制源码分析
1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
随机推荐
- (转)对《30个提高Web程序执行效率的好经验》的理解
阅读了博客园发布的IT文章<30个提高Web程序执行效率的好经验>,这30条准则对我们web开发是非常有用的,不过大家可能对其中的一些准则是知其然而不知其所以然. 下面是我对这些准则的理解 ...
- javaScript 相关笔记
1.js中对象复制 思路:将js对象先转成json字符串,然后再将json字符串转换为两个对象
- cs11_adventure c++_lab1
exercise1.cc #include <iostream> #include <vector> #include <stdlib.h> #include &l ...
- 关于Oracle数据库字符集
我们现在使用的字符集有以下两种: 推荐使用 AL32UTF8,避免以后数据导入导出字符集不同的麻烦. 推荐数据库设置参考图:
- An exception occurred while acquiring a poolable resource. Will retry.
我的是mysql-connector-java-5.1.20.jar加到lib下面就行了.或者在pom中加入配置也行.
- Kafka报错-as it has seen zxid 0x83808 our last zxid is 0x0 client must try another server
as it has seen zxid 0x83808 our last zxid is 0x0 client must try another server 停止zookeeper,删除datadi ...
- nginx https配置
nginx的https服务配置如下(在server中配置): ssl on; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl ...
- java 枚举
DK1.5引入了新的类型——枚举.在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便. 用法一:常量 在JDK1.5 之前,我们定义常量都是: publicstaticfianl... ...
- linux 学习 软件工具
_____secureCRT 远程通信工具 第二行第二个快速连接,输入主机名,即ip地址,和用户名,root .其它默认:端口22,协议ssh2,防火墙 none. 保存会话,连接.需要输入用户密码. ...
- CheckedComboBoxEdit 重置初始化值的方法
CheckedComboBoxEdit ccbgb; ccbgb绑定方式ccbgb .Properties .DataSource =数据集; ccbgb.Properties.DisplayMemb ...