首先,我们需要创建一个名为DownloadThread 的类,该类继承自 HandlerThread。这个类的主要目的是用于下载功能。
```java
public class DownloadThread extends HandlerThread {
private static final String TAG = "DownloadThread";
public static final int TYPE_START = 2; // 通知主线程任务开始
public static final int TYPE_FINISHED = 3; // 通知主线程任务结束
private Handler mUIHandler; // 主线程的Handler
public DownloadThread(String name) {
super(name);
}
@Override
protected void onLooperPrepared() {
Log.e(TAG, "onLooperPrepared: 1.Download线程开始准备");
super.onLooperPrepared();
}
//注入主线程Handler
public void setUIHandler(Handler UIhandler) {
mUIHandler = UIhandler;
Log.e(TAG, "setUIHandler: 2.主线程的handler传入到Download线程");
}
//Download线程开始下载
public void startDownload() {
Log.e(TAG, "startDownload: 3.通知主线程,此时Download线程开始下载");
mUIHandler.sendEmptyMessage(TYPE_START);
//模拟下载
Log.e(TAG, "startDownload: 5.Download线程下载中...");
SystemClock.sleep(2000);
Log.e(TAG, "startDownload: 6.通知主线程,此时Download线程下载完成");
mUIHandler.sendEmptyMessage(TYPE_FINISHED);
}
}
```
```
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
```
```java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private DownloadThread mHandlerThread; //子线程
private Handler mUIhandler; //主线程的Handler
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化,参数为线程的名字
mHandlerThread = new DownloadThread("mHandlerThread");
//调用start方法启动线程
mHandlerThread.start();
//初始化Handler,传递 mHandlerThread 内部的一个 looper
mUIhandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
//判断mHandlerThread里传来的msg,根据msg进行主页面的UI更改
switch (msg.what) {
case DownloadThread.TYPE_START:
//不是在这里更改UI哦,只是说在这个时间,你可以去做更改UI这件事情,改UI还是得在主线程。
Log.e(TAG, "4.主线程知道Download线程开始下载了...这时候可以更改主界面UI");
break;
case DownloadThread.TYPE_FINISHED:
Log.e(TAG, "7.主线程知道Download线程下载完成了...这时候可以更改主界面UI,收工");
break;
default:
break;
}
super.handleMessage(msg);
}
};
//子线程注入主线程的mUIhandler,可以在子线程执行任务的时候,随时发送消息回来主线程
mHandlerThread.setUIHandler(mUIhandler);
//子线程开始下载
mHandlerThread.startDownload();
}
@Override
protected void onDestroy() {
//有2种退出方式
//mHandlerThread.quit();
//mHandlerThread.quitSafely(); 需要API >=18
super.onDestroy();
}
}
```
以下是重构后的代码,并保持了原有的段落结构:
```java
public class HandlerThread extends Thread {
int mPriority; // 优先级
int mTid = -1; // 线程ID
Looper mLooper; // 自带的Looper
private @Nullable Handler mHandler; // Handler对象,可以为null
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* 构造一个HandlerThread。
* @param name 线程名
* @param priority 要运行线程的优先级。提供的值必须来自{@link android.os.Process},而不是来自java.lang.Thread。
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
}
```
源码解析:
- `HandlerThread`类继承自`Thread`类,表示一个自定义的线程类。
- `mPriority`成员变量用于存储线程的优先级,取值范围从`android.os.Process.THREAD_PRIORITY_DEFAULT`到`android.os.Process.THREAD_PRIORITY_BACKGROUND`,默认为`THREAD_PRIORITY_DEFAULT`。
- `mTid`成员变量用于存储线程的ID,初始化为-1。
- `mLooper`成员变量用于存储线程的Looper对象,它是Android系统中的一个基本组件,用于处理消息队列。
- `mHandler`成员变量用于存储Handler对象,它负责在线程中处理消息和执行任务。这个成员变量可以为null。
构造函数方面,有两个重载版本:
- 一个不带参数的构造方法,接受一个字符串作为线程名字,并将线程的优先级设置为`THREAD_PRIORITY_DEFAULT`。
- 一个带有两个参数的构造方法,分别接受一个字符串和一个整数作为线程名字和优先级。这个构造方法将线程的优先级设置为传入的整数值。
```
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
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;
}
```
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
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()` 方法会立即停止消息循环,而 `quitSafely()` 方法则会在停止消息循环后等待一段时间,以确保所有正在处理的消息都已经被处理完毕。这意味着在调用 `quitSafely()` 方法时,可能会有部分消息未能被处理。如果不关心这些未处理的消息,可以选择使用 `quit()` 方法。
这段代码是一个名为`quit`的方法,它接受一个布尔类型参数`safe`。该方法的作用是退出应用程序或者停止某个操作。如果`safe`为`true`,则在清空消息之前会派发所有的非延迟消息;如果`safe`为`false`,则直接清空所有的消息,包括延迟消息和非延迟消息。
具体实现过程如下:
1. 首先检查是否允许退出,如果不允许,则抛出异常。
2. 使用`synchronized`关键字对当前对象进行同步,确保在同一时刻只有一个线程可以执行该方法。
3. 判断是否正在退出,如果是,则直接返回。
4. 将`mQuitting`设置为`true`,表示正在退出。
5. 根据`safe`参数的值,调用不同的方法来清空消息池中的不同类型的消息。
6. 调用`nativeWake(mPtr)`方法唤醒主线程。
HandlerThread 和 Looper 是 Android 开发中用于实现消息循环的两个重要组件。它们之间的主要区别在于如何终止消息循环以及处理退出时的情况。
## quit 方法
`quit()` 方法是 HandlerThread 类的一个成员方法,用于安全地终止消息循环。这个方法从 API 1 就开始存在了,比较早。当调用 `quit()` 方法后,Looper 将立即停止接收新的消息,并且不再将消息放入消息队列中。这意味着在调用 `quit()` 方法之后,通过 Handler 发送的消息不会被处理。以下是一个简单的示例:
```java
public class MyHandlerThread extends HandlerThread {
public MyHandlerThread(String name) {
super(name);
}
@Override
public void run() {
Looper.prepare();
Handler handler = new MyHandler();
handler.sendEmptyMessage(0);
Looper.loop();
}
}
```
```java
public class MyActivity extends AppCompatActivity {
private MyHandlerThread mHandlerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandlerThread = new MyHandlerThread("MyHandlerThread");
mHandlerThread.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quitSafely(); // 注意这里使用了 quitSafely 而不是 quit
}
}
```
在上面的代码中,我们在 `MyActivity` 的 `onDestroy()` 方法中调用了 `mHandlerThread.quitSafely()`,以确保在活动销毁时能够安全地结束消息循环。如果不使用 `quitSafely`,则可能会导致内存泄漏或者应用程序异常退出。
## quitSafely 方法(API 18+)
从 Android API 18(Android 6.0 Marshmallow)开始,`HandlerThread` 还提供了一个名为 `quitSafely()` 的方法。这个方法与 `quit()` 在功能上相似,但它可以更优雅地处理退出时的场景。当调用 `quitSafely()` 或者 `quit()` 时,Looper 将尝试优雅地停止接收新的消息,并通知所有已注册的处理器进行清理工作。在处理完所有消息后,Looper 将关闭自身。以下是一个简单的示例:
```java
public class MyHandlerThread extends HandlerThread {
public MyHandlerThread(String name) {
super(name);
}
@Override
public void run() {
Looper.prepare();
Handler handler = new MyHandler();
handler.sendEmptyMessage(0);
Looper.loop();
}
}
```
```java
public class MyActivity extends AppCompatActivity {
private MyHandlerThread mHandlerThread;
{
mHandlerThread = new MyHandlerThread("MyHandlerThread");
mHandlerThread.start();
}
}
```