AsyncTask не может работать в потоке Android

#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 и соответствующим образом обновляет пользовательский интерфейс и т.д. Однако эти решения кажутся излишними.