#android #thread-safety #android-asynctask
#Android #потокобезопасность #android-asynctask
Вопрос:
Я использую AsyncTask для изменения текста TextView следующим образом:
private class LongOperation extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... urls) {
String response = "";
for (String url : urls) {
response = url;
}
return response;
}
@Override
protected void onPostExecute(String result) {
textView.setText(result);
}
}
Все будет хорошо, если я вызову его в событии OnClick:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = (TextView) findViewById(R.id.txt);
Button button = (Button)this.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new LongOperation().execute(new String[]{"Hello"});
}
});
}
Но проблема, когда я вызвал ее в своем потоке, программа принудительно закрылась
this.closeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Thread t= new Thread(){
@Override
public void run(){
try{
//Do something
//Then call AsyncTask
new LongOperation().execute(new String[]{"Hello"});
}catch(Exception e){}
}
};
t.start();
}
});
Где я не прав? Я не понимаю, какая разница, вызывать AsyncTask в потоке или нет.
Комментарии:
1. Мне любопытно, зачем вам нужно вызывать AsyncTask в другом потоке, поскольку AsyncTask сам по себе уже является отдельным потоком. Похоже, что комментарий // Do something, который вы разместили в другом потоке, может быть частью doInBackground в LongOperation. Возможно, у вас есть допустимый вариант использования, но я редко видел такое использование потока внутри потока, как то, что вы пытаетесь сделать.
Ответ №1:
Я рекомендую вам ознакомиться с документацией AsyncTask и процессами и потоками для лучшего понимания того, как это работает. По сути, вы должны создать свой подкласс AsyncTask в основном потоке.
Когда вы вызываете AsyncTask.execute(), предоставленный вами, AsyncTask.onPreExecute вызывается в главном потоке, так что вы можете выполнить настройку пользовательского интерфейса.
Вызывается следующий метод AsyncTask.doInBackground, который выполняется в его собственном потоке.
Наконец, когда ваш метод AsyncTask.doInBackground завершается, выполняется вызов AsyncTask.onPostExecute в главном потоке, и вы можете выполнить любую очистку пользовательского интерфейса.
Если вам нужно обновить пользовательский интерфейс из вашего метода AsyncTask.doInBackground, вызовите AsyncTask.publishProgress, который вызовет onProgressUpdate в главном потоке.
Комментарии:
1. Спасибо, я прочитал документ и примеры. Я знал, как это работает. Я нашел это правило: экземпляр задачи должен быть создан в потоке пользовательского интерфейса. Но я хочу вызвать его в своем собственном потоке. Как я могу это сделать?
2. Асинхронная задача сама по себе является therad, поэтому вам не нужно помещать в поток
Ответ №2:
Когда вы вызываете его из потока пользовательского интерфейса, связанный с ним Context
является запущенным Activity
. Когда вы вызываете его из обычного потока, с этим потоком не связано ни одного действительного значения Context
. AsyncTask
выполняется в своем собственном потоке, вам не следует создавать его собственный поток. Если это реальный код, то вы неправильно поняли суть AsyncTask
. Найдите руководства по его использованию.
Комментарии:
1. Спасибо, я прочитал документ и несколько руководств, я знал, как им пользоваться. Но проблема в том, что я должен использовать thread и что-то делать. После этого пользовательский интерфейс должен быть обновлен. Однако я не могу найти способ попробовать это.
2. Если вы должны создать поток вне основного потока пользовательского интерфейса, то AsyncTask — неправильный путь. Вы все еще можете создавать обычные потоки и выполнять вызовы между ними, используя Runnable и Handler
Ответ №3:
Добавление к тому, что сказали другие: я думаю, вы можете использовать AsyncTask для запуска задачи в другом потоке, даже если вы запускаете AsyncTask из другого потока, отличного от пользовательского интерфейса.
Но в этом случае единственный способ, которым вы сможете изменять пользовательский интерфейс только косвенно, например: каким-либо образом передать обработчик текущего действия этому экземпляру AsyncTask и отправлять ему сообщения (сообщения обработчика обрабатываются в потоке пользовательского интерфейса). Или используйте широковещательные намерения, которые перехватывает Activity и соответствующим образом обновляет пользовательский интерфейс и т.д. Однако эти решения кажутся излишними.