HandlerThread,顾名思义,Handler+Thread,就是让子线程也能有一套和MainThread一样的Handler消息机制。
- 用户启动一个应用,系统内部建立一个进程。
- 进程启动主线程Main Thread。
- Main Thread通过Looper建立一个消息队列Message Queue。
- 消息队列是存在于主线程中的,在主线程中开始无限循环。
- 每当有新的Message进来,消息队列就开始处理,如果没有,就执行return继续等待。
- 创建实例对象HandlerThread
- 启动HandlerThread的主线程Thread()
- 创建一个Handler,该Handler使用的是HandlerThread的looper,并传入一个自定义的的消息处理机制来处理
- 处理完毕调用MainThread创建的另外一个Handler去处理结果
区别:
平时使用Handler的时候系统内部已经帮我们创建好主线程和启动主线程了,而使用HandlerThread需要我们手动去创建一个属于HandlerThread的主线程并且去启动它
使用Handler的时候使用的是HandlerThread的looper而不是MainThread的looper
不使用handleMessage来处理,而是使用一个实现了Handler.Callback接口的自定义的处理方法
HandlerThread不能操作UI,所以处理完毕后还是需要使用主线程的Handler去处理UI
这个和直接在MainThread中new Thread的作用是一样的,只不过是HandlerThread比Thread多了一套和MainThread一样的消息处理机制,有利于串行地处理消息,不会造成并发。
如图所示:
package com.bourne.android_common.ServiceDemo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import com.bourne.android_common.R;
import com.bourne.common_library.utils.Logout;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
public class HandlerThreadActivity extends AppCompatActivity {
class ImageBean {
private String url;
private Bitmap bitmap;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
}
/**
* 图片地址集合
*/
private String url[] = {
"http://img0.imgtn.bdimg.com/it/u=1597254274,1405139366&fm=23&gp=0.jpg",
"http://img0.imgtn.bdimg.com/it/u=3901634069,2243065451&fm=23&gp=0.jpg",
"http://img4.imgtn.bdimg.com/it/u=1800624712,2677106110&fm=23&gp=0.jpg",
"http://img0.imgtn.bdimg.com/it/u=2456066925,446683653&fm=23&gp=0.jpg",
"http://img0.imgtn.bdimg.com/it/u=565155430,1247415230&fm=23&gp=0.jpg",
"http://img4.imgtn.bdimg.com/it/u=2845715753,1348257911&fm=23&gp=0.jpg",
"http://img3.imgtn.bdimg.com/it/u=3634032659,2514353810&fm=23&gp=0.jpg"
};
private ImageView imageView;
private HandlerThread handlerThread;
private Thread loadImageThread;
private int count = 0;
/**
* 处理UI
*/
Handler mainThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Logout.e("次数:" + msg.what);
ImageBean imageBean = (ImageBean) msg.obj;
imageView.setImageBitmap(imageBean.getBitmap());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
imageView = (ImageView) findViewById(R.id.imageView);
createHandlerThread();
}
/**
* 通过HandlerThread的方式加载
* @param view
*/
public void loadByHandlerThread(View view) {
Handler.Callback callBack = new loadImageCallBack();
Handler handlerThreadHandler = new Handler(handlerThread.getLooper(), callBack);
for (int i = 0; i < url.length; i++) {
handlerThreadHandler.sendEmptyMessageDelayed(i, 1000 * i);
}
}
/**
* 通过Thread的方式加载
* @param view
*/
public void loadByThread(View view) {
if (loadImageThread != null && !loadImageThread.isInterrupted()) {
loadImageThread.interrupt();
}
count = 0;
loadImageThread = new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
Thread.sleep(1000);
//在子线程中进行网络请求
Bitmap bitmap = downloadUrlBitmap(url[count]);
ImageBean imageBean = new ImageBean();
imageBean.setBitmap(bitmap);
imageBean.setUrl(url[count]);
Message message = new Message();
message.what = count;
message.obj = imageBean;
count++;
mainThreadHandler.sendMessage(message);
//最后一张时停止加载
if (count >= url.length) {
loadImageThread.interrupt();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
Logout.e("加载完毕,停止线程");
}
}
});
loadImageThread.start();
}
/**
* 处理下载图片
*/
class loadImageCallBack implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在子线程中进行网络请求
Bitmap bitmap = downloadUrlBitmap(url[msg.what]);
ImageBean imageBean = new ImageBean();
imageBean.setBitmap(bitmap);
imageBean.setUrl(url[msg.what]);
Message message = new Message();
message.what = msg.what;
message.obj = imageBean;
mainThreadHandler.sendMessage(message);
return false;
}
}
/**
* 创建一个HandlerThread
*/
private void createHandlerThread() {
//创建实例对象
handlerThread = new HandlerThread("downloadImage");
handlerThread.start();
}
/**
* 下载图片的网络请求
*
* @param urlString
* @return
*/
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;
}
@Override
protected void onDestroy() {
super.onDestroy();
//释放资源
handlerThread.quit();
}
}
可以看到我们在onCreate的时候创建了一个HandlerThread,并开启它的线程。点击按钮的时候创建一个Handler,取HandlerThread的Looper,并完成线程的操作。操作完成之后,发消息给UI线程并改变UI。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context="com.bourne.android_common.ServiceDemo.HandlerThreadActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="loadByHandlerThread"
android:text="使用HandlerThread下载图片"
android:textAllCaps="false"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="loadByThread"
android:text="使用Thread下载图片"
android:textAllCaps="false"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
LinearLayout>
完整代码:
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
/**
*
*/
public class HandlerThread extends Thread {
/**
* 线程优先级
*/
int mPriority;
int mTid = -1;
/**
* 当前线程持有的Looper对象
*/
Looper mLooper;
public MyHandlerThread(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 MyHandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
*必要时可以自己去重写
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
//Looper对象将被创建
mLooper = Looper.myLooper();
//唤醒等待线程
notifyAll();
}
//设置进程优先级
Process.setThreadPriority(mPriority);
onLooperPrepared();
//开启looper循环语句
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is 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;
}
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
//等待唤醒
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* 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 {@link 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 {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
*
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
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 {@link Handler#sendMessage(Message)} method will return false.
*
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
*
*
* @return 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;
}
}
/**
* 线程优先级
*/
int mPriority;
int mTid = -1;
/**
* 当前线程持有的Looper对象
*/
Looper mLooper;
public MyHandlerThread(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 MyHandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
*必要时可以自己去重写
*/
protected void onLooperPrepared() {
}
这里初始化了Handler的对象,设置了优先级,mLooper是持有的Looper对象,onLooperPrepared是一个空实现,必要是可以自己重写。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
//Looper对象将被创建
mLooper = Looper.myLooper();
//唤醒等待线程
notifyAll();
}
//设置进程优先级
Process.setThreadPriority(mPriority);
onLooperPrepared();
//开启looper循环语句
Looper.loop();
mTid = -1;
}
run()方法中调用了Looper.prepare(),Loop.loop(),prepare()负责创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量。
loop()就开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is 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;
}
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
//等待唤醒
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
Looper的创建是在子线程中执行的,而Handler通过getLooper去获取mLooper的时候又是在主线程,怎么保证它们可以同步呢,原因是通过唤醒机制:
getLooper方法获取looper对象时会先先判断当前线程是否启动了,如果启动了会判断Looper对象有没有被创建,如果都都没有满足则会继续等待,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,返回mLooper对象给Handler使用。
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消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。
Android 多线程之HandlerThread 完全详解
Android HandlerThread 完全解析