AsyncTask是执行后台线程的最简单方式,但它不适用于那些重复且长时间运行的任务。

1. Looper

Android中,线程拥有一个消息队列(message queue),使用消息队列的线程叫做消息循环(message loop)。消息循环会循环检查队列上是否有新消息。

消息循环由线程和looper组成,Looper对象管理着线程的消息队列。

主线程就是个消息循环,因此也拥有Looper,主线程的所有工作都是由其looper完成的,looper不断的从消息队列中抓去消息,然后完成消息指定的任务。

2. Message

消息是Message类的一个实例,它有好几个实例变量,其中有三个需要在实现时定义。

1.What:用户定义的int型消息代码,用来描述消息。

2.obj:随消息发送的用户指定对象。

3.target: 处理消息的Handler。

3. Handler

Message的目标(target)是Handler类的一个实例,Handler可看作message handler的简称,创建Message时,它会自动与一个handler相关联,message待处理时,Handler对象负责触发消息处理事件、

Handler不仅仅是处理Message的目标,也是创建和发布Message的接口。

4. 关系

Looper拥有Message收件箱,所以Message必须在Looper上发布或处理。为与Looper协同工作,Handler总是引用着它。

一个Handler仅与一个Looper相关联,一个Message也仅于一个目标Handler(也称作Message目标)相关联。Looper拥有整个Message队列,多个Message可以引用同一目标Handler。

多个Handler也可与一个Looper相关联,这意味着一个Handler的Message可能与另一个Handler的Message存放在同一消息队列中。

5. 使用Handler

通常不需要手动设置消息的目标Handler。创建信息时,调用Handler.obtainMessage()方法。当传入其他消息字段给他时,该方法会自动设置目标给Handler对象。

为避免创建新的Message对象,Handler.obtainMessage()方法会从公共循环吃获取消息。  

一旦取得Message,就可以调用sendToTarget()方法将其发送给它的Handler,然后Handler会将这个Message放置在Looper消息队列的尾部。

Looper取得消息队列中的特定消息后,会将它发送给消息目标去处理。消息一般是在目标的Handler.handleMessage()实现方法中进行处理。

6. HandlerThread

HandlerThread类帮我们完成了建立looper的过程,只要继承它就能省去一些工作。

public class ThumbnailDownloader<T> extends HandlerThread {
private static final String TAG = "ThumbnailDownloader";
private static final int MESSAGE_DOWNLOAD = 0; //标识下载请求
private Boolean mHasQuit = false;
private Handler mRequestHandler; //存储对Handler的引用,这个Handler负责在ThumbnailDownloader后台线程上管理下载请求消息队列。这个Handler也负责从消息队列里取出并处理下载请求消息。 //onLooperPrepared()在Looper首次检查消息队列之前调用的。
@Override
protected void onLooperPrepared(){
mRequestHandler = new Handler(){
@Override
public void handleMessage(Message msg){ //队列中的下载消息取出并可以处理时,就会触发调用Handler.handleMessage()方法。
//处理操作
}
};
} public void queueThumbnail(T target, String url) {
//当传入其他消息字段给它时,该方法会自动设置目标给Handler对象(obtainMessage)
//sendToTarget()方法将Message发送给它的Handler,然后Handler会将这个Message放置在Looper消息队列的尾部。
mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD,target).sendToTarget();

public void clearQueue(){
mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
} }

主线程中这样调用:

mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);
mThumbnailDownloader.start();
mThumbnailDownloader.getLooper(); //在start()方法之后调用getLooper()方法是一种保证线程就绪的处理方式。可以避免潜在竞争。
// 在需要的时候调用
mThumbnailDownloader.queueThumbnail(holder, url);

7.线程交互

主线程现在能够适时调用这个线程的方法,用于下载图片了。但是还存在一个问题,那就是下载线程下载完一个任务以后如何更新视图呢?我们知道 UI 只能在主线程里更新,所以我们采用在主线程里声明一个 Handler,传递给下载线程,让下载线程在下载完成后在主线程执行更新操作。因为不能直接引用主线程的方法,故而在这里用到了回调。

