HandlerThread的妙用
很多博客讲了HandlerThread的用法,但是没有具体的应用场景,可能也是了解并未真正使用!
1. HandlerThread是什么?
handlerThread是内部有一个looper轮询器的Thread线程。
HandlerThread产生的背景
google为了避免以下几种情况,给android开发者提供了这样一个异步框架
- 在需要做子线程耗时操作时,我们有可能会new Thread来进行
- 频繁多次创建子线程,对性能是有影响的
- 可以自己在子线程中使用looper轮询,避免多次创建线程
- HandlerThread的特点
- HandlerThread是一个线程类,它继承了Thread
- 它的内部有自己的Looper对象,可以进行loop轮询
- 使用HandlerThread的looper创建的Handler在handleMessage中可以进行耗时操作,因为它是执行在子线程的
- 它的优点是不会阻塞主线程,缺点是不能同时执行多任务,需要有序执行,执行效率低。
2. HandlerThread如何使用
注意,指定优先级时,是指定进程的优先级,不是线程的优先级,可选值为:
public static final int THREAD_PRIORITY_DEFAULT = 0;
public static final int THREAD_PRIORITY_LOWEST = 19;
public static final int THREAD_PRIORITY_BACKGROUND = 10;
public static final int THREAD_PRIORITY_FOREGROUND = -2;
public static final int THREAD_PRIORITY_DISPLAY = -4;
public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;
public static final int THREAD_PRIORITY_AUDIO = -16;
- 创建HandlerThread对象,指定名字和进程优先级
- 复写onLoopPrepared,进行准备工作
- 调用start开启HandlerThread线程轮询,执行任务
- 根据应用场景在HandlerThread的handleMessage中调用UI线程的Handler,更新UI。
下面我用一个简单的例子演示该调用方式。
创建HandlerThread的子类,复写构造方法,指定优先级和线程名;复写onLoopPrepared进行初始化的相关操作。创建一个sendMessage(Message msg)方法,可供外部调用,添加新的任务执行。
/**
* 用来下载文件的一个HandlerThread
*
* Created by rytong on 2018/2/3.
*/
public class DownloadHanderThread extends HandlerThread implements Handler.Callback{
private Handler downLoadHandler;
private ArrayList<String> mDownLoadUrlList;
private Handler UIHandler;
public DownloadHanderThread(String name) {
super("download Thread!");
}
public void setUIHandler(Handler UIHandler) {
this.UIHandler = UIHandler;
}
public void setmDownLoadUrlList(ArrayList<String> mDownLoadUrlList) {
this.mDownLoadUrlList = mDownLoadUrlList;
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
downLoadHandler = new Handler(getLooper(),this);
if (UIHandler == null){
throw new IllegalArgumentException("UIHandler不能为空!!");
}
//将初始的下载队列,进行排队
for (int i=0;i<mDownLoadUrlList.size();i++){
String url = mDownLoadUrlList.get(i);
Message message = Message.obtain();
message.obj = url;
downLoadHandler.sendMessage(message);
}
}
/**
* 执行新消息
*/
public void sendMessage(Message msg){
if (downLoadHandler == null){
throw new IllegalArgumentException("downLoadHandler不能为空!");
}
downLoadHandler.sendMessage(msg);
}
@Override
public boolean handleMessage(Message msg) {
// 处理工作线程的异步任务(非UI线程)
String url = (String) msg.obj;
if (!TextUtils.isEmpty(url)){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//给UI线程传递消息
Message message = Message.obtain();
message.obj = url;
UIHandler.sendMessage(message);
}
return true;
}
}
在MainActivity中执行HandlerThread,并可以通过按钮事件添加新的任务到Thread中,排队执行
public class MainActivity extends AppCompatActivity implements Handler.Callback, View.OnClickListener {
private Button bt;
private TextView tv;
private DownloadHanderThread downloadHanderThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt = (Button) findViewById(R.id.bt);
tv = (TextView) findViewById(R.id.tv);
// 创建HandlerThread
downloadHanderThread = new DownloadHanderThread("");
bt.setOnClickListener(this);
ArrayList<String> urlList = new ArrayList<>();
urlList.add("www.baidu.com/meinv.png");
urlList.add("www.baidu.com/youxi.png");
urlList.add("www.baidu.com/phone.png");
Handler uiHandler = new Handler(Looper.getMainLooper(),this);
// 初始化数据
downloadHanderThread.setmDownLoadUrlList(urlList);
downloadHanderThread.setUIHandler(uiHandler);
downloadHanderThread.start();
}
@Override
public boolean handleMessage(Message msg) {
tv.append("\n"+(String)msg.obj+":下载完成");
return true;
}
@Override
public void onClick(View v) {
String url = "老板又让我去下载美女图片,地址:www.baidu.com/meinv.png";
Message message = Message.obtain();
message.obj = url;
downloadHanderThread.sendMessage(message);
}
}
动态图演示:
3. HandlerThread的使用场景
- 可以使用在需要频繁调用子线程执行耗时任务的地方(比如,文件的下载等)
- 为了保证不让多线程影响业务逻辑,需要同步执行任务的场景,同时还不能阻塞UI线程(推送消息接收处理)
4. HandlerThread源码剖析
HandlerThread是一个线程,重要逻辑在run方法中,Looper的获取加了同步逻辑。其他代码跟我们在子线程创建Looper一样。
public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
创建Looper时需要阻塞线程,只有等looper获取到之后才能继续执行。这是因为,我们调研getLooper是在run方法执行前,所以,需要保证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; }
退出
有quit() 和quitSafely()两个方法,区别在于,直接退出还是执行完非延迟任务再退出。