protected void oncreate bundle savedinstancestate что это
Настройки и состояние приложения
Сохранение состояния приложения
Чтобы точнее понять проблему, с которой мы можем столкнуться, рассмотрим пример. Изменим файл activity_main следующим образом:
Здесь определено поле EditText, в которое вводим имя. И также определена кнопка для его сохранения.
Теперь изменим класс MainActivity :
Запустим приложение введем какое-нибудь имя, сохраним и получим его в TextView:
Но если мы перейдем к альбомному режиму, то TextView окажется пустым, несмотря на то, что в него вроде бы уже получили нужное значение:
И даже если мы попробуем заново получить значение из переменной name, то мы увидим, что она обнулилась:
Чтобы избежать подобных ситуаций как раз и следует сохранять и восстанавливать состояние activity. Для этого изменим код MainActivity:
put() : универсальный метод, который добавляет значение типа Object. Соответственно поле получения данное значение необходимо преобразовать к нужному типу
putString() : добавляет объект типа String
putInt() : добавляет значение типа int
putByte() : добавляет значение типа byte
putChar() : добавляет значение типа char
putShort() : добавляет значение типа short
putLong() : добавляет значение типа long
putFloat() : добавляет значение типа float
putDouble() : добавляет значение типа double
putBoolean() : добавляет значение типа boolean
putCharArray() : добавляет массив объектов char
putIntArray() : добавляет массив объектов int
putFloatArray() : добавляет массив объектов float
putSerializable() : добавляет объект интерфейса Serializable
putParcelable() : добавляет объект Parcelable
get() : универсальный метод, который возвращает значение типа Object. Соответственно поле получения данное значение необходимо преобразовать к нужному типу
getString() : возвращает объект типа String
getInt() : возвращает значение типа int
getByte() : возвращает значение типа byte
getChar() : возвращает значение типа char
getShort() : возвращает значение типа short
getLong() : возвращает значение типа long
getFloat() : возвращает значение типа float
getDouble() : возвращает значение типа double
getBoolean() : возвращает значение типа boolean
getCharArray() : возвращает массив объектов char
getIntArray() : возвращает массив объектов int
getFloatArray() : возвращает массив объектов float
getSerializable() : возвращает объект интерфейса Serializable
getParcelable() : возвращает объект Parcelable
Для примера рассмотрим сохранение-получение более сложных данных. Например, объектов определенного класса. Пусть у нас есть класс User :
Пусть у нас будет следующий интерфейс в activity_main.xml :
Здесь определены два поля ввода для имени и возраста соответственно.
В классе MainActivity пропишем логику сохранения и получения данных:
Здесь также сохраняем данные в переменную User, которая предварительно инициализированна некоторыми данными по умолчанию. А при нажатии на кнопку получения получем данные из переменной и передаем их для вывода в текстовое поле.
Полный список
— сохраняем данные при повороте экрана
Теорию по этому вопросу можно почитать тут. Я здесь вкратце дам вольный перевод.
Эти методы используются в случаях, когда Activity уничтожается, но есть вероятность, что оно еще будет востребовано в своем текущем состоянии. Т.е. при нехватке памяти или при повороте экрана. Если же вы просто нажали кнопку Back (назад) и тем самым явно сами закрыли Activity, то эти методы не будут выполнены.
Но даже если не реализовать эти методы, у них есть реализация по умолчанию, которая сохранит и восстановит данные в экранных компонентах. Это выполняется для всех экранных компонентов, у которых есть ID.
Создадим простое приложение, чтобы протестить все эти тезисы. Посмотрим, в какой момент вызываются эти методы, попробуем в них что-нить сохранить. Также убедимся, что необходимо вызывать соответствующие методы супер-класса, чтобы сохранялись данные экранных компонентов.
Т.к. нам надо будет поворачивать экран, используйте при разработке Android 2.2. В AVD с версией 2.3 поворот глючит.
Project name: P0701_SaveInstanceState
Build Target: Android 2.2
Application name: SaveInstanceState
Package name: ru.startandroid.develop.p0701saveinstancestate
Create Activity: MainActivity
В strings.xml пропишем тексты:
В main.xml нарисуем кнопку и пару полей для ввода текста:
Обратите внимание, что второй EditText без ID.
В MainActivity будем вызывать все методы Lifecycle и два вышеописанных:
В каждом из них пишем лог, чтобы отследить последовательность вызовов. Метод onclick пока не реализуем.
Все сохраним и запустим. Введем в текстовые поля какие-нить данные:
и повернем экран CTRL+F12.
Данные в первом поле сохранились при повороте, а во втором пропали. Это произошло потому, что дефолтовые методы сохранения/восстановления умеют работать только с компонентами, которые имеют ID. Посмотрим лог.
Эти три метода выполнились при запуске.
Затем мы повернули экран:
onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
Первым делом вызывается onSaveInstanceState, здесь нам надо будет реализовывать сохранение своих данных. Далее идет уничтожение Activity (onPause, onStop, onDestroy) и создание нового onCreate, onStart. И перед onResume вызывается метод восстановления данных – onRestoreInstanceState.
Повернем эмулятор обратно в вертикальную ориентацию. Запустим приложение, и жмем на кнопку Count. Видим сообщение с кол-вом нажатий. Нажмем еще несколько раз, получим, например 5.
Теперь повернем экран и снова нажмем кнопку.
Мы видим, что счетчик сбросился.
Это произошло потому, что текущий объект Activity был уничтожен и потерял значения всех переменных, в том числе и cnt. При создании нового Activity значение cnt равно 0 и отсчет пошел заново. Давайте это пофиксим. Реализуем метод сохранения onSaveInstanceState:
В объект outState мы пишем значение переменной cnt. Механизм аналогичен помещению данных в Intent.
Метод восстановления onRestoreInstanceState:
Из savedInstanceState вытаскиваем значение и помещаем в переменную cnt. Теперь при уничтожении и воссоздании Activity переменная cnt сохранит свое значение, и наш счетчик продолжит работать.
Проверим. Вернем AVD в вертикальную ориентацию. Все сохраним, запустим приложение. Понажимаем на кнопку, немного накрутим счетчик
и поворачиваем экран.
счетчик не сбросился, а продолжил увеличиваться с последней позиции.
Итак, методы onSaveInstanceState и onRestoreInstanceState по дефолту сохраняют данные в экранных компонентах. Если мы реализуем их самостоятельно, то вызываем методы супер-класса и пишем свой код для своих переменных. Ради интереса, можете попробовать убрать вызовы методов суперкласса из onSaveInstanceState и onRestoreInstanceState. Данные в текстовом поле перестанут сохраняться при повороте экрана.
Кроме метода onRestoreInstanceState, доступ к сохраненным данным также можно получить в методе onCreate. На вход ему подается тот же самый Bundle. Если восстанавливать ничего не нужно, он будет = null.
Есть еще один полезный механизм сохранения данных. Android дает нам возможность сохранить ссылку на какой-либо объект и вернуть ее в новый созданный Activity. Для этого существуют методы:
onRetainNonConfigurationInstance – в нем мы сохраняем ссылку, передавая ее на выход (return) метода
Т.е., например, у нас есть какой то объект myObj (класс MyObject) и нам надо сохранить ссылку на него при повороте экрана.
Мы реализуем в Activity метод onRetainNonConfigurationInstance:
Этот метод будет вызван перед уничтожением Activity. От нас требуется дать на выход этому методу наш объект, который надо сохранить.
А, при создании нового Activity, в onCreate (например) мы используем метод getLastNonConfigurationInstance:
Мы получили обратно объект класса Object и привели его к нашему классу MyObject.
На следующем уроке:
— используем Preferences для работы с настройками приложения
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Fragment (Фрагменты). Часть пятая
Сохранение данных
Теперь рассмотрим важный вопрос, как сохранять данные при изменении конфигурации и других операциях.
Кажется, я поторопился со своим утверждением.
Новый проект создавать не будем, а модифицируем старый. Заодно закрепим материал.
Повторяем прошлые шаги. Создаём новый класс, наследуемся от Fragment.
Создадим две разметки для них. Для удобства будем использовать фон, чтобы различать фрагменты на экране.
Подключаем разметки к соответствующим фрагментам в методе onCreateView().
Для второго фрагмента напишите код самостоятельно.
Подключаем фрагменты в разметке основной активности activity_main.xml:
Не копируйте код с сайта, проделывайте операции самостоятельно через редактор в режиме Design и Text, как это объяснялось в предыдущих примерах.
Подключим кнопку в первом фрагменте.
Во втором фрагменте подключим компонент TextView и добавим метод для вывода текста.
Если сейчас запустить пример, то фрагменты будут работать независимо друг от друга. Счётчик будет увеличиваться, но его значения пока не попадают во второй фрагмент и мы не можем увидеть число котов.
Напоминаю, мы не связываем два фрагмента между собой напрямую. Создаём интерфейс для этих целей.
В предыдущем примере мы создавали интерфейс внутри первого фрагмента. Для разнообразия изменим подход. Создадим новый класс через New | Java Class и в диалоговом окне для поля Kind выберем Interface. Также зададим ему имя Communicator.
Интерфейс будет состоять из одного метода.
Активность должна реализовать интерфейс.
Фрагмент с кнопкой может использовать объект интерфейса для отправки данных. Инициализируем его в новом методе onActivityCreated(), который ранее не использовали. Метод сработает, когда активность будет создана и готова к работе.
При щелчках на кнопках мы вызываем метод count(), которому передаём нужную информацию.
А сам метод в активности будет выглядеть следующим образом:
Фрагмент передаёт через метод count() данные data, а активность их принимает и передаёт их в метод второго фрагмента changeText().
Подготовительные работы завершены и можно запустить пример для проверки.
Если вы создавали пример на основе предыдущих уроков, то не поворачивайте экран. Сначала удалите (или переименуйте) файлы для альбомной ориентации, чтобы не получить ошибку и крах приложения.
Мы можем в новой ориентации начать новый подсчёт, но повернув устройство обратно в портретный режим, снова потеряем данные.
Мы знаем, что при поворотах активность создаётся заново. Поэтому все данные сбрасываются. Чтобы сохранить данные, у фрагментов есть соответствующие методы, схожие с подобными методами у активностей. Задействуем их.
Метод onSaveInstanceState() поможет нам. Добавим метод в первый фрагмент с кнопкой.
Параметр метода outState является объектом класса Bundle и позволяет хранить различные типы в формате «ключ-значение».
У фрагментов также есть метод onCreate(Bundle savedInstanceState), где используется объект этого же класса Bundle только под другим именем savedInstanceState. Несмотря на разные имена, речь идёт об одном и том же объекте. И сохраняя данные в методе onSaveInstanceState(), мы можем их получить в методе onCreate():
При повороте фрагмент сохранит значение счётчика, перезапустится и восстановит значение счётчика. Если запустить пример, то увидим следующее. Щёлкнем несколько раз по кнопке и повернём экран. Данные сбросятся и мы снова увидим пустой текст. Но стоит нам нажать на кнопку, то увидим, что отсчёт пошёл не сначала, а продолжил со своего последнего значения. Мы видим, что первый фрагмент запоминает свои данные. А второй фрагмент пока тупит. Поможем ему.
Добавим новую переменную mData, которая будет хранить текст сообщения. У метода onCreateView() также есть параметр savedInstanceState класса Bundle, позволяющий извлечь сохранённые данные.
А текст мы сохраним в методе onSaveInstanceState(). В методе changeText() добавим строку кода, чтобы текст брался из новой переменной. Теперь при первом запуске всё работает как прежде. При повороте текст из mData сохраняется в методе putString() и восстанавливается через getString().
После этих изменений программа больше не теряет своих данных и можно спокойно считать котов. Я стал смотреть на гифку и считать. Через 8 минут 16 секунд последний кот выпрыгнул в окно и в итоге получилось 154578 котов. Перепроверьте.
Activity
Activity и жизненный цикл приложения
Жизненный цикл приложения
Все приложения Android имеют строго определенный системой жизненный цикл. При запуске пользователем приложения система дает этому приложению высокий приоритет. Каждое приложение запускается в виде отдельного процесса, что позволяет системе давать одним процессам более высокой приоритет, в отличие от других. Благодаря этому, например, при работе с одними приложениями Android позволяет не блокировать входящие звонки. После прекращения работы с приложением, система освобождает все связанные ресурсы и переводит приложение в разряд низкоприоритетного и закрывает его.
После запуска activity проходит через ряд событий, которые обрабатываются системой и для обработки которых существует ряд обратных вызовов:
Схематично взаимосвязь между всеми этими обратными вызовами можно представить следующим образом
onCreate()
onStart
onResume
При вызове метода onResume activity переходит в состояние Resumed и отображается на экране устройства, и пользователь может с ней взаимодействовать. И собственно activity остается в этом состоянии, пока она не потеряет фокус, например, вследствии переключения на другую activity или просто из-за выключения экрана устройства.
onPause
onStop
В этом методе activity переходит в состояние Stopped. В этом состоянии activity полностью невидима. В методе onStop следует особождать используемые ресурсы, которые не нужны пользователю, когда он не взаимодействует с activity. Здесь также можно сохранять данные, например, в базу данных.
При этом во время состояния Stopped activity остается в памяти устройства, сохраняется состояние всех элементов интерфейса. К примеру, если в текстовое поле EditText был введен какой-то текст, то после возобновления работы activity и перехода ее в состояние Resumed мы вновь увидим в текстовом поле ранее введенный текст.
onDestroy
В целом переход между состояниями activity можно выразить следующей схемой:
Управление жизненным циклом
Мы можем управлять этими событиями жизненного цикла, переопределив соответствующие методы. Для этого возьмем из прошлой главы класс MainActivity и изменим его следующим образом:
И при запуске приложения мы сможем увидеть в окне Logcat отладочную информацию, которая определяется в методах жизненного цикла activity:
Лекция 1. Введение в архитектуру клиент-серверных андроид-приложений. Часть 2
Продолжение Лекции 1 Курса по архитектуре андроид-приложений. Часть 1 здесь.
Обработка смены конфигурации
Общеизвестно, что Activity пересоздается при каждом изменений конфигурации (например, при смене ориентации или языка). Пересоздание означает уничтожение Activity и запуск ее заново. Уничтожение в свою очередь подразумевает то, что все поля, которые вы хранили в Activity, будут уничтожены. Что это означает на практике? Это означает то, что, если вы при создании Activity получаете информацию с сервера, сохраняете ее в какое-то поле в Activity и отображаете пользователю, то при пересоздании Activity вы потеряете всю информацию, запрос начнет выполняться заново со всеми возможными последствиями:
Так как же с этим бороться? И почему вообще возникла такая проблема при таком невинном действии?
Ответить на вопрос о том, почему такая проблема возникла, достаточно сложно. Очевидно, что было необходимо уничтожать все данные при изменении конфигурации, чтобы не показать пользователю некорректное состояние. Но наверняка был путь, который позволил бы уменьшить количество таких проблем, но, вероятно, он был слишком непростым для первых версий Android, а сейчас необходимо поддерживать обратную совместимость.[wpanchor >
Раз сделать с такой ситуацией ничего нельзя, то приходится привыкать и к таким условиям. В самом деле, есть немало способов, как корректно сохранить и восстановить данные при пересоздании Activity. Рассмотрим их.
Запрет смены ориентации
Разумеется, наиболее частой причиной пересоздания Activity по причине изменения конфигурации является смена ориентации. Поэтому достаточно многие разработчики, не желая иметь дела со всеми проблемами, связанными с обработкой жизненного цикла, жестко фиксируют ориентацию и дальше не думают об этой проблеме. Такая фиксация достигается за счет добавления флага в манифесте:
[wpanchor такой подход многое упрощает, но все же он не всегда приемлем. В принципе, существует немало приложений, которым достаточно только портретной ориентации, но это скорее исключение, чем правило. Часто пользователи работают в альбомной ориентации (особенно на планшетах) и заставлять их менять ее ради вашего приложения не очень хорошо. И ко всему прочему нужно понимать, что фиксированная ориентация не избавляет вас от проблем с пересозданием Activity, так как для этого есть и другие причины, а не только смена ориентации. Поэтому такое решение не может считаться идеальным.
Самостоятельная обработка
Кроме того, можно не запретить изменение какой-то конфигурации (например, ориентации), а обрабатывать его самому. Для этого нужно указать флаг в манифесте с соответствующим значением:
[wpanchor каких-то случаях эта обработка не требуется. Но нужно понимать, что такая обработка также чревата последствиями, так как система не применяет альтернативные ресурсы автоматически (а это уже относится не только к языковым ресурсам, но и к привычным layout-land, к примеру). Поэтому это достаточно редкий вариант, но его нужно также иметь в виду.
Сохранение состояния в Bundle
Android предоставляет нам способ сохранения состояния с последующим его восстановлением при пересоздании Activity. Здесь стоит обратить внимание на параметр savedInstanceState, который передается в методе onCreate в Activity. Этот экземпляр класса Bundle передается не просто так, он может хранить в себе различные поля, которые в него запишут. При первом запуске Activity этот параметр всегда будет равен null. При пересоздании Activity он уже не будет равен null, поэтому можно отследить, происходит ли первый запуск Activity или же это вызов после пересоздания, что весьма удобно. И теперь главное – вы можете сохранить в Bundle свои поля в методе onSaveInstanceState в классе Activity примерно следующим образом:
И этот объект класса Bundle, в который вы сохранили какие-то значения, после пересоздания попадет в качестве параметра в метод onCreate, и уже оттуда вы сможете извлечь все данные. При таком подходе код для обработки смены состояния экрана выглядит следующим образом:
То есть мы проверяем, если Activity запускается в первый раз (словосочетание “в первый раз” здесь не очень подходит, поскольку Activity может запускаться несколько раз, но здесь понимается запуск не после пересоздания), то мы начинаем загружать информацию о погоде. Если же Activity пересоздается, то мы сохраняем информацию о погоде в методе onSaveInstanceState, а восстанавливаем в методе onCreate.
Тут нужно заметить важный факт – не всегда погода будет загружена до того, как Activity будет пересоздана. Поэтому код выше надо слегка модифицировать:
[wpanchor что этот способ не настолько удобен, но он хорошо работает, когда вы должны сохранить небольшие данные на каком-то одном экране. Но при этом нужно учитывать, что таким образом нельзя сохранять большие объекты или огромные объемы данных, так как их сериализация и восстановление занимает много времени, и из-за этого приложение будет работать медленно.
Retain Fragments
Еще одним очень популярным и очень эффективным способом обработки смены конфигурации являются Retain Fragments. По сути это обычные фрагменты, для которых был вызван метод setRetainInstance:
Вызов этого метода меняет жизненный цикл фрагмента, а именно, он убирает из него вызовы onCreate и onDestroy при пересоздании Activity. Теперь при пересоздании Activity этот фрагмент не будет уничтожен, и все его поля сохранят свои значения. Но при этом остальные методы из жизненного цикла Fragment будут вызваны, так что не возникнет проблем с заменой ресурсов в зависимости от конфигурации. Поэтому нам только нужно добавить этот фрагмент при первом старте Activity и выполнять все запросы в нем, так как ему безразличны пересоздания Activity:
Во фрагменте в методе onViewCreated мы проверяем, если данные уже загрузились, то отображаем их, иначе начинаем загрузку данных и показываем процесс загрузки:
Кажется, что такой подход идеален. И в принципе, да, у него есть серьезные достоинства, по сравнению с предыдущими подходами:
Но при этом нужно понимать и ограничения такого подхода:
Поэтому и работа с retain фрагментами требует аккуратности и имеет свои минусы.
Лоадеры
И последним компонентом, который мы рассмотрим, будут лоадеры. Несмотря на принципиальные различия в сути, с точки зрения проблемы обработки смены конфигурации, этот компонент очень похож на предыдущий: он точно также без потери данных переживает пересоздание Activity, он точно также управляется специальным классом (LoaderManager), как и фрагменты (FragmentManager).
Лоадеры будут использоваться и дальше в ходе курса, поэтому сейчас мы остановимся на них чуть подробнее.
Даже если смотреть по названию, лоадеры должны быть предназначены для загрузки чего-либо. А обычно мы загружаем данные – из базы или с сервера. Поэтому решение использовать лоадеры для задачи обеспечения клиент-серверного взаимодействия выглядит логично. Так что же такое лоадеры и как их использовать?
Лоадер – это компонент Android, который через класс LoaderManager связан с жизненным циклом Activity и Fragment. Это позволяет использовать их без опасения, что данные будут утрачены при закрытии приложения или результат вернется не в тот коллбэк. Разберем простейший пример (который хоть и простейший, но требует немало кода, это один из недостатков лоадеров). Создаем класс лоадера (для простоты он не будет грузить данные с сервера, а лишь имитировать загрузку):
Класс лоадера очень похож на класс AsyncTask-а (впрочем, не зря же мы наследуемся от AsyncTaskLoader). Понятно, что в методе loadInBackground мы должны загрузить данные, а вот для чего нужен метод onStartLoading (и другие методы) мы разберем позже. А пока перейдем к использованию. В отличие от AsyncTask-а лоадер не нужно запускать вручную, это делается неявным образом через класс LoaderManager. У этого класса есть два метода с одинаковой сигнатурой:
В методе onCreateLoader вы должны вернуть нужный лоадер в зависимости от переданного id и используя аргументы в Bundle. В методе onLoadFinished в параметре D вам придет результат работы лоадера. В методе onLoaderReset вы должны очистить все данные, которые связаны с этим лоадером.
Тогда давайте создадим экземпляр LoaderCallbacks для нашего лоадера:
Через 2 секунды после запуска Activity покажется тоаст. В принципе, ничего другого мы и не ждали. Но теперь главное – повернем устройство. Теперь по логике работа лоадера должна начаться заново и через 2 секунды покажется тоаст. Однако при этом мы видим, что тоаст показался мгновенно!
Вся магия заключается в методе initLoader и в классе LoaderManager. И теперь настала пора объяснить, как эта магия работает. Если лоадер еще не был создан (он не хранится в LoaderManager), то метод initLoader создает его и начинает его работу. Однако, если лоадер уже был создан (при первом запуске активити), то при повторном вызове initLoader, LoaderManager не пересоздаст лоадер и не будет его перезапускать. Вместо этого он заменит экземпляр LoaderCallbacks на новый переданный (что замечательно, ведь старый экземпляр был уничтожен вместе со старой Activity) и, если данные уже загрузились, передаст их в onLoadFInished. И при всем этом нам не нужно заниматься ручной проверкой на null для Bundle в onCreate. Если у нас на экране есть один запрос, то мы вполне можем при старте вызывать initLoader и не беспокоиться о пересоздании, что дает хороший уровень абстракции при обработке смены конфигурации.
Логичным будет вопрос о том, а что если мы все же хотим перезапустить выполнение лоадера (например, мы хотим обновить данные, а не получить предыдущий результат)? Для этого есть метод restartLoader, который всегда перезапускает выполнение лоадера. В остальном его работа абсолютно аналогична вызову initLoader.
Давайте теперь посмотрим, как это можно применить для решения нашей реальной задачи и реального запроса. Создадим лоадер, который будет загружать информацию о погоде:
И вызовем его в Activity следующим образом:
Обратите внимание на передаваемый флаг, который определяет, какой метод вызывать, initLoader или restartLoader. Даже на таком простом экране нам требуется вызов restartLoader, например, для обработки ошибки, когда пользователь хочет выполнить запрос повторно. Аналогичная ситуация возникает и при обновлении данных (например, Callback от SwipeRefreshLayout).
Нужно опять заметить важный плюс от использования лоадеров – мы нигде специально не пишем код для обработки пересоздания, что очень удобно.
Как уже говорилось выше, также очень важно понимать внутреннее устройство класса Loader. Если раньше особо не было другого пути, как изучать лоадеры, то сейчас ситуация изменилась. Сегодня технологии разработки под Android выросли очень серьезно, появилось множество библиотек для обеспечения асинхронности и работы с сетью (именно поэтому типичный путь разработчика сейчас выглядит следующим образом: Thread, AsyncTask, RxJava, минуя лоадеры). Но знать такой мощный компонент и уметь его использовать – это необходимо. Поэтому мы разберем и то, как лоадер устроен внутри.
Пока что мы наследовались всегда от класса AsyncTaskLoader, который обеспечивал работу в фоне. Но все же изначальным классом является именно класс Loader. При этом примечательно, что класс Loader не предоставляет никаких средств для обеспечения работы в фоне. И это не просто так. Лоадер в первую очередь предназначен для того, чтобы быть связанным с жизненным циклом Activity / Fragment. Для обеспечения же работы в фоне нужно либо использовать класс AsyncTaskLoader, либо использовать другие средства обеспечения многопоточности (например, Call из Retrofit, RxJava). И за такое решение нужно сказать большое спасибо разработчикам из Google. Ведь они позволили нам использовать свои средства для обеспечения многопоточности (иначе у нас, к примеру, не было бы возможности использовать RxJava в связке с лоадерами, чем мы займемся далее), при этом сохранив мощь лоадеров.
В классе Loader определено 3 основных метода, которые нужно переопределить для корректного написания своего лоадера. Это следующие методы: