第二十六章 Looper, Handle, HandleThread
Looper
Looper
可以对应一个或多个handler
, handler
可以对应一个或多个message
.Looper拥有着整个message队列
Message
what
: 标志消息的int型代码obj
: 随消息发送的对象target
: 所需设置的目标handler
Handler
- 要处理消息及消息指定的任务, 就需要handler实例.
- 初始化handler, 并定义其在得到消息队列中的下载消息后应该执行的任务
由于onLooperPrepared
方法在Looper
首次检查消息队列之前调用, 故onLooperPrepared
是初始化handler的好地方.
mRequestHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_DOWNLOAD){
T target = (T) msg.obj;
Log.i(TAG, "Got a request for URL: " + mRequestMap.get(target));
handleRequest(target);
}
}
};
private void handleRequest(final T target) {
try{
final String url = mRequestMap.get(target);
if (url == null){
return;
}
byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
final Bitmap bitmap = BitmapFactory
.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
Log.i(TAG, "Bitmap created");
mResponseHandler.post(new Runnable() {
@Override
public void run() {
if (!Objects.equals(mRequestMap.get(target), url) ||
mHasQuit){
return;
}
mRequestMap.remove(target);
mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
}
});
} catch (IOException e) {
Log.e(TAG, "Error downloading image", e);
}
}
- 创建消息
1.从公共 回收池里获取消息, 而不用去反复创建新的message对象
handle.obtainMessage(int what, Object obj)
2..sendToTarget()
将自身自动设置为目标handle. 不应手动设置消息的目标handler
mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD, target)
.sendToTarget();
创建并启动后台线程
- 创建继承
HandlerThread
的类 - 在前台的
onCreate()
中实例化该类并启动它 - 调用
HandlerThead.getLooper()
方法
mThumbnailDownloader = new ThumbnailDownloader<>();
mThumbnailDownloader.start();
mThumbnailDownloader.getLooper();
- 在handler初始化完成的情况下
- 在上面提到的, 传递给Handler, 让其创建消息. 每次有新的任务, 就使用
handler.obtainMessage(int what, Object obj).sendToTarget();
的方法
- 后台发送给前台
在handleMessage(msg)
中, 每次处理完都可以去实现向前台发送消息, 让其处理相关事物
- 在前台创建handler实例, 该handler与主线程的looper相关联.
- 将该handler传递到后台中, 然后利用
handler.post(Runnable)
方法. Runnable
中的run
法会直接执行 - 主线程可以实现相关的回调接口
mResponseHandler.post(new Runnable() {
@Override
public void run() {
if (!Objects.equals(mRequestMap.get(target), url) ||
mHasQuit){
return;
}
mRequestMap.remove(target);
mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
}
});
mThumbnailDownloader.setThumbnailDownloadListener(
new ThumbnailDownloader.ThumbnailDownloaderListener<PhotoHolder>() {
@Override
public void onThumbnailDownloaded(PhotoHolder photoHolder, Bitmap bitmap) {
if (bitmap != null){
Drawable drawable = new BitmapDrawable(getResources(), bitmap);
photoHolder.bindDrawable(drawable);
}else{
photoHolder.bind();
}
}
});
清理后台进程
- 在
onDestory()
中清理后台进程: 使用handlerThread.quit();
@Override
public void onDestroy() {
super.onDestroy();
mThumbnailDownloader.quit();
Log.i(TAG, "Background thread destroyed");
}
- 由于屏幕旋转,
PhotoHolder
视图失效, handlerThread
会挂起, 从而会引发异常.
故要在onDestoryView()
中清除相关请求
@Override
public void onDestroyView() {
super.onDestroyView();
mThumbnailDownloader.clearQueue();
}
public void clearQueue(){
mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
mRequestMap.clear();
}
相关小记
ConcurrentHashMap
是一个线程安全的hashMap
ConcurrentHashMap.put(K key, V value);
ConcurrentHashMap.remove(Object key)
挑战练习
- 使用picasso库实现下载功能
- Android引入
StrictMode
帮助开发者探测代码问题 - 预加载与缓存