我对Android系统的工作原理感到困惑,特别是当它更新视图层次结构时。 我们都知道不应该从UI(主)线程以外的任何线程更新任何视图。当我们尝试这样做时,甚至 Android系统会抛出异常。 前几天我试图在我的应用程序中实现显示视图的自定义进度。所以我开始使用标准的Java线程和处理程序组合。
我发现让我感到惊讶,因为我能够从后台线程更新TextView。
new Thread(new Runnable() {
@Override
public void run() {
mTextView.setText("I am " + Thread.currentThread().getName());
}
}).start();
之后我尝试更新其他视图,效果非常好。所以我尝试在后台线程中进行睡眠调用。
new Thread(new Runnable() {
@Override
public void run() {
mTextView.setText("Thread : before sleep");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mTextView.setText("Thread : after sleep");
}
}).start();
它如预期的那样崩溃
android.view.ViewRootImpl $ CalledFromWrongThreadException:只有 创建视图层次结构的原始线程可以触及其视图。
然后我尝试在sleep()调用之前和之后将循环中的setText()调用放置100次,1000次。当然,应用程序每次都崩溃,但我能够在textview上看到“Before Sleep”文本。
所以我的问题是 系统何时检测到某些非UI线程正在尝试更新视图 。并且 为什么在非UI线程中没有sleep()调用时它不起作用 ?
答案 0 :(得分:2)
我在Lollipop中使用sleep
运行您的代码段,然后崩溃了。堆栈跟踪是:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6357)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:909)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:4690)
at android.view.View.invalidateInternal(View.java:11801)
at android.view.View.invalidate(View.java:11765)
at android.view.View.invalidate(View.java:11749)
at android.widget.TextView.checkForRelayout(TextView.java:6850)
at android.widget.TextView.setText(TextView.java:4057)
at android.widget.TextView.setText(TextView.java:3915)
at android.widget.TextView.setText(TextView.java:3890)
at com.test.MainActivity$16.run(MainActivity.java:1126)
at java.lang.Thread.run(Thread.java:818)
因此密钥隐藏在TextView.setText
的第4057行附近:
if (mLayout != null) {
checkForRelayout();
}
我们可以看到mLayout
的{{1}}是TextView
,null
是否会被调用,因此应用不会崩溃。 checkForRelayout()
将在mLayout
的{{1}}中进行初始化。因此,第一次调用onDraw
时应用程序不会崩溃,因为TextView
为空。绘制后,setText
已初始化,导致应用在第二次调用mLayout
时崩溃。
我猜您在mLayout
被绘制之前开始setText
(例如在Thread
或TextView
中)。正确?
应用程序是否崩溃取决于您拨打的时间onCreate
。如果您在首次绘制onResume
之前致电setText
,一切正常。否则应用程序崩溃。
答案 1 :(得分:-1)
Thread是UI线程的并行进程。当您尝试将sleep函数放入线程时,线程的执行会停止。你问题的答案就在问题本身内部。它说 - 只有创建视图层次结构的原始线程才能触及其视图。所以其他的是两个线程运行一个ui线程,另一个是你创建的。当你调用睡眠方法时。你的线程停止执行与ui线程没有同步。当你的线程试图改变textview的文本时,线程都不同步。在睡眠之前,线程是同步的。睡觉后他们不同步。