gpt4 book ai didi

java - 从后台线程异常设置 TextView 上的文本

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:44:31 26 4
gpt4 key购买 nike

我刚开始玩 Android 并发/Loopers/Handers,我刚刚遇到了奇怪的异常情况。下面的代码不会阻止我在不同线程的 TextView 上设置文本。

TextView tv;
Handler backgroundHandler;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

tv = (TextView) findViewById(R.id.sample_text);
Runnable run = new Runnable() {
@Override
public void run() {
Looper.prepare();
backgroundHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String text = (String) msg.obj;
tv.setText(Thread.currentThread().getName() + " " + text);
}
};
Looper.loop();
}
};

Thread thread = new Thread(run);
thread.setName("Background thread");
thread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = backgroundHandler.obtainMessage();
message.obj = "message from UI";
backgroundHandler.sendMessage(message);
}

猜猜会发生什么

enter image description here

但是,当我让后台线程 hibernate 一段时间

backgroundHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String text = (String) msg.obj;
tv.setText(Thread.currentThread().getName() + " " + text);
}
};

它确实像我预期的那样抛出异常

07-03 18:54:40.506 5996-6025/com.stasbar.tests E/AndroidRuntime: FATAL EXCEPTION: Background thread
Process: com.stasbar.tests, PID: 5996
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7275)

有人可以向我解释发生了什么吗?为什么我能够设置来自不同线程的文本?

最佳答案

从第一次尝试开始,您的代码就是错误的,因为您在不同的线程上创建了 Handler。这导致循环器/处理程序在不同的线程上运行。根据您的评论,我猜您知道这个问题,并且您想了解为什么第一次尝试时不抛出异常,而第二次尝试抛出异常。

您应该注意这一点:在除UI Thread 之外的不同线程上访问 UI 元素会导致未定义的行为。这意味着:

  • 有时您会发现它有效。
  • 有时你不会。如您所见,您将遇到异常。

这意味着在不同线程上访问 UI 元素并不总是 100% 可重现。

为什么您应该只在 UI 线程上访问所有 UI 元素?因为处理 UI 元素(更改内部状态、绘制到屏幕...)是一个复杂的过程,需要在相关方之间进行同步。例如,您在屏幕上都可见的 2 个 fragment 上调用 ​​TextView#setText(String)。 Android 不会同时执行此操作,而是将所有作业推送到 UI 消息队列中并按顺序执行。不仅从您的应用程序的角度来看,而且从整个 Android 系统的角度来看也是如此。从系统调用的状态栏更新,从您的应用程序调用的应用程序更新总是在处理之前将操作推送到相同 UI 消息队列。

当您在不同线程上访问和修改 UI 元素时,您破坏了该进程。这意味着也许两个线程可能会在同一状态和同一时间访问和修改一个元素。因此,您有时会遇到竞争条件。发生错误时。

很难解释您的情况,因为没有足够的数据进行分析。但是有一些原因:

  • 第一次尝试时,TextView 尚未显示在屏幕上。所以不同的线程能够对 TextView 进行更改。但是在你第二次尝试时,你睡了 1 秒钟。此时,所有 View 都已成功渲染并显示在屏幕上,因此抛出异常。您可以尝试 Thread.sleep(0),希望您的代码不会崩溃。
  • 这在某些情况下会发生,但很难猜出原因。碰巧,您的线程和 ui 线程访问同一个锁对象,抛出异常。

您可以在此处阅读有关线程问题的更多信息 Android Thread

Explicit references

Many tasks on non-main threads have the end goal of updating UI objects. However, if one of these threads accesses an object in the view hierarchy, application instability can result: If a worker thread changes the properties of that object at the same time that any other thread is referencing the object, the results are undefined.

希望对你有帮助。

关于java - 从后台线程异常设置 TextView 上的文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44890807/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com