7.1下载线程中:

// ThumbnailDownloader,也就是下载线程中

// 成员声明
private Handler mResponseHandler;
private ThumbnailDowloadListener<T> mThumbnailDownloadListener; // 回调接口
public interface ThumbnailDowloadListener<T> {
/*
* 图片下载完成,可以交给UI去显示时,接口中的方法就会被调用。
* 会使用这个方法把处理已下载图片的任务代理给另一个类(PhotoGalleryFragment),这样ThumbnailDownloader就可以把下载结果传给其他视图对象。
*/
void onThumbnailDownloaded(T target, Bitmap thumbnail);
} public void setThumbnailDownloaderListener(ThumbnailDowloadListener<T> listener) {
mThumbnailDownloadListener = listener;
} // 通过构造函数传递主线程的 Handler
public ThumbnailDowloader(Handler responseHandler) {
super(TAG);
mResponseHandler = responseHandler;
}

这样,主线程通过调用这些方法,就能够让下载线程获取到主线程的 Handler 和回调接口实例。

7.2主线程中

// 成员声明
private ThumbnailDowloader<PhotoHolder> mThumbnailDownloader; // 传递实例给下载线程
// 这个 Handler 在主线程中建立,所以是和主线程 Looper 相关联的
Handler responseHandler = new Handler();
mThumbnailDownloader = new ThumbnailDowloader<>(responseHandler);
mThumbnailDownloader.setThumbnailDownloaderListener(
new ThumbnailDowloader.ThumbnailDowloadListener<PhotoHolder>() {
@Override
public void onThumbnailDownloaded(PhotoHolder target, Bitmap thumbnail) {
Drawable drawable = new BitmapDrawable(getResources(), thumbnail);
target.bindDrawable(drawable);
}
}
);

8.线程交互

现在,通过 mResponseHandler,下载线程能够访问与主线程 Looper 绑定的 Handler。同时,还有 ThumbnailDownloadListener 使用返回的 Bitmap 执行 UI 更新操作。具体来说, 就是通过 onThumbnailDownloaded 实现,使用新下载的 Bitmap 来设置 PhotoHolder 的 Drawable。 
和在下载线程上把下载图片的请求放入消息队列类似,我们也可以返回定制 Message 给主线程,要求显示已下载图片。不过,这需要另一个 Handler 子类,以及一个 handleMessage(…) 覆盖方法。方便起见,我们转而使用另一个方便的 Handler 方法——post(Runnable)。

mResponseHandler.post(new Runnable() {
@Override
public void run() {
if (mRequestMap.get(target) != url ||
mHasQuit) {
return;
} mRequestMap.remove(target);
mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
}
});

在这里,新建的 Runnable 对象会被当成 Message 的回调方法,直接执行 run() 方法,所以相当于发送一个消息,里面写明了怎么做,而不是把对象和消息类型发给 Handler,让 Handler 决定怎么做。

