Ответ 1
Обновление 2
React Native
Если вы можете в желудке, используйте React Native. Я знаю, я знаю... "грязные веб-технологии", но со всей серьезностью Android SDK - это катастрофа, поэтому проглотите свою гордость и просто отпустите ее. Вы можете удивить себя; Я знаю, что сделал!
Нельзя или не использовать React Native
Не беспокойтесь, я бы предложил принципиально изменить подход к сетевому взаимодействию. Увольнение запроса и запуск обработчика запросов для обновления пользовательского интерфейса просто не работает с жизненными циклами компонентов Android.
Вместо этого попробуйте один из:
- Перейдите к простой системе передачи сообщений, основанной на
LocalBroadcastReceiver
и имеющей долгоживущие объекты (обычные Java-классы или службы Android), выполняйте ваши запросы и запускайте события, когда изменяется локальное состояние вашего приложения. Затем в вашем Activity/Fragment просто прослушайте определенныеIntent
и соответствующим образом обновите. - Используйте библиотеку активных событий (например, RxJava). Я не пробовал это самостоятельно на Android, но имел довольно хороший успех, используя аналогичную концептуальную библиотеку, ReactiveCocoa для Mac/настольного приложения. По общему признанию, эти библиотеки имеют довольно крутую кривую обучения, но подход становится достаточно освежающим, как только вы привыкнете к нему.
Обновление 1: быстрое и грязное (официальное) решение
Я считаю, что это последнее официальное решение от Google. Однако решение действительно не очень хорошо масштабируется. Если вам не комфортно возиться с очередями, обработчики и сохраненный экземпляр заявляют о себе, это может быть вашим единственным вариантом... но не говорите, что я вас не предупреждал!
Действия и фрагменты Android поддерживают LoaderManager, которые можно использовать с AsyncTaskLoader. За кулисами менеджеров загрузчиков сохраняются точно так же, как и оставшиеся фрагменты. Таким образом, это решение действительно имеет немного общего с моим собственным решением ниже. AsyncTaskLoader - это частично готовое решение, которое технически работает. Однако API чрезвычайно громоздкий; как я уверен, вы заметите в течение нескольких минут после его использования.
Мое решение
Во-первых, мое решение отнюдь не просто реализовать. Однако, как только вы приступите к выполнению своей работы, вам понадобится легкий ветерок, и вы можете настроить его на свой сердечный контент.
Я использую сохраненный фрагмент, который добавляется в диспетчер фрагмента активности (или в моем случае - менеджер фрагмента поддержки). Это тот же самый метод, который упоминается в моем вопросе. Этот фрагмент действует как поставщик сорта, который отслеживает, к какой активности он подключен, и имеет очередь сообщений и Runnable (фактически пользовательский подкласс). Очереди выполняются, когда состояние экземпляра больше не сохраняется, и соответствующий обработчик (или исполняемый файл) "готов к выполнению".
Каждый обработчик /runnable хранит UUID, который ссылается на пользователя. Потребители обычно являются фрагментами (которые могут быть надежно закреплены) где-то в пределах активности. Когда пользовательский фрагмент привязан к активности, он ищет фрагмент провайдера и регистрирует себя, используя свой UUID.
Важно, чтобы вы использовали какую-то абстракцию, например UUID, вместо ссылки на потребителей (т.е. фрагменты) напрямую. Это связано с тем, что фрагменты уничтожаются и часто воссоздаются, и вы хотите, чтобы ваши обратные вызовы имели "ссылку" на новые фрагменты; а не старые, принадлежащие к разрушенной деятельности. Как таковые, к сожалению, вы редко можете безопасно использовать переменные, захваченные анонимными классами. Опять же, это связано с тем, что эти переменные могут ссылаться на старый уничтоженный фрагмент или активность. Вместо этого вы должны спросить поставщика для потребителя, который соответствует UUID, который хранил обработчик. Затем вы можете передать этого потребителя любому фрагменту/объекту, который он на самом деле, и использовать его безопасно, так как вы знаете его последний фрагмент с действующим Контекстом (активностью).
Обработчик (или runnable) будет "готов к выполнению", когда потребитель (упомянутый UUID) готов. Необходимо проверить, готов ли потребитель в дополнение к провайдеру, потому что, как упоминалось в моем вопросе, фрагмент потребителя может полагать, что его состояние экземпляра сохраняется, даже если провайдер говорит иначе. Если потребитель (или поставщик) не готов, вы помещаете сообщение (или выполняемое) в очередь в провайдере.
Когда потребительский фрагмент достигает значения onResume(), он информирует провайдера о том, что он готов потреблять поставленные в очередь сообщения /runnables. В этот момент поставщик может попытаться выполнить что-либо в своих очередях, принадлежащих потребителю, который только что был готов.
Это приводит к тому, что обработчики всегда выполняются с использованием действительного контекста (активность, на которую ссылается поставщик) и последний действительный фрагмент (он же "потребитель" ).
Заключение
Решение довольно запутанное, однако оно работает безупречно, как только вы выработаете, как его реализовать. Если кто-то придумает более простое решение, я был бы рад его услышать.