AsyncTask действительно концептуально испорчен, или я просто пропускаю что-то?

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

Проблема с AsyncTask. Согласно документации это

"позволяет выполнять фоновые работы и публиковать результаты на потоке UI, не имея необходимость управлять потоками и/или обработчиками".

Пример затем продолжает показывать как некоторые образцовые showDialog() метод призван onPostExecute(). Это, однако, кажется полностью изобретенным мне, потому что для показа диалогового окна всегда нужна ссылка на допустимое Context, и AsyncTask никогда не должен содержать сильную ссылку к объекту контекста.

Причина очевидна: что, если действие уничтожается, который инициировал задачу? Это может произойти все время, например, потому что Вы зеркально отразили экран. Если задача содержала бы ссылку на контекст, который создал ее, Вы не только держитесь за бесполезный объект контекста (окно будет уничтожено, и любое взаимодействие UI перестанет работать за исключением!), Вы даже рискуете создавать утечку памяти.

Если моя логика не испорчена здесь, это переводит в: onPostExecute() совершенно бесполезно, потому что, что хороший это, чтобы этот метод работал на потоке UI, если у Вас нет доступа ни к какому контексту? Вы не можете сделать ничего значимого здесь.

Одно обходное решение не должно было бы передавать экземпляры контекста AsyncTask, но a Handler экземпляр. Это работает: так как Обработчик свободно ограничивает контекст и задачу, можно ли обмениваться сообщениями между ними, не рискуя утечкой (право?). Но это означало бы, что предпосылка AsyncTask, а именно, что Вы не должны беспокоиться обработчиками, является неправильной. Это также походит на злоупотребление Обработчиком, так как Вы отправляете и получаете сообщения на том же потоке (Вы создаете его на UI, распараллеливают и отправляют через него в onPostExecute (), который также выполняется на потоке UI).

В довершение ко всему прочь, даже с тем обходным решением, у Вас все еще есть проблема, что, когда контекст уничтожается, у Вас нет записи задач, которые это запустило. Это означает, что необходимо перезапустить любые задачи при воссоздании контекста, например, после экранного изменения ориентации. Это медленно и расточительно.

Мое решение этого (как реализовано в библиотеке Droid-Fu) состоит в том, чтобы поддержать отображение WeakReferences от имен компонентов до их текущих экземпляров на уникальном объекте приложения. Каждый раз, когда AsyncTask запускается, он записывает контекст вызова в той карте, и на каждом обратном вызове, он выберет текущий экземпляр контекста от того отображения. Это гарантирует, что Вы никогда не будете ссылаться на устаревший экземпляр контекста, и у Вас всегда есть доступ к допустимому контексту в обратных вызовах, таким образом, можно сделать значимую работу UI там. Это также не протекает, потому что ссылки слабы и очищены, когда никакой экземпляр данного компонента больше не существует.

Однако, это - сложное обходное решение и требует для разделения некоторых на подклассы классов библиотеки Droid-Fu, делая это довольно навязчивым подходом.

Теперь я просто хочу знать: я просто в широком масштабе пропускаю что-то, или AsyncTask действительно полностью испорчен? Как Ваши события работают с ним? Как Вы решали их проблема?

Спасибо за Ваш вход.

260
задан Bobrovsky 11 September 2013 в 19:31
поделиться

4 ответа

Как насчет чего-то вроде этого:

class MyActivity extends Activity {
    Worker mWorker;

    static class Worker extends AsyncTask<URL, Integer, Long> {
        MyActivity mActivity;

        Worker(MyActivity activity) {
            mActivity = activity;
        }

        @Override
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalSize = 0;
            for (int i = 0; i < count; i++) {
                totalSize += Downloader.downloadFile(urls[i]);
                publishProgress((int) ((i / (float) count) * 100));
            }
            return totalSize;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            if (mActivity != null) {
                mActivity.setProgressPercent(progress[0]);
            }
        }

        @Override
        protected void onPostExecute(Long result) {
            if (mActivity != null) {
                mActivity.showDialog("Downloaded " + result + " bytes");
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mWorker = (Worker)getLastNonConfigurationInstance();
        if (mWorker != null) {
            mWorker.mActivity = this;
        }

        ...
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        return mWorker;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mWorker != null) {
            mWorker.mActivity = null;
        }
    }

    void startWork() {
        mWorker = new Worker(this);
        mWorker.execute(...);
    }
}
86
ответ дан 23 November 2019 в 02:37
поделиться

Лично я просто расширяю Thread и использую интерфейс обратного вызова для обновления пользовательского интерфейса. Я никогда не смог бы заставить AsyncTask работать правильно без проблем с FC. Я также использую неблокирующую очередь для управления пулом выполнения.

0
ответ дан 23 November 2019 в 02:37
поделиться

Причина очевидна: что, если деятельность уничтожается, что инициировал задачу?

Вручную отключите действие от AsyncTask в onDestroy () . Вручную повторно свяжите новое действие с AsyncTask в onCreate () . Для этого требуется либо статический внутренний класс, либо стандартный класс Java, а также, возможно, 10 строк кода.

20
ответ дан 23 November 2019 в 02:37
поделиться

Я не уверен, что это правда, что вы рискуете утечкой памяти при ссылке на контекст из AsyncTask.

Обычный способ их реализации заключается в создании нового экземпляра AsyncTask в области видимости одного из методов активности. Итак, если активность будет уничтожена, то после завершения AsyncTask не станет ли она недоступной и не попадет ли она в сборку мусора? Таким образом, ссылка на активность не будет иметь значения, потому что сама AsyncTask не будет висеть на месте.

5
ответ дан 23 November 2019 в 02:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: