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