Поток может изменить пользовательский интерфейс, если запущен изнутри методов жизненного цикла активности. Зачем?

Я тестировал этот код, чтобы проверить, происходит ли сбой приложения при изменении компонента пользовательского интерфейса из фонового потока. но это не так.

Здесь в коде, добавленном ниже. Я начал новую тему в onCreate() метод MainActivity и он должен был разбиться согласно документам Android, который говорит

В классе метод Runnable.run() содержит код, который выполняется. Обычно в Runnable все допустимо. Помните, однако, что Runnable не будет работать в потоке пользовательского интерфейса, поэтому он не может напрямую изменять объекты пользовательского интерфейса, такие как объекты View.

Так что я ожидал, что он рухнет. Что это не так. Смотрите код -

public class MainActivity extends AppCompatActivity {

  TextView txt;
  Thread thread;
  Runnable runnable = new Runnable() {
    @Override
    public void run() {
      txt.setText("bro");
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    txt = (TextView) findViewById(R.id.name_txt);
    thread = new Thread(runnable);
    thread.start();
  }
}

Хотя, если я пытаюсь изменить пользовательский интерфейс при запуске темы из onClicklistener() как показано ниже, происходит сбой. что ожидается.

public class MainActivity extends AppCompatActivity {

  TextView txt;
  Thread thread;
  Runnable runnable = new Runnable() {
  @Override
  public void run() {
    txt.setText("bro");
  }
  };

  View.OnClickListener listener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
     thread.start();
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    txt = (TextView) findViewById(R.id.name_txt);
    thread = new Thread(runnable);
    txt.setOnClickListener(listener);
  }
}

Теперь, когда второй фрагмент кода дает сбой, что ожидается, а первый - нет.

Пожалуйста, объясните, почему это происходит, так как я создаю новый рабочий поток каждый раз, но только в разных местах. Официальные ссылки на документы будут оценены.

2 ответа

Решение

Я нашел причину такого поведения, как указано @krish в комментариях к моему вопросу. Причина в том, что поток смог внести изменения в TextView объект только до тех пор, пока он не был виден на экране UI т.е. не отображается. Только после рендеринга представления любой поток, кроме основного потока, не может вносить изменения в какие-либо компоненты пользовательского интерфейса. Я пытался с помощью view viewer увидеть, был ли вид отрисован до изменений или нет. который показал, что изменения были сделаны перед рендерингом представления.

Вот код, который я попробовал.

public class MainActivity extends AppCompatActivity {

  TextView txt;
  Thread thread;
  Runnable runnable = new Runnable() {
    @Override
    public void run() {
      txt.setText("bro");
      Log.d("ThreadTest", "The Text was changed.");
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    txt = (TextView) findViewById(R.id.name_txt);
    thread = new Thread(runnable);

    txt.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
        Log.d("ThreadTest", "The TextView was rendered");
      }
    });
  }

  @Override
  protected void onResume() {
    super.onResume();
    thread.start();
  }
}

Используя код выше. Вы увидите в выводе:

Текст был изменен.

TextView был представлен

Что означает, что текст был изменен до отображения представления. если вы попытаетесь запустить поток, чтобы внести изменения в методе onGlobalLayout. Приложение вылетает, как и должно.

Интерфейс пользователя не ориентирован на многопотоковое представление процессов и потоков, поэтому вам просто повезло, что вы не попали в одну из множества мин, ожидающих вас.
Если вам не нравится полагаться на удачу, тогда:
Вы должны использовать:

runOnUiThread(runnable);

вместо:

thread = new Thread(runnable);

AsyncTask позволяет правильно и легко использовать поток пользовательского интерфейса.

Другие вопросы по тегам