Нет ошибки "только исходный поток, который создал вид иерархии может коснуться его мнения", когда представление обновляется без задержек

я столкнулся с интересной проблемой. Если вы напишете следующий код в 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 и пришел к следующим выводам. Nandeesh написал правду. При инициализации представления называется 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.");
        }
    }

таким образом, если представление не было инициализировано, нет проверки и изменения не бросает исключений.

1 ответов


проверка потока присутствует, только если textView relayout сделано. Но разметка вида производится только после OnCreate называется . Поэтому, пока пользовательский интерфейс не будет показан, изменение textView не приведет к недействительности представления.

но как только textView был показан, требуется ретрансляция пользовательского интерфейса, и в этом случае поток проверяется. Таким образом, вы получаете исключение только через некоторое время Oncreate, но не сразу.