Вопрос

Я работал с AsyncTasks в Android и столкнулся с проблемой.

Возьмем простой пример, действие с одной AsyncTask.Задача в фоновом режиме не делает ничего впечатляющего, она просто спит в течение 8 секунд.

В конце AsyncTask в методе onPostExecute() я просто устанавливаю статус видимости кнопки для просмотра.ВИДИМЫЙ, только для проверки моих результатов.

Теперь это отлично работает до тех пор, пока пользователь не решит изменить ориентацию своего телефона во время выполнения AsyncTask (в пределах 8-секундного окна ожидания).

Я понимаю жизненный цикл активности Android, и я знаю, что активность уничтожается и воссоздается заново.

Вот тут-то и возникает проблема.AsyncTask ссылается на кнопку и, по-видимому, содержит ссылку на контекст, который в первую очередь запустил AsyncTask.

Я бы ожидал, что этот старый контекст (поскольку пользователь вызвал изменение ориентации) либо станет нулевым, а AsyncTask выдаст NPE для ссылки на кнопку, которую он пытается сделать видимой.

Вместо этого NPE не генерируется, AsyncTask считает, что ссылка на кнопку не равна null, и присваивает ей значение visible .Каков результат?На экране ничего не происходит!

Обновить: Я решил эту проблему, сохранив WeakReference к активности и переключению при изменении конфигурации.Это громоздко.

Вот код:

public class Main extends Activity {

    private Button mButton = null;
    private Button mTestButton = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mButton = (Button) findViewById(R.id.btnStart);
        mButton.setOnClickListener(new OnClickListener () {
            @Override
            public void onClick(View v) {
                new taskDoSomething().execute(0l);
            }
        });
        mTestButton = (Button) findViewById(R.id.btnTest);   
    }

    private class TaskDoSomething extends AsyncTask<Long, Integer, Integer> 
    {
        @Override
        protected Integer doInBackground(Long... params) {
            Log.i("LOGGER", "Starting...");
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 0;
        }

        @Override
        protected void onPostExecute(Integer result) {
            Log.i("LOGGER", "...Done");
            mTestButton.setVisibility(View.VISIBLE);
        }
    }
}

Попробуйте выполнить его и, пока AsyncTask работает, измените ориентацию вашего телефона.

Это было полезно?

Решение

AsyncTask не предназначен для повторного использования после того, как Действие было удалено и перезапущено.Внутренний объект обработчика становится устаревшим, как вы и заявили.В примере с полками Ромена Гая он просто отменяет все выполняемые в данный момент AsyncTask, а затем перезапускает новые после изменения ориентации.

Можно передать свой поток новому Действию, но это добавляет много сантехники.Общепринятого способа сделать это нет, но вы можете прочитать о моем методе здесь : http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html

Другие советы

Если вам нужен только контекст, и вы не будете использовать его для пользовательского интерфейса, вы можете просто передать ApplicationContext в свою AsyncTask.Контекст часто нужен, например, для системных ресурсов.

Не пытайтесь обновлять пользовательский интерфейс из AsyncTask и старайтесь избегать самостоятельной обработки изменений конфигурации, так как это может привести к беспорядку.Чтобы обновить пользовательский интерфейс, вы могли бы зарегистрировать широковещательный приемник и отправить широковещательную передачу.

У вас также должна быть AsyncTask как отдельный общедоступный класс от activity, как упоминалось выше, это намного упрощает тестирование.К сожалению, программирование на Android часто усиливает плохие практики, и официальные примеры не помогают.

Это тот тип вещей, который заставляет меня всегда предотвращать уничтожение / воссоздание моей Активности при смене ориентации.

Для этого добавьте это в свой <Activity> пометьте в вашем файле манифеста:

android:configChanges="orientation|keyboardHidden" 

И переопределите onConfigurationChanged в вашем классе Activity:

@Override
public void onConfigurationChanged(final Configuration newConfig)
{
    // Ignore orientation change to keep activity from restarting
    super.onConfigurationChanged(newConfig);
}

Чтобы избежать этого, вы можете использовать приведенный здесь ответ: https://stackoverflow.com/a/2124731/327011

Но если вам нужно уничтожить activity (разные макеты для книжной и альбомной ориентации), вы можете сделать AsyncTask общедоступным классом (читайте здесь, почему он не должен быть закрытым Android:Рекомендации по асинхронной задаче:частный класс или публичный класс?) а затем создайте метод setActivity, чтобы устанавливать ссылку на текущее действие всякий раз, когда оно уничтожается / создается.

Вы можете увидеть пример здесь: Android AsyncTask во внешнем классе

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top