HandlerThread特点
- HandlerThread本身是一个线程类,它继承了Thread
- HandlerThread有自己内部Looper对象,可以进行Looper循环
- 通过获取HandlerThread的Looper对象并传递给Handler对象,可以在handleMessage方法中执行异步任务
- 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象
使用步骤
1.创建实例对象
HandlerThread handlerThread = new HandlerThread("downloadImage");
传入参数的作用主要是标记当前线程的名字,可以任意字符串。
2.启动HandlerThread线程
1. //必须先开启线程
2. handlerThread.start();
此时,ThreadHandler已经创建完并启动了线程。如何将耗时的异步任务放到HandlerThread线程中?
3.构建循环消息处理机制
/**
* 该callback运行于子线程
*/
class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在子线程中进行相应的网络请求
//通知主线程去更新UI
mUIHandler.sendMessage(msg1);
return false;
}
}
4.构建异步handler
//子线程Handler
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
第3和第4步是构建一个可以用于异步操作的handler,并将前面创建的HandlerThread的Looper对象以及Callback接口类,作为参数传递给当前的handler,这样当前的异步handler就拥有了HandlerThread的Looper对象,由于HandlerThread本身是异步线程,因此Looper也与异步线程绑定,从而handlerMessage方法也就可以异步处理耗时任务了。Looper+Handler+MessageQueue+Thread异步循环机制构建完成。
示例代码
创建了两个handler,一个用于更新UI线程的mUIHandler,一个用于异步下载图片的childHandler。
public class HandlerThreadActivity extends AppCompatActivity {
/**
* 图片地址集合
*/
private String url[] = {
"https://img-blog.csdn.net/20160903083245762",
"https://img-blog.csdn.net/20160903083252184",
"https://img-blog.csdn.net/20160903083257871",
"https://img-blog.csdn.net/20160903083257871",
"https://img-blog.csdn.net/20160903083311972",
"https://img-blog.csdn.net/20160903083319668",
"https://img-blog.csdn.net/20160903083326871"
};
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
imageView = (ImageView) findViewById(R.id.image);
//创建异步HandlerThread
HandlerThread handlerThread = new HandlerThread("downloadImage");
//必须先开启线程
handlerThread.start();
//子线程Handler 用于异步下载图片
Handler childHandler = new Handler(handlerThread.getLooper(), new ChildCallback());
for (int i = 0; i < 7; i++) {
//每一秒去更新一个图片
childHandler.sendEmptyMessageDelayed(i, 1000 * i);
}
}
/**
* 该callback运行于子线程
*/
class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message message) {
//在子线程中进行网络请求
Bitmap bitmap = downloadUrlBitmap(url[message.what]);
ImageModel imageModel = new ImageModel();
imageModel.url = url[message.what];
imageModel.bitmap = bitmap;
Message msg = new Message();
msg.what = message.what;
msg.obj = imageModel;
//通知主线程去更新UI
mUIHandler.sendMessage(msg);
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 (IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bitmap;
}
/**
* 用于更新UI
*/
private Handler mUIHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
ImageModel model = (ImageModel) msg.obj;
imageView.setImageBitmap(model.bitmap);
}
};
class ImageModel {
String url;
Bitmap bitmap;
ImageModel() {
}
}
}
源码分析
/**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
int mPriority;//线程优先级
int mTid = -1;
Looper mLooper;//当前线程持有的Looper对象
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.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() {
}
可以看出HandlerThread继承Thread,构造函数有两个,name指的是线程的名称,priority指的是线程优先级。成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,必要时可以重写,注意重写时机是在Looper循环启动前。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
创建HandlerThread对象后,必须要调用start()方法才能开启线程,即run()方法会被调用。可以看出,run()方法执行了Looper.prepare()代码,这时,Looper对象被创建,当Looper对象被创建后绑定在当前线程(也就是异步线程),这样才可以把Looper对象赋值给Handler对象,进而确保Handler对象中的hanleMessage方法是异步线程执行的。
接着执行代码:
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
这里在Looper对象创建后将其赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程,最后执行Looper.loop();代码,开启looper循环。这里为什么要唤醒等待线程呢?
/**
* 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;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
在外部通过getLooper方法获取looper对象时会先判断当前线程是否启动了,如果线程已经启动,将会进入同步语句并判断Looper对象是否为null,为null则代表Looper对象还没有被复制,也就是还没被创建,此时当前调用线程进入等待阶段,直到Looper对象被创建并通过notifyAll()方法唤醒等待线程,最后才返回Looper对象。之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,这样我们就无法保障我们在调用getLooper方法时Looper已经被创建。
所以在获取mLooper对象时会存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,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方法,该方法主要把MessageQueue消息池中所有的消息全部清空,无论是延迟消息还是非延迟消息。
当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法,而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的费延迟消息派发出去让handler去处理完成后,才停止Looper循环。
quitSafely相比quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。Looper的quit方法基于API1,quitSafely方法基于API18。
转自:https://blog.csdn.net/javazejian/article/details/52426353