1.HandlerThread是什么?
产生的背景:开启子线程进行耗时操作,多次创建和销毁子线程是很耗费资源的,但是木有关系,谷歌考虑了这点为我们专门开发出了HandlerThread机制,那么它有什么特点呢?请看下面。
本质:Handler + Thread + Looper,是一个Thread内部有Looper。当你被面试官问道HandlerThread是什么,有何特点,那么你应该这么回答:
a.HandlerThread本质上是一个线程类,它继承了Thread。
b.HandlerThread有自己内部的Looper对象,可以进行Looper循环。
c.通过获取HandlerThread的Looper对象传递给Handler对象,可以在handlerMessage方法中执行异步任务。
d.优点是不会有堵塞,减少对性能的消耗,缺点是不能进行多任务的处理,需要等待进行处理,处理效率较低。
e.与线程池注重并发不同,HandlerThread是一个串行队列,HandlerThread背后只有一个线程。
2.HandlerThread的使用
其实HandlerThread的使用就是为了主线程通知子线程,再由子线程通知主线程,这里面建立一套完整的通信体系,并不像传统的开启子线程,使用HandlerThread的好处就是主线程可以发送信息告诉子线程要干什么,而且可以一直发送信息,无论在主线程的何处,都可以发送合适的信息告诉子线程,它要干什么,子线程做完了信息中交代的事情,然后再通知主线程更新等操作。说白了HandlerThread提供了主线程向子线程的通信。
public class MainActivity extends AppCompatActivity {
private ImageView imageView;
private HandlerThread handlerThread;
/**
* 图片地址数组
*/
private String url[]={
"图片地址1","图片地址2","图片地址3","图片地址4","图片地址5"
};
private Handler uiHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//加载图片
imageView.setImageBitmap((Bitmap) msg.obj);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.iv1);
handlerThread = new HandlerThread("downloadImage");
//必须开启HandlerThread线程
handlerThread.start();
//获取子Handler
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
for(int i=0;i<url.length;i++){
childHandler.sendEmptyMessageDelayed(i,1000*i);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
handlerThread.quit();//注意释放资源
}
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;
}
class ChildCallback implements Handler.Callback{
@Override
public boolean handleMessage(Message message) {
//在子线程中进行网络请求
Bitmap bitmap=downloadUrlBitmap(url[message.what]);
Message message1 = new Message();
message1.obj =bitmap;
//通知主线程去更新UI
uiHandler.sendMessage(message1);
return false;
}
}
}
在上面代码中,创建了两个Handler,一个用于更新UI线程的uiHandler,一个是用于异步下载图片的childHandler。最终的结果是childHandler会每个隔1秒钟通过sendEmptyMessageDelayed方法去通知ChildCallback的回调函数handleMessage方法去下载图片并告诉mUIHandler去更新UI界面,以上便是HandlerThread常规使用。
3.HandlerTread机制原理
下面我们通过源码来分析一下HandlerThread:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //唤醒其他线程的等待锁,这边是为了唤醒getLooper()方法中的等待锁
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 线程开始,该方法会一直等待Looper对象创建完成才会执行,
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
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;
}
public int getThreadId() {
return mTid;
}
}
首先我们看一下它的run()方法,可以发现该方法中调用Looper.myLooper()创建了一个Looper对象mLooper,并把该对象放到线程变量sThreadLocal中,然后通过调用Looper.loop()开启消息循环,Looper.loop()方法会不断循环从MessageQueue中取出消息处理消息,没有消息是则会阻塞。getLooper()方法是获取线程中的Looper对象,可以用来初始化Handler对象。
quit()和quitSafely()的区别在于,quit()会清空所有消息(无论延时或非延时),quitSafely()只清空延时消息,无论是调用了quit()方法还是quitSafely()方法,Looper就不再接收新的消息。即在调用了Looper的quit()或quitSafely()方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,线程也就结束了。