Handler/Looper → Coroutines
老写法(Java)
java
// 延迟执行
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
updateUI();
}
}, 1000);
// 子线程发消息到主线程
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
String data = (String) msg.obj;
updateUI(data);
}
}
};
// 在子线程发送
Message msg = handler.obtainMessage(1, "结果");
handler.sendMessage(msg);
// 定时重复任务
handler.postDelayed(new Runnable() {
@Override
public void run() {
doPeriodicWork();
handler.postDelayed(this, 5000);
}
}, 5000);
问题在哪里
Handler 三个问题:线程间消息用 what 整型区分,代码一多根本记不住哪个数字代表什么;Message.obj 强转类型不安全;忘记 removeCallbacks 会造成内存泄漏。
新写法(Kotlin + Coroutines)
kotlin
// 延迟执行
viewModelScope.launch {
delay(1000)
updateUI()
}
// 子线程返回结果到主线程
viewModelScope.launch {
val result = withContext(Dispatchers.IO) {
doHeavyWork()
}
updateUI(result)
}
// 定时重复任务
viewModelScope.launch {
while (isActive) {
doPeriodicWork()
delay(5000)
}
}
// 线程间通信 --- 用 Channel 替代 Handler 消息
private val channel = Channel<String>(capacity = Channel.BUFFERED)
viewModelScope.launch {
for (data in channel) {
updateUI(data)
}
}
一句话注意
delay() 是挂起函数,不阻塞线程。和 Thread.sleep() 完全不同------delay 期间当前协程让出线程资源,其他协程可以继续使用该线程。Handler 的 postDelayed 依赖 MessageQueue,存在 Handler 持有 Activity 引用导致泄漏的风险,而协程配合 viewModelScope 自动取消,不存在这个问题。
Channel 替代 Handler 消息时注意,如果生产者快过消费者,Channel.BUFFERED 默认 64 个元素,超出会挂起生产者。如果需要无线缓冲用 Channel.UNLIMITED,但要注意内存。
Java Android 老项目迁移系列,持续更新中。