8

我遇到了一个有趣的问题。如果在onCreate/onStart/onResumeactivity的方法中编写如下代码:

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的源码,得出以下结论。Nandeesh写下了真相。初始化视图时调用View的dispatchAttachedToWindow(AttachInfo info, int visibility)方法,该方法初始化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.");
        }
    }

因此,如果视图未初始化,则没有检查和更改不会引发异常。

4

1 回答 1

9

textView仅当重新布局完成时才会检查线程。但是视图的布局仅在OnCreate调用后完成。因此,在未显示 Ui 之前,更改 textView 不会导致视图无效。

但是一旦显示了 textView,就需要重新布局 UI,在这种情况下会检查线程。因此,只有在 Oncreate 一段时间后才会出现异常,但不会立即出现。

于 2012-10-25T07:56:35.917 回答