Поток может изменить пользовательский интерфейс, если запущен изнутри методов жизненного цикла активности. Зачем?
Я тестировал этот код, чтобы проверить, происходит ли сбой приложения при изменении компонента пользовательского интерфейса из фонового потока. но это не так.
Здесь в коде, добавленном ниже. Я начал новую тему в 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 позволяет правильно и легко использовать поток пользовательского интерфейса.