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