Изменение UI из background потока. Неодназначное поведение

232
08 сентября 2017, 20:41

Как известно, в Android обращаться к вью элементам можно только из того потока, в котором они были созданы. То есть только из UI (он же main) Thread. Но как видно из примера ниже, видимо есть какие-то исключения (либо я что-то не так понимаю):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final EditText et = (EditText) findViewById(R.id.et);
    String name = currentThread().getName();
    long id = Thread.currentThread().getId();
    Log.i("THREAD", String.format(Locale.getDefault(),
            "Outside runnable. Thread name = %s, threadId = %d", name, id));
    // Создаём новый поток, запускаем его и изменяем UI
    new Thread(new Runnable() {
        @Override
        public void run() {
            String name = currentThread().getName();
            long id = Thread.currentThread().getId();
            Log.i("THREAD", String.format(Locale.getDefault(),
            "Inside runnable. Thread name = %s, threadId = %d", name, id));
            et.setText("From background");
            }
    }).start();
    // Создаём новый поток, запускаем его и изменяем UI при клике на кнопку
    findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String name = currentThread().getName();
                    long id = Thread.currentThread().getId();
                    Log.i("THREAD", String.format(Locale.getDefault(),
                            "Inside runnable with click. Thread name = %s, threadId = %d", name, id));
                    et.setText("From background with click");
                }
            }).start();
        }
    });

Имеется поле EditText, в которое мы пытаеся установить новый текст из бекграунд потока. В первом случае поток создаётся и запускается в методе onCreate. Во втором, поток создаётся и запускается при клике на кнопке.

Я ожидаю, что получу исключение android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. в первом же случае. Но этого не происходит и текст в поле корректно изменяется. Но во втором случае, при запуске потока по клике, данное исключение ожидаемо выбрасывается. Для уверенности, логирую имена и id потоков, в которых выполняются действия:

I/THREAD: Outside runnable. Thread name = main, threadId = 1
I/THREAD: Inside runnable. Thread name = Thread-64100, threadId = 64100
// Текст изменился
// Клик по кнопке
I/THREAD: Inside runnable with click. Thread name = Thread-64105, threadId = 64105
// Exception
android.view.ViewRootImpl$CalledFromWrongThreadException: 
        Only the original thread that created a view hierarchy can touch its views.

Кто нибудь может объяснить, почему мы не получаем исключение в первом случае? Чем он отличается от второго? Это нюансы работы с потоками в Java или особенности Android фреймворка?

READ ALSO
Сортировка коллекций list

Сортировка коллекций list

Как в коллекции, например, list реализовать механизм сортировки элементов, чтобы удалились те элементы, что делятся на 2 и 3

277
Как из вложенного класса обратится к объекту внешнего класса?

Как из вложенного класса обратится к объекту внешнего класса?

Как из вложенного класса правильно обратится к объекту внешнего класса?

270
Не получается распарсить JSON

Не получается распарсить JSON

Есть вот такой JSON:

266
NullPointerException при вызове метода setVisibility

NullPointerException при вызове метода setVisibility

В погодном приложении выдает ошибку NullPointerException на строчке: mWeatherLayoutsetVisibility(View

261