Android HandlerThread

1.HandlerThread
官方对HandlerThread的介绍:
A Thread that has a looper. The Looper can then be used to create Handlers.
Note that just like a regular Thread, start() must still be called.
①HandlerThread本质上是一个线程类,它继承了Thread;
②HandlerThread有自己的内部Looper对象,可以进行looper循环;
③通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务。
④创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。
HandlerThread其实就是在一个子线程内部自己创建并管理了一个Looper。

2.HandlerThread使用步骤
①创建实例对象
HandlerThread handlerThread = new HandlerThread(“downloadImage”);
传入参数的作用主要是标记当前线程的名字,可以任意字符串。
②启动HandlerThread线程
//必须先开启线程
handlerThread.start();
创建完HandlerThread并启动了线程,接下来需要将一个耗时的异步任务投放到HandlerThread线程中去执行了。
③构建循环消息处理机制
//构建异步handler,即子线程Handler
Handler childHandler = new Handler( handlerThread.getLooper(), new ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) { //该handleMessage方法运行在子线程中,可以进行耗时处理
//……
return false;
}
});
构建了一个可以用于异步操作的handler,将HandlerThread的Looper对象作为参数传递给当前的handler,并自定义一个Callback实现handleMessage方法,这样当前的异步handler就拥有了HandlerThread的Looper对象,由于HandlerThread本身是异步线程,所以Looper是在异步线程中创建的,属于异步线程的Looper,从而handler就是在异步线程中实例化的,因此handlerMessage方法就可以异步处理耗时任务了。

3.使用举例
public class HandlerThreadActivity extends Activity {
private String url[]={
“https://img-blog.csdn.net/20160903083245762”,
“https://img-blog.csdn.net/20160903083252184”};
private ImageView imageView;
//主线程handler,用于子线程发送消息后在主线程中更新界面
private Handler mUIHandler =new Handler(){
@Override
public void handleMessage(Message msg){
LogUtils.e(“次数:”+msg.what);
ImageModel model = (ImageModel) msg.obj;
imageView.setImageBitmap( model.bitmap);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
//创建异步HandlerThread
HandlerThread handlerThread = new HandlerThread(“downloadImage”);
handlerThread.start(); //必须先开启线程
Handler childHandler = new Handler( handlerThread.getLooper(),new ChildCallback()); //子线程Handler
//主线程中使用子线程的handler发送消息,然后在子线程handler的handlerMessage中处理
for(int i=0;i<url.length;i++){
//每隔1秒去更新图片
childHandler.sendEmptyMessageDelay ed( i,1000*i);
}
}
//该callback运行于子线程
class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在子线程中进行网络请求
Bitmap bitmap = downloadUrlBitmap( url[msg.what]);
ImageModel imageModel=new ImageModel();
imageModel.bitmap=bitmap;
imageModel.url=url[msg.what];
Message msg1 = new Message();
msg1.what = msg.what;
msg1.obj =imageModel;
//通知主线程去更新UI
mUIHandler.sendMessage(msg1);
return false;
}
}
private Bitmap downloadUrlBitmap(String urlString) {
HttpURLConnection urlConnection = null;
BufferedInputStream in = null;
Bitmap bitmap=null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream( urlConnection.getInputStream(), 8 * 1024);
bitmap=BitmapFactory.decodeStream( in);
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (in != null) {
in.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
return bitmap;
}
}
创建了两个Handler,一个是用于更新UI线程的mUIHandler(主线程handler),一个是用于异步下载图片的childHandler(子线程handler)。childHandler会每隔1秒钟通过sendEmptyMessageDelayed方法通知ChildCallback的回调函数handleMessage方法去下载图片,并告诉mUIHandler去更新UI界面。

以上便是HandlerThread常规使用,实际上在android比较典型的应用是IntentService。

4.HandlerThread源码解析
HandlerThread的源码只有一百多行,那就一步一步来分析吧。
先来看看构造函数:
public class HandlerThread extends Thread {
int mPriority;//线程优先级
int mTid = -1;
Looper mLooper;//当前线程持有的Looper对象
private Handler mHandler; //子线程handler

public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_D EFAULT;
}
public HandlerThread(String name, int priority){
super(name);
mPriority = priority;
}
HandlerThread继承自Thread,构造函数的传递参数有两个,一个是name指的是线程的名称,一个是priority指的是线程优先级,根据需要调用即可。其中成员变量mLooper就是HandlerThread自己持有的Looper对象。

protected void onLooperPrepared() {
}
onLooperPrepared()方法是一个空实现,必要时可以重写,但是注意重写时机是在Looper循环启动前。

再看看run方法:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();//创建了一个Looper对象并存进sThreadLocal中
synchronized (this) {
mLooper = Looper.myLooper();//从sThreadLocal中取出Looper对象赋值给HandlerThread的成员变量mLooper
notifyAll(); //唤醒等待线程
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在创建HandlerThread对象后必须调用start()方法才能进行其他操作,而调用start()方法相当于启动了线程,也就是run方法将会被调用,从run源码中可以看出其执行了Looper.prepare()代码,进入到Looper类看看该方法:
Looper.java:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed){
if(sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}
sThreadLocal.set(new Looper(quitAllowed));
}
prepare()方法创建了一个Looper对象并存进sThreadLocal中,并且每个线程只能创建一个Looper对象,否则会抛异常。

创建Looper对象后接着将执行代码:
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //唤醒等待线程
}
同步代码块中首先执行了Looper.myLooper(),还是去Looper类看看这个方法:
Looper.class:
public static Looper myLooper() {
return sThreadLocal.get();
}
Looper.myLooper()方法从sThreadLocal中取出了刚刚创建的Looper对象,并将它赋值给HandlerThread的内部变量mLooper,然后通过notifyAll()方法唤醒等待线程,最后执行Looper.loop();代码,开启looper循环语句。

