深入理解HandlerThread
程序员文章站
2022-06-10 10:38:01
...
1.HandlerThread 是什么?
英文解释:
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.
HandlerThread就是一个包含looper的线程。我们可以使用这个looper创建handler。HandlerThread其本质还是一个普通的Thread,其内部创建了一个looper。
即:它就是一个帮我们创建 Looper 的线程,让我们可以直接在子线程中使用 Handler 来处理异步任务。
2.HandlerThread的起源
某些场景需要我们在子线程创建handler
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// 这里处理消息
}
};
Looper.loop();
}
此种写法对开发者要求高。HandlerThread集handler与Thread所长。简化此步骤。
3.HandlerThread的特点
- HandlerThread将loop转到子线程中处理,将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
-
开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理。
相比多次使用new Thread(){…}.start()这样的方式节省系统资源。
但是由于每一个任务都将以队列的方式逐个被执行到(会把所有消息放入messageQuene,随后才执行handleMessage),一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。 - HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
- 通过设置优先级就可以同步工作顺序的执行,而又不影响UI的初始化
- HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作,耗时不多而且也不会产生较大的阻塞。对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。
HandlerThread 所做的就是在新开的子线程中创建了 Looper,那它的使用场景就是 Thread + Looper 使用场景的结合,即:在子线程中执行耗时的、可能有多个任务的操作。比如说多个网络请求操作,或者多文件 I/O 等等。使用 HandlerThread 的典型例子就是
IntentService。
4.案例 模拟下载多个文件
DownLoadThread.class
public class DownLoadThread extends HandlerThread implements Handler.Callback {
private final String TAG = this.getClass().getSimpleName();
private final String KEY_URL = "url";
public static final int TYPE_START = 1;
public static final int TYPE_FINISHED = 2;
private Handler mWorkerHandler;
private Handler mUIHandler;
private List<String> mDownloadUrlList;
public DownLoadThread(String name) {
super(name);
}
public DownLoadThread(String name, int priority) {
super(name, priority);
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mWorkerHandler = new Handler(getLooper(), this);
if (mUIHandler == null) {
throw new IllegalArgumentException("Not set UIHandler!");
}
for (String url : mDownloadUrlList) {
Message message = mWorkerHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString(KEY_URL, url);
message.setData(bundle);
mWorkerHandler.sendMessage(message);
Log.i("onion", "sendMessage....");
}
}
public void setDownloadUrlList(String... urls) {
mDownloadUrlList = Arrays.asList(urls);
}
//注入主线程 Handler
public DownLoadThread setUIHandler(Handler handler) {
this.mUIHandler = handler;
return this;
}
public Handler getUIHandler() {
return mUIHandler;
}
@Override
public boolean quitSafely() {
Log.i("onion", "quitSafely....");
mUIHandler = null;
mWorkerHandler.removeCallbacksAndMessages(null);//添加后台终止下载,不添加继续运行
return super.quitSafely();
}
@Override
public boolean handleMessage(Message msg) {
//这个方法运行在子线程中
if (msg == null || msg.getData() == null) {
return false;
}
Log.i("onion", "mWorkerHandler begin handleMessage....");
String url = (String) msg.getData().getString(KEY_URL);
if (mUIHandler != null) {
//下载开始,通知主线程
Message startMsg = mUIHandler.obtainMessage(TYPE_START, "\n 开始下载 @" + System.currentTimeMillis() + "\n" + url);
mUIHandler.sendMessage(startMsg);
}
SystemClock.sleep(4000); //模拟下载
if (mUIHandler != null) {
//下载完成,通知主线程
Message finishMsg = mUIHandler.obtainMessage(TYPE_FINISHED, "\n 下载完成 @" + System.currentTimeMillis() + "\n" + url);
mUIHandler.sendMessage(finishMsg);
}
return true;
}
}
MainActivity.class
public class MainActivity extends RxAppCompatActivity implements View.OnClickListener, Handler.Callback {
TextView mTvStartMsg;
TextView mTvFinishMsg;
Button mBtnStartDownload;
private Handler mUIHandler;
private DownLoadThread mDownloadThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvStartMsg = (TextView) findViewById(R.id.tv_start_msg);
mTvFinishMsg = (TextView) findViewById(R.id.tv_finish_msg);
mBtnStartDownload = (Button) findViewById(R.id.btn_start_download);
mBtnStartDownload.setOnClickListener(this);
mUIHandler = new Handler(this);
mDownloadThread = new DownLoadThread("下载线程");
mDownloadThread.setUIHandler(mUIHandler);
mDownloadThread.setDownloadUrlList("http://pan.baidu.com/s/11111",
"http://bbs.005.tv/22222", "http://list.youku.com/show/33333", "http://list.youku.com/show/33333"
, "http://list.youku.com/show/33333"
, "http://list.youku.com/show/33333"
);
}
@Override
protected void onDestroy() {
mDownloadThread.quitSafely();
super.onDestroy();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_start_msg:
break;
case R.id.tv_finish_msg:
break;
case R.id.btn_start_download:
mDownloadThread.start();
mBtnStartDownload.setText("正在下载");
mBtnStartDownload.setEnabled(false);
break;
}
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case DownLoadThread.TYPE_FINISHED:
mTvFinishMsg.setText(mTvFinishMsg.getText().toString() + "\n " + msg.obj);
break;
case DownLoadThread.TYPE_START:
mTvStartMsg.setText(mTvStartMsg.getText().toString() + "\n " + msg.obj);
break;
}
return true;
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="lib.com.myapplication.MainActivity">
<TextView
android:id="@+id/tv_start_msg"
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="下载开始信息"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorAccent"/>
<TextView
android:id="@+id/tv_finish_msg"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="8dp"
android:text="下载完成信息"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorAccent"/>
<Button
android:id="@+id/btn_start_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="开始下载"/>
</LinearLayout>
其他常规用法:
mThread = new HandlerThread("handler_thread");
mThread.start();
mWorkHandler = new Handler(mThread.getLooper());
mUIHandler = new Handler();
源码:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//也可以指定线程的优先级,注意使用的是 android.os.Process 而不是 java.lang.Thread 的优先级!
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
// 子类需要重写的方法,在这里做一些执行前的初始化工作
protected void onLooperPrepared() {
}
//获取当前线程的 Looper
//如果线程不是正常运行的就返回 null
//如果线程启动后,Looper 还没创建,就 wait() 等待 创建 Looper 后 notify
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) { //循环等待
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
//调用 start() 后就会执行的 run()
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare(); //帮我们创建了 Looepr
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //Looper 已经创建,唤醒阻塞在获取 Looper 的线程
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop(); //开始循环
mTid = -1;
}
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;
}
}