Я исследовал эту проблему уже несколько месяцев, придумал разные решения, которые мне не нравятся, поскольку они - все массивные хаки. Я все еще не могу поверить, что класс, который испорчен в дизайне, превратил его в рамки, и никто не говорит об этом, поэтому я думаю, что я просто должен что-то упустить.
Проблема заключается в AsyncTask
. Согласно документации, он
"позволяет выполнять фон операций и опубликовать результаты на Пользовательский интерфейс без необходимости манипулировать потоки и/или обработчики."
Далее пример показывает, как в onPostExecute()
вызывается какой-то примерный метод showDialog()
. Это, однако, кажется мне полностью надуманным, поскольку для отображения диалога всегда нужна ссылка на допустимый Context
, а AsyncTask никогда не должен содержать сильную ссылку на объект контекста.
Причина очевидна: что, если активность уничтожается, что вызвало задачу? Это может происходить все время, например. потому что вы перевернули экран. Если в задаче будет содержаться ссылка на контекст, который ее создал, вы не только держитесь за бесполезный объект контекста (окно будет уничтожено, и любое взаимодействие с пользовательским интерфейсом завершится с исключением!), Вы даже рискуете создать утечка памяти.
Если моя логика здесь некорректна, это означает: onPostExecute()
совершенно бесполезно, потому что для этого метода нужно работать в потоке пользовательского интерфейса, если у вас нет доступа к контексту? Здесь вы не можете ничего значимого.
Обходным решением было бы не передавать экземпляры контекста в AsyncTask, а в экземпляр Handler
. Это работает: поскольку Handler свободно связывает контекст и задачу, вы можете обмениваться сообщениями между ними, не рискуя утечкой (правильно?). Но это будет означать, что предпосылка AsyncTask, а именно то, что вам не нужно беспокоиться с обработчиками, неверна. Это также похоже на злоупотребление обработчиком, поскольку вы отправляете и получаете сообщения в одном потоке (вы создаете его в потоке пользовательского интерфейса и отправляете через него в onPostExecute(), который также выполняется в потоке пользовательского интерфейса).
Чтобы справиться с этим, даже с этим обходным решением, у вас все еще есть проблема, что при разрушении контекста у вас нет записи о запущенных задачах. Это означает, что вам необходимо повторно запустить любые задачи при повторном создании контекста, например. после изменения ориентации экрана. Это медленно и расточительно.
Мое решение для этого (как реализованное в библиотеке Droid-Fu) заключается в том, чтобы поддерживать отображение WeakReference
от имен компонентов до их текущие экземпляры в уникальном объекте приложения. Всякий раз, когда запускается AsyncTask, он записывает вызывающий контекст на этой карте, и при каждом обратном вызове он извлекает текущий экземпляр контекста из этого сопоставления. Это гарантирует, что вы никогда не будете ссылаться на устаревший экземпляр контекста, и у вас всегда есть доступ к допустимому контексту в обратных вызовах, чтобы вы могли делать осмысленный пользовательский интерфейс там. Он также не течет, потому что ссылки слабы и очищаются, когда экземпляр данного компонента больше не существует.
Тем не менее, это сложное обходное решение и требует подкласса некоторых классов библиотеки Droid-Fu, что делает этот процесс довольно интрузивным.
Теперь я просто хочу знать: Я просто просто что-то теряю, или AsyncTask действительно полностью испорчен? Как ваш опыт работает с ним? Как вы решили эту проблему?
Спасибо за ваш вклад.