我需要帮助理解使用线程的视图操作。我有TextView
我希望通过Thread
操作的文字。这是我的代码:
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
mTextView.setText("foo");
}
});
thread.run();
我最初的理解是不能从后台线程操纵视图。但是这个示例确实将TextView
的文本更改为“foo”:在这种情况下究竟发生了什么?我在Activity的onCreate方法中运行了这个。
这是否意味着这里的TextView确实是在UI线程上操作的?如果我错过了一些非常明显的事情,我会道歉。
答案 0 :(得分:4)
问题是你以错误的方式启动线程。
你调用thread.run()
,这只是在UI线程中执行run()
方法。
如果要执行单独的线程,则应该调用
thread.start()
。这是它的工作原理。
在这种情况下,您将捕获访问UI线程之外的UI元素的异常。
修改强>
此外,您可能在onCreate()
方法中执行代码。
该线程花费很少的时间,因此如果它在onCreate()
方法内,它会在UI甚至出现在屏幕上之前完成。
例如,如果您将代码移至onResume()
,并在访问run()
之前在TextView
方法中添加一些睡眠代码,请执行以下操作:
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
它会崩溃。
<强> EDIT2 强>
这是否意味着直到经过一段时间后才从线程池中分配新线程?
没有。新线程分配与时间无关。 它一开始就开始...... 问题是它执行得太快了。
不失一般性,让我们继续TextView
作为我推理的主题。 TextView
是一个普通班级。您创建该类的实例并使用它来操作相应的低级UI元素。为TextView
分配新文字时会发生什么?首先,新值已写入其实例字段。然后,您的实例会触发相应的低级UI元素,例如:“嘿,我的状态已更改。请在屏幕上更新它。”如果屏幕可见,那么UI元素会说:“好了,得到它”,获取新值并自行更新。但如果屏幕不可见,则表示UI元素尚不存在。没有人可以回答,触发器是徒劳的。
这是主要部分。当您调用setText()
类的TextView
方法时,所有这一切都会发生。但是在非UI线程中仅禁止“更新UI元素”-part。从单独的线程访问和更改TextView's
实例字段完全合法。问题是你不能没有另一个。
那么为什么它不会在我们的情况下崩溃?
我们的线程只做一件事 - 文本更改,因此它甚至在onCreate()
方法返回之前完成得非常快。并且因为UI仅在onResume()
方法返回后变得可见,所以“从单独的非UI线程访问UI元素”实际上与UI无关,因为此UI甚至还不存在,因此您的更改不会触发任何UI元素。但是当UI实际变得可见时,它只是读取TextView
实例的当前状态 - 已经更改为新值 - 并在UI线程中自行更新。
通常,即使将您的线程放在onResume()
方法中也不能保证它会在onResume()
方法返回后完成。
通过在我们的线程中添加Thread.sleep(...)
方法,我们只是给它一些时间并有机会保持活着直到onResume()
返回,实际UI被加载并出现在屏幕上。在此之后,“access-UI-only-from-UI-thread” RULE 生效并使应用程序崩溃。
通常我们不需要人为地延长线程的生命周期以捕获崩溃,就像在这种情况下一样,因为我们正在使用的活动或片段通常已经运行了一段时间并且它们的UI是已经可见了。
因此,重点是在非UI线程能够访问它之前UI是否可见。
如果我错了,有人会纠正我,但这是我对这一切的理解。
答案 1 :(得分:0)
使用它可以启动一个异步运行的任务,但是你有一些将在主线程中运行的方法(onPreExecute
和onPostExecute
)
答案 2 :(得分:0)
这里发生的是你生成一个与UI线程不同的后台线程。您无法修改该线程中的视图。您将创建一个处理程序,它将向UI线程发送消息以告知它更新。
有关详细信息,请参阅https://developer.android.com/training/multiple-threads/communicate-ui.html