这里为什么要唤醒等待线程呢?来看看HandlerThread的getLooper方法:
public Looper getLooper() {
if (!isAlive()) { //先判断当前线程是否启动了
return null;
}
boolean was interrupted = false;
// 当前线程已经启动了,但是mLooper还没创建好,就等待
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();//等待唤醒
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
}
if(wasInterrupted) {
Thread.currentThread().interrupt();
}
return mLooper;
}
在通过getLooper()方法获取HandlerThread的mLooper对象时,会先判断当前线程是否启动了。如果线程已经启动,将会进入同步语句并判断mLooper是否为null,为null则代表Looper对象还没被创建,此时当前调用线程进入等待阶段,直到mLooper对象被创建并通过notifyAll()方法唤醒等待线程,最后才返回mLooper对象。

之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的(HandlerThread里),而调用getLooper()方法则是在其他线程进行的(比如在主线程中创建Handler时调用getLooper),这样就无法保障在调用getLooper()方法时Looper对象已经被创建。

到这里也就明白了在获取mLooper对象时会存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,HandlerThread内部则通过等待唤醒机制解决了同步问题。

HandlerThread中还有一个方法:
public Handler getThreadHandler() {
if(mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
返回了HandlerThread子线程的Handler对象。

最后看一下HandlerThread的退出:
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
当调用quit方法时,其内部实际上是调用Looper的quit方法,而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息还是非延迟消息。
当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法,最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。

5.完整源码
最后贴一下HandlerThread的完整源码:
/**A Thread that has a Looper. The Looper can then be used to create Handlers.
Note that just like with a regular Thread, start() must still be called.*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;

public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DE FAULT;
}

/**Constructs a HandlerThread.
param: name -
priority: The priority to run the thread at. The value supplied must be from Process and not from java.lang.Thread. */
public HandlerThread(String name,int priority){
super(name);
mPriority = priority;
}

/**Call back method that can be explicitly overridden if needed to execute some setup before Looper loops.*/
protected void onLooperPrepared() {
}

@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**This method returns the Looper associated with this thread. If this thread not been started or for any reason isAlive() returns false, this method will return null. If this thread has been started, this method will block until the looper has been initialized.
return: The looper.*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
boolean wasInterrupted = false;

// If the thread has been started, wait until the looper has been created.
synchronized (this)
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
}
if(wasInterrupted) {
Thread.currenThread().interrupt();
}
return mLooper;
}

/*returns: a shared Handler associated with this thread/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}

/**Quits the handler thread’s looper.
Causes the handler thread’s looper to terminate without processing any more messages in the message queue.
Any attempt to post messages to the queue after the looper is asked to quit will fail.For example, the Handler.sendMessage(Message) method will return false.
Using this method may be unsafe because some messages may not be delivered before the looper terminates. Consider using quitSafely instead to ensure that all pending work is completed in an orderly manner.
returns: True if the looper looper has been asked to quit or false if the thread had not yet started running.*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}

/**Quits the handler thread’s looper safely.
Causes the handler thread’s looper to terminate as soon as all remaining messages in the message queue that are already due to be delivered have been handled. Pending delayed messages with due times in the future will not be delivered.
Any attempt to post messages to the queue after the looper is asked to quit will fail. For example, the Handler.sendMessage(Message)} method will return false.
If the thread has not been started or has finished (that is if getLooper returns null), then false is returned. Otherwise the looper is asked to quit and true is returned.
returns: True if the looper looper has been asked to quit or false if the thread had not yet started running.*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}

/** Returns the identifier of this thread. See Process.myTid(). */
public int getThreadId() {
return mTid;
}
}

我的理解:其实HandlerThread就是为了让我们在构造子线程的Handler对象时,可以不用显示的去调用Looper.prepare和Looper.loop(因为这两个方法已经在HandlerThread内部调用过了)。HandlerThread本身是一个Thread,通过在这个Thread内部添加一个Looper,然后用HandlerThread.getLooper()方法去构造一个Handler对象,这样这个Handler对象就属于子线程的,它的handleMessage()方法就可以在子线程中执行。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值