其实原本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源码分析的更多相关文章

  1. Android HandlerThread 源码分析

    HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我又有一 个耗时任务需要执行,我们不得不重新创建线 ...

  2. IntentService使用以及源码分析

    一 概述 我们知道,在Android开发中,遇到耗时的任务操作时,都是放到子线程去做,或者放到Service中去做,在Service中开一个子线程来执行耗时操作. 那么,在Service里面我们需要自 ...

  3. Android HandlerThread源码解析

    在上一章Handler源码解析文章中,我们知道App的主线程通过Handler机制完成了一个线程的消息循环.那么我们自己也可以新建一个线程,在线程里面创建一个Looper,完成消息循环,可以做一些定时 ...

  4. 【转载】Android异步消息处理机制详解及源码分析

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

  5. 【Android】IntentService & HandlerThread源码解析

    一.前言 在学习Service的时候,我们一定会知道IntentService:官方文档不止一次强调,Service本身是运行在主线程中的(详见:[Android]Service),而主线程中是不适合 ...

  6. Android源码分析-消息队列和Looper

    转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17361775 前言 上周对Android中的事件派发机制进行了分析,这次博主 ...

  7. Android异步消息传递机制源码分析

    1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...

  8. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  9. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

随机推荐

  1. Unity3D NGUI 给button按钮添加单间事件

    Unity3D中, NGUI 给button按钮添加单间事件的方法很多,在这里只给推荐一种比较常用的方法. 推荐方法:使用UIListener. 1.给button组价添加上UIListener.选择 ...

  2. Effective objective-c 2.0阅读笔记

    这本书非常的好,看完后,感触挺深,总结纪录一下,针对ios开发的备忘: 注:分类和原著有些不同,自己总结学习用的,仅供参考.   系统篇: 了解oc起源:继承c,由Smalltalk演化而来.动态语言 ...

  3. TCP/IP之大明内阁---协议的制定

    个人感言:真正的知识是深入浅出的,码农翻身" 公共号将苦涩难懂的计算机知识,用形象有趣的生活中实例呈现给我们,让我们更好地理解.感谢"码农翻身" 公共号,感谢你们的成果, ...

  4. FlASK中的endpoint问题

    先贴一点有关的flask代码,时间有限,我慢慢扩充 以下是flask源码中app.py中add_url_rule的代码. 主要是view_func  -- endpoint -- url 之间的对应关 ...

  5. Encrypting bootloader (程序BIN文件加密及在线升级)

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 在上一个博客随笔,我介 ...

  6. tomcat7/8 启用调试模式,可进行远程调试

    tomcat7,和 tomcat6 的jpda 不一样,tomcat7已经把jpda配置的属性在catalina.sh/catalina.bat里面已经写好了,我们不需要向tomcat6那样去设置参数 ...

  7. <读书笔记>软件调试之道 :从大局看调试-零容忍策略

    声明:本文档的内容主要来源于书籍<软件调试修炼之道>作者Paul Butcher,属于读书笔记.欢迎转载! ---------------------------------------- ...

  8. BitLocker 加密工具挂起和恢复命令行(windows7)

    如果你的硬盘使用BitLocker加密了,但是有时候需要高效率的硬盘做某些事情,可以暂时挂起加密,命令行如下方便做个bat. 挂起: manage-bde -protectors -disable C ...

  9. 数组中第K小的数字(Google面试题)

    http://ac.jobdu.com/problem.php?pid=1534 题目1534:数组中第K小的数字 时间限制:2 秒 内存限制:128 兆 特殊判题:否 提交:1120 解决:208 ...

  10. 转:python中对list去重的多种方法

    对一个list中的新闻id进行去重,去重之后要保证顺序不变. 直观方法 最简单的思路就是: ids = [1,2,3,3,4,2,3,4,5,6,1] news_ids = [] for id in ...