我遇到了一个有趣的问题。如果您在onCreate/onStart/onResume
活动方法中编写以下代码:
final Button myButton = (Button)findViewById(R.id.myButton);
final TextView myTextView = (TextView)findViewById(R.id.myTextView);
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
myTextView.setText("Hello text");
}
});
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
thread.start();
}
});
或:
final TextView myTextView = (TextView)findViewById(R.id.myTextView);
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.currentThread().sleep(500);
}
catch (InterruptedException e) {
e.printStackTrace();
}
myTextView.setText("Hello text");
}
});
thread.start();
它应该如何,抛出错误
android.view.ViewRoot $ CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
很明显,在这种情况下,我必须在ui-thread中更新视图 (Handler, AsyncTask, runOnUiThread, view.post).
但是如果你在没有延迟的情况下更新另一个线程中的视图(没有睡眠调用或没有按下按钮启动线程),异常将不会被抛出。
final TextView myTextView = (TextView)findViewById(R.id.myTextView);
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
myTextView.setText("Hello text");
}
});
thread.start();
有人能告诉我为什么会有这种行为吗?
更新
我已经学习了Android的源代码并得出以下结论。 南德斯写下了真相。 初始化视图时,调用名为dispatchAttachedToWindow(AttachInfo info,int visibility)的View方法,初始化mAttachInfo字段。 mAttachInfo对象具有mViewRootImpl字段。如果为null,则getViewRootImpl将返回null:
public ViewRootImpl getViewRootImpl() {
if (mAttachInfo != null) {
return mAttachInfo.mViewRootImpl;
}
return null;
}
ViewRootImpl包含checkThread方法。它比较线程:创建视图的线程和视图更新请求的线程。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
因此,如果视图未初始化,则没有检查和更改不会抛出异常。
答案 0 :(得分:9)
仅当textView
重新布局完成时才会检查线程。但只有在调用OnCreate
之后才能进行视图的Layouting。因此,直到Ui未显示,更改textView不会导致视图无效。
但是一旦显示了textView,就需要重新布局UI,在这种情况下会检查线程。因此,只有在Oncreate的一段时间后才能获得异常,但不会立即获得异常。