本文主要是介绍Looper、Handler与HandlerThread,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
子线程与主线程通信
Android系统中,线程使用的收件箱叫做消息队列(message queue)。使用消息队列的线程叫做消息循环(message loop)。消息循环会不断循环检查队列上是否有新消息。消息循环由一个线程和一个looper组成。Looper对象管理着线程的消息队列。
主线程也是一个消息循环,因此具有一个looper。主线程的所有工作都是由其looper完成的。looper不断从消息队列中抓取消息,然后完成消息指定的任务。
Message与 Message Handler
消息是Message类的一个实例,包含好几个实例变量。其中有三个须在实现时定义:
what 用户定义的int型消息代码,用来描述消息;
obj 随消息发送的用户指定对象;
target 处理消息的Handler
Message的目标是Handler的一个实例。Message在创建时会自动与一个Handler相关联。Message在准备处理状态下,Handler是负责让消息处理行为发送的对象。Handler不仅仅是Message的目标(target),也是创建和发布Message的接口。
Looper拥有Message对象收件箱,所以Message必须在Looper上发布或读取。基于Looper与Message的这种关系,为与Looper协同工作,Handler总是引用着它。
一个Handler仅与一个Looper相关联,一个Message也仅与一个目标Handler相关联。多个Handler可与一个Looper相关联,这意味着一个Handler的Message可能与另一个Handler的Message存放在同一个消息队列中。
创建并启动后台线程
ThumbnailDownloader.java
package com.example.photogallery;import java.io.IOException;
import java.security.PublicKey;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;import android.R.interpolator;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.GpsStatus.Listener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;/*** ThumbnailDownloader类需要使用某些对象来标识每一次下载。因此,在类创建对话框中,通过ThumbnailDownloader<Token>* 的命名, 为其提供一个Token泛型参数。*/
public class ThumbnailDownloader<Token> extends HandlerThread {private static final String TAG = "ThumbnailDownloader";private static final int MESSAGE_DOWNLOAD = 0;Handler mHandler;//requestMap是一个同步HashMap,使用Token作为key,可存储或与获取特定Token相关联的URL.Map<Token, String> requestMap = Collections.synchronizedMap(new HashMap<Token, String>());/*** HandlerThread能在主线程上完成任务的一种方式是,让主线程将其自身的Handler传递给HandlerThread。* 主线程是一个拥有Handler和Looper的消息循环。主线程创建的Handler会自动与它的Looper相关联。我们可以* 将主线程上创建的Handler传递给另一线程。传递出去的Handler与创建它的线程Looper始终保持着联系。因此,* 任何已传出Handler负责处理的消息都将在主线程的消息队列中处理。这看上去就像我们在使用HandlerThread的* Handler,实现在主线程上安排后台线程上的任务。*/Handler mResponseHandler;Listener<Token> mListener;public interface Listener<Token>{void onThumbnailDownloaded(Token token, Bitmap bitmap);}public void setListener(Listener<Token> listener){mListener = listener;}public ThumbnailDownloader(Handler responseHandler) {super(TAG);mResponseHandler = responseHandler;}/*** 添加@SuppressLint("HandlerLeak")注解的原因:* 这里,Android Lint将报出Handler类相关的警告信息。Looper控制着Handler的生死,* 因此如果Handler是匿名内部类,则隐式的对象引用很容易导致内存泄漏。不过,所有的对象都与HandlerThread* 绑定在一起,因此这里不用担心任何内存泄漏问题。* * * HandlerThread.onLooperPrepared()方法调用发生在Looper第一次检查消息队列之前,* 所以该方法成了我们创建Handler实现的好地方。*/@SuppressLint("HandlerLeak") @Overrideprotected void onLooperPrepared() {mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {if(msg.what == MESSAGE_DOWNLOAD){/*** 这里必须使用@SuppressWarnings("unchecked")注解,因为Token是泛型类参数,* 而msg.obj是一个Object。由于类型擦除(type erasure),这里的强制类型转换应该是不可以的。*/@SuppressWarnings("unchecked")Token token = (Token) msg.obj;handleRequest(token);}}};}public void queueThumbnail(Token token, String url) {requestMap.put(token, url);/*** Handler.obtainMessage(...)方法会从公共循环持里获取消息,因此比创建新实例更有效率。* Message.sendToTarget()方法会将message发生给它的Handler,紧接着Handler会将Message* 放置在Looper消息队列的末尾。*/Message message = mHandler.obtainMessage(MESSAGE_DOWNLOAD, token);message.sendToTarget();}private void handleRequest(final Token token){try {final String url = requestMap.get(token);if(url == null)return;byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);final Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);/*** Handler.post(Runnable)方法是一个张贴Message的便利方法*/mResponseHandler.post(new Runnable() {@Overridepublic void run() {/*** 因为GridView会循环使用它的视图,ThumbnailDownloader完成Bitmap下载后,* GridView可能已经循环使用了ImageView,并继续请求一个不同的URL。该检查可保证每个* Token都能获取到正确的图片,即使中间发生了其他请求也无妨。*/if(requestMap.get(token) != url)return;requestMap.remove(token);mListener.onThumbnailDownloaded(token, bitmap);}});} catch (IOException e) {e.printStackTrace();}}/*** 如果用户旋转屏幕,因ImageView视图的失效,ThumbnailDownloader则可能挂起。* 如果点击这些ImageView,就可能发生异常。*/public void clearQueue(){mHandler.removeMessages(MESSAGE_DOWNLOAD);requestMap.clear();}
}
启动与销毁后台线程ThumbnailDownloader
@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setRetainInstance(true);new FetchItemsTask().execute();/*** 创建并启动线程* getLooper()方法必在start()之后调用,这是一种保证线程就绪的处理方式。* * Handler默认与当前线程的Looper相关联。该Handler是在onCreate(...)方法中创建的,因此它将与主线程的Looper相关联。 */mThumbnailThread = new ThumbnailDownloader<ImageView>(new Handler());mThumbnailThread.setListener(new ThumbnailDownloader.Listener<ImageView>() {@Overridepublic void onThumbnailDownloaded(ImageView imageView, Bitmap bitmap) {//保证不会将图片设置到无效的ImageView视图上去if(isVisible()){imageView.setImageBitmap(bitmap);}}});mThumbnailThread.start();mThumbnailThread.getLooper();}
@Overridepublic void onDestroyView() {super.onDestroyView();mThumbnailThread.clearQueue();}@Overridepublic void onDestroy() {super.onDestroy();mThumbnailThread.quit();//结束线程。若不终止HandlerThread,它会一直运行下去}
代码地址
这篇关于Looper、Handler与HandlerThread的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!