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