1、子线程到主线程通信都有哪些方式?子线程到主线程通信的原理?
这个是 Android 特有的概念。线程间的通信方式:eventbus、rxjava、livedata。然而,这些方式的底层原理都是基于 handler,所以,下面我将为大家讲解 handler 通信原理。
handler调度流程:
子线程: handler.sendMessage(msg) =》 handler.enqueueMessage =》MessageQueue.enqueueMessage()
主线程:Looper.loop=》queue.next()=》handler.handleMessage()
handler的核心原理图:
两个重要类的图解:
2、handler内存泄漏的原因是什么?
JVM 垃圾回收机制:GCroot 回收机制
持有链: static sThreadLocal -》 mLooper -》MessageQueue -》msg -》handler -》 activity
解决方法:打破持有链
3、子线程中如何创建handler?
- 写法一:直接在子线程创建 handler,错误
new Thread(
() -> {
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
});
mHandler.sendMessage(new Message());
}
).start();
报错展示:
Process: com.wust.empty02, PID: 25002
java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:207)
at android.os.Handler.<init>(Handler.java:133)
at com.wust.empty02.MainActivity.lambda$onCreate$0$MainActivity(MainActivity.java:20)
at com.wust.empty02.MainActivity$$ExternalSyntheticLambda0.run(Unknown Source:2)
at java.lang.Thread.run(Thread.java:919)
报错源码位置:
- 写法二:正确写法
new Thread(
() -> {
//创建该线程的 looper
Looper.prepare();
//创建该线程的 handler
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
});
//looper开启循环
Looper.loop();
}
).start();
这种写法有一个缺点:这样的话 该线程就只能创建一个handler。
- 写法三:通过 public Handler(@NonNull Looper looper) 该构造方法进行 handler 创建
package com.wust.empty02;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyHandlerThread t = new MyHandlerThread();
t.start();
try {
//这句话如果去掉 就会导致 t.getmLooper() 为空,因为你无法保证 Handler handler = new Handler(t.getmLooper()); 在 t 线程 run 之后执行
Thread.sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Handler handler = new Handler(t.getmLooper());
}
}
class MyHandlerThread extends Thread {
private Looper mLooper;
@Override
public void run() {
Looper.prepare();
this.mLooper = Looper.myLooper();
Looper.loop();
}
public Looper getmLooper() {
return mLooper;
}
}
从上面代码的注释 就可以很明显的看出其缺点之所在了。无法保证 t.getmLooper() 在 线程 run 方法之后执行。
- 方法四:使用 HandlerThread 创建线程
HandlerThread t = new HandlerThread("wustyq");
t.start();
Handler handler = new Handler(t.getLooper());
系统的这个 HandlerThread 中的 run 方法为什么就能保证在 t.getLooper() 之前执行呢??关键代码 + 关键思想如下:
为什么要用 synchronized ? 为了解决并发操作 =》 即 run 和 getLooper 只能同时执行一个。
为什么要用 notifyAll() ?因为有可能多个线程调用了wait()方法进行等待。
为什么要用 while?不用 if?因为这个 wait() 可能由别的线程 notify() 唤醒。所以要使用while反复检测。
4、子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?主线程呢?
quit:loop退出。防止内存泄漏。不会被停止。
5、handler如何处理发送延时消息?
会调用底层 Linux =》 epoll 完成等待
6、我们使用Message时应该如何创建它?
obtain()