安卓权威编程指南-笔记(第24章 Looper Handler 和 HandlerThread)的更多相关文章

  1. 安卓权威编程指南 -笔记(19章 使用SoundPool播放音频)

    针对BeatBox应用,可以使用SoundPool这个特别定制的实用工具. SoundPool能加载一批声音资源到内存中,并支持同时播放多个音频文件.因此所以,就算用户兴奋起来,狂按按钮播放全部音频, ...

  2. 安卓权威编程指南 -笔记(18章 处理assets)

    resources资源可以存储声音文件,但当处理多个音乐文件时,效率会很低. assets可以被看作随应用打包的微型文件系统,支持任意层次的文件目录结构.类似游戏这样需要加载大量图片和声音资源的应用通 ...

  3. 安卓权威编程指南-笔记(第21章 XML drawable)

    在Andorid的世界里,凡事要在屏幕上绘制的东西都可以叫drawable,比如抽象图形,Drawable的子类,位图图形等,我们之前用来封装图片的BitmapDrawable就是一种drawable ...

  4. 安卓权威编程指南-笔记(第27章 broadcast intent)

    本章需求:首先,让应用轮询新结果并在有所发现时及时通知用户,即使用户重启设备后还没有打开过应用.其次,保证用户在使用应用时不出现新结果通知. 1. 一般intent和broadcast intent ...

  5. 安卓权威编程指南-笔记(第23章 HTTP与后台任务)

    1. 网络连接基本 //通过指定URL获取原始数据,并返回一个字节流数组. public byte[] getUrlBytes(String urlSpec)throws IOException{ / ...

  6. 安卓权威编程指南-笔记(第22章 深入学习intent和任务)

    本章,我们会使用隐式intent创建一个替换android默认启动器的应用.名为NerdLauncher. NerdLauncher应用能列出设备上的其他应用,点选任意列表项会启动相应应用. 1. 解 ...

  7. 安卓权威编程指南 挑战练习 25章 深度优化 PhotoGallery 应用

    你可能已经注意到了,提交搜索时, RecyclerView 要等好一会才能刷新显示搜索结果.请接受挑战,让搜索过程更流畅一些.用户一提交搜索,就隐藏软键盘,收起 SearchView 视图(回到只显示 ...

  8. 安卓权威编程指南 - 第五章学习笔记(两个Activity)

    学习安卓编程权威指南第五章的时候自己写了个简单的Demo来加深理解两个Activity互相传递数据的问题,然后将自己的学习笔记贴上来,如有错误还请指正. IntentActivityDemo学习笔记 ...

  9. 安卓权威编程指南 挑战练习(第26章 在 Lollipop 设备上使用 JobService)

    26.11 挑战练习:在 Lollipop 设备上使用 JobService 请创建另一个 PollService 实现版本.新的 PollService 应该继承 JobService 并使用 Jo ...

随机推荐

  1. Scipy优化算法--scipy.optimize.fmin_tnc()/minimize()

    scipy中的optimize子包中提供了常用的最优化算法函数实现,我们可以直接调用这些函数完成我们的优化问题. scipy.optimize包提供了几种常用的优化算法. 该模块包含以下几个方面 使用 ...

  2. C#通过窗体应用程序操作数据库(增删改查)

    为了体现面向对象的思想,我们把“增删改查”这些函数封装到一个数据库操作类里: 为了便于窗体程序与数据库之间进行数据交互,我们建一个具有数据库行数据的类,通过它方便的在窗体程序与数据库之间传输数据: 我 ...

  3. tomcat高并发配置

    最近在项目中负责Tomcat高并发优化方案写一写新得. 优化1)tomcat默认的并发是75,可以启用线程池根据生产环境硬件设定线程池大小. <Executor name="tomca ...

  4. cf F. Shovels Shop

    https://codeforces.com/contest/1154/problem/F 给定m个折扣 每个折扣的{x,y}的意思是每次购买如果买到确切的x只铲子就其中的最便宜的y只铲子免付: 先贪 ...

  5. 实现hashmap

    /**数组下面挂着链表*/ #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include< ...

  6. Macroevolution|Silent changes|CNEs|Transposable elements|Neutral sites

    Interspecies genomic comparison 因为脊椎动物诞生早,在演化过程中有Macroevolution(因为自然选择或遗传漂变导致持续突变同时表型发生改变),但是存在一种基因缺 ...

  7. scala编程(八)——函数和闭包

    当程序变得庞大时,你需要一些方法把它们分割成更小的,更易管理的片段.为了分割控制流,Scala 提供了所有有经验的程序员都熟悉的方式:把代码分割成函数.实际上,Scala 提供了许多 Java 中没有 ...

  8. linux打开和关闭端口

    查看哪些端口被打开  netstat -anp 关闭端口号:iptables -A INPUT -p tcp -drop 8080 -j DROP 打开端口号:iptables -A INPUT -p ...

  9. 如何将EXCEL两列比较后不重复的数据复制到另一列上

    Q1:我有两列数据,需要做重复性比较,比较完后需要将不重复的数据提取出来自成一列,请问该如何操作? 假如你要比较A列与B列数据是否重复,应该有三种结果(即AB皆有,A有B无,B有A无),可在C列存放A ...

  10. ckeditor深入挖掘吃透