背景
在Android中,当应用程序启动时,系统会为应用程序创建一个执行线程,称为主线程(也称为UI线程)。 Google 介绍了主线程及其负责人如下。
主线程有一个非常简单的设计:它唯一的工作就是接受和
从线程安全的工作队列中执行工作块,直到它的应用程序
终止。该框架生成其中一些工作块
各种各样的地方。这些地方包括与
生命周期信息、用户事件(如输入)或即将到来的事件
从其他应用程序和进程。另外,app可以显式入队
自己的块,不使用框架。
您的应用执行的几乎所有代码块都与事件相关联
回调,例如输入、布局膨胀或绘制。当某事
触发事件,事件发生的线程推送
事件本身,并进入主线程的消息队列。这
然后主线程可以为事件提供服务。
当动画或屏幕更新发生时,系统会尝试
执行一个工作块(负责绘制屏幕)
每 16 毫秒左右,以便以每秒 60 帧的速度平滑渲染。
为了使系统达到这个目标,UI/View 层次结构必须更新
在主线程上。但是,当主线程的消息队列
包含对主要任务来说太多或太长的任务
线程以足够快的速度完成更新,应用程序应该移动它
工作到工作线程。如果主线程无法完成执行
在 16 毫秒内完成工作块,用户可能会观察到搭便车、滞后或
缺乏对输入的 UI 响应。如果主线程阻塞
大约 5 秒,系统显示 Application Not
响应 (ANR) 对话框,允许用户直接关闭应用程序。
要更新视图,必须在主线程中进行,如果尝试在后台线程中更新,系统会抛出CalledFromWrongThreadException。
如何从后台线程更新主线程上的视图?
主线程分配有一个Looper 和一个MessageQueue。要更新视图,我们需要创建一个任务,然后将其放入 MessageQueue。为此,Android 提供
Handler API 允许我们将任务发送到主线程的 MessageQueue 以便稍后执行。
// Create a handler that associated with Looper of the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());
// Send a task to the MessageQueue of the main thread
mainHandler.post(new Runnable() {
@Override
public void run() {
// Code will be executed on the main thread
}
});
为了帮助开发人员轻松地从后台线程与主线程进行通信,Android 提供了几种方法:
在后台,他们使用 Handler API 来完成他们的工作。
回到你的问题
AsyncTask
这是一个设计为围绕Thread 和Handler 的辅助类的类。它负责:
ThreadPoolExecutor
在 Java 中创建和处理线程有时很困难,如果开发人员处理不当,可能会导致很多错误。 Java 提供了 ThreadPoolExecutor 来更有效地创建和管理线程。
此 API 不提供任何更新 UI 的方法。
Kotlin Coroutines
Coroutines 是一种在 Android 上进行异步编程的解决方案,用于简化异步执行的代码。但它仅适用于 Kotlin。
所以我的问题是,传达结果的正确方式是什么
后台线程什么时候完成?。
1.使用Handler或基于Handler构建的机制
1.1.如果线程与Activity/Fragment绑定:
1.2.如果一个线程有一个视图的引用,比如Adapter类。
1.3.如果线程没有绑定到任何UI元素,那么你自己创建一个Handler。
Handler mainHandler = new Handler(Looper.getMainLooper);
注意: 使用 Handler 的一个好处是您可以使用它在线程之间进行 2 种方式的通信。这意味着您可以从后台线程向主线程的 MessageQueue 发送任务,而从主线程,您可以向后台的 MessageQueue 发送任务。
2。使用BroadcastReceiver
此 API 旨在让 Android 应用可以发送和接收来自 Android 系统、应用内其他应用或组件(Activity、Service 等)的广播消息,类似于 publish-subscribe 设计伙伴。
因为BroadcastReceiver.onReceive(Context, Intent)方法默认是在主线程中调用的。因此,您可以使用它来更新主线程上的 UI。例如。
从后台线程发送数据。
// Send result from a background thread to the main thread
Intent intent = new Intent("ACTION_UPDATE_TEXT_VIEW");
intent.putExtra("text", "This is a test from a background thread");
getApplicationContext().sendBroadcast(intent);
从活动/片段接收数据
// Create a broadcast to receive message from the background thread
private BroadcastReceiver updateTextViewReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String text = intent.getStringExtra("text");
myTextView.setText(text);
}
};
@Override
protected void onStart() {
super.onStart();
// Start receiving the message
registerReceiver(updateTextViewReceiver, new IntentFilter("ACTION_UPDATE_TEXT_VIEW"));
}
@Override
protected void onStop() {
// Stop receving the message
unregisterReceiver(updateTextViewReceiver);
super.onStop();
}
此方法通常用于Android应用程序之间或Android应用程序与系统之间的通信。实际上,你可以使用它来实现Android应用中组件之间的通信,例如(Activity、Fragment、Service、Thread等),但它需要大量的代码。
如果你想要一个类似的解决方案,但代码更少,易于使用,那么你可以使用下面的方法。
3。使用EventBus
EventBus 是适用于 Android 和 Java 的发布/订阅事件总线。如果要执行在主线程上运行的方法,只需用@Subscribe(threadMode = ThreadMode.MAIN)注解标记即可。
// Step 1. Define events
public class UpdateViewEvent {
private String text;
public UpdateViewEvent(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
// Step 2. Prepare subscriber, usually inside activity/fragment
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
myTextView.setText = event.getText();
};
// Step 3. Register subscriber
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
// Step 4. Unregister subscriber
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
// Step 5. Post events from a background thread
UpdateViewEvent event = new UpdateViewEvent("new name");
EventBus.getDefault().post(event);
当您想要在用户可见活动/片段时更新视图(他们正在与您的应用交互)时,这很有用。