HandlerThread的必要性
在Android开发中,我们经常会遇到需要创建和处理多个线程的情况。有时候,我们需要在子线程中执行一些操作,然后将结果传递给主线程进行处理。为了实现这个功能,我们需要使用HandlerThread来简化子线程与子线程之间的通信过程。那么,为什么需要使用HandlerThread呢?
HandlerThread的本质
HandlerThread本质上就是一个普通的Thread,只不过在内部建立了一个Looper和一个消息队列。Looper负责处理消息队列中的事件,而Handler则是用于处理这些事件的具体实现。通过这种方式,HandlerThread为子线程提供了一个独立的运行环境,使得它们可以在这个环境中执行任务并与其他线程进行通信。
简化子线程与子线程之间的通信
由于HandlerThread为子线程提供了一个独立的运行环境,因此它可以简化子线程与子线程之间的通信过程。在传统的多线程编程中,我们需要使用各种方法来实现不同线程之间的数据交换,例如使用Intent、Bundle等。而在HandlerThread中,我们可以通过Handler来实现子线程与主线程之间的通信,这样就避免了繁琐的数据交换操作。
总之,HandlerThread在Android开发中具有重要的作用,它可以帮助我们简化子线程与子线程之间的通信过程,提高开发效率。
public class MainActivity extends AppCompatActivity {
private Handler handler1;
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyThread1().start();
new MyThread2().start();
}
class MyThread1 extends Thread {
@Override
public void run() {
super.run();
Looper.prepare();
handler1 = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("threadName--" + Thread.currentThread().getName() + "messageWhat-" + msg.what);
}
};
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
handler2.sendEmptyMessage(2);
Looper.loop();
}
}
class MyThread2 extends Thread {
@Override
public void run() {
super.run();
Looper.prepare();
handler2 = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("threadName--" + Thread.currentThread().getName() + "messageWhat-" + msg.what);
}
};
try {
sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler1.sendEmptyMessage(5);
Looper.loop();
}
}
}
HandlerThread是一个继承自Thread的类,它可以用来处理子线程的消息队列,而不需要创建一个新的Looper。HandlerThread的用法可以参考这篇文章:
Handler是Android中的一个重要组件,它是用来处理消息队列中的消息的。HandlerThread和Handler之间的关系可以参考这篇文章:
```java
public class MainActivity extends AppCompatActivity {
private HandlerThread myHandlerThread;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建一个线程,线程名字:handler-thread
myHandlerThread = new HandlerThread("handler-thread");
//开启一个线程
myHandlerThread.start();
//在这个线程中创建一个handler对象
handler = new Handler(myHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
Log.d("handler", "消息: " + msg.what + " 线程: " + Thread.currentThread().getName());
}
};
//在主线程给handler发送消息
handler.sendEmptyMessage(1);
new Thread(new Runnable() {
@Override
public void run() {
//在子线程给handler发送数据
handler.sendEmptyMessage(2);
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
//释放资源
if (myHandlerThread != null) {
myHandlerThread.quit();
}
}
}
```
HandlerThread退出循环的方法有三种:handlerThreadquit()、handlerThread.quitSafely()和handlerThread.quitAndWait()。
相同点:将不再接受新的事件加入消息队列。无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了。
不同点:当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
需要注意的是Looper的quit方法从API Level 1就存在了,但是Looper的quitSafely方法从API Level 18才添加进来。
HandlerThread的主要作用是将主线程中的Looper任务转移到子线程中执行,从而降低主线程的压力,使主界面更加流畅,不会阻塞UI线程。HandlerThread本质上是一个线程,在线程内部,代码是串行处理的。
对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
在使用HandlerThread时,需要注意在不需要使用的时候进行手动回收,以避免资源浪费。
以下是创建和启动HandlerThread的示例代码:
```java
// 创建一个线程,线程名字:handler-thread
myHandlerThread = new HandlerThread("handler-thread");
// 开启一个线程
myHandlerThread.start();
```
通过这段代码,我们成功创建了一个名为"handler-thread"的HandlerThread,并启动了这个线程。在实际应用中,我们需要在合适的时机回收HandlerThread以释放资源。
```java
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
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;
}
}
```
我们实际上是初始化并启动了一个线程。在run()方法中,我们调用了Looper.prepare()和Looper.loop()。prepare()方法创建了一个Looper对象,并将其放入该线程范围内的变量(sThreadLocal)中。在Looper对象的构造过程中,它初始化了一个MessageQueue,作为该Looper对象的成员变量。当没有消息时,loop()会阻塞,当有消息到来时,它会唤醒。
接下来,我们创建了一个handler:
```java
Handler handler = new Handler(myHandlerThread.getLooper());
```
```java
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 如果线程已经启动,等待直到looper被创建。
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
```
在这段代码中,`myHandlerThread.getLooper()`返回的就是我们在`run`方法中创建的`mLooper`。如果你足够细心,你会发现,在`run`方法里当`mLooper`创建完成后有个`notifyAll()`,而`getLooper()`中有个`wait()`。这是为什么呢?因为`mLooper`在一个线程中执行,而我们的handler是在UI线程初始化的,也就是说,我们必须等到`mLooper`创建完成,才能正确地返回`getLooper()`。`wait()`和`notify()`就是为了解决这两个线程的同步问题。