Android loader is started

Loader, LoaderManager

Метки: LoaderManager , Loader , LoaderManager.LoaderCallbacks , AsyncTaskLoader , CursorLoader

В старых версиях Android при работе с курсором использовались методы, которые теперь считаются устаревшими.

  • startManagingCursor(Cursor)
  • managedQuery(Uri, String, String, String, String)

Первый метод работал в жизненном цикле активности. Курсор автоматически деактивировался или закрывался, когда активность останавливалась или закрывалась. При рестарте активности, курсор также активировался, создавая повторный запрос к данным.

Второй метод осуществлял сам запрос. Проблема с методами заключалась в том, что они работали в одном потоке с интерфейсом приложения и выполняли лишние запросы при изменении состояния активности, тормозя всё приложение, если база данных была слишком большая.

Например, загрузим данные в список старым способом, используя контент-провайдер. Результат обрабатывается адаптером SimpleCursorAdapter и выводится на экран.

От этого кода следует отказаться по описанным выше причинам, чтобы не иметь проблем с производительностью.

Начиная с Android 3.0, появился другой механизм работы с курсором — загрузчик Loader и менеджер LoaderManager, которые также доступны в библиотеке совместимости для старых устройств.

Теперь все операции с курсорами происходят в асинхронном режиме. А данные кэшируются и при необходимости информация обновляется, если данные изменились.

LoaderManager

LoaderManager позволяет грамотно управлять загрузчиками, связанными с активностью или фрагментом. Каждая активность и каждый фрагмент имеет один экземпляр менеджера LoaderManager, который работает с загрузчиками через методы initLoader(), restartLoader(), destroyLoader(). Активность через данного менеджера может предупредить о своём уничтожении, чтобы LoaderManager в свою очередь закрыл загрузчики для экономии ресурсов.

Сам LoaderManager не знает, как данные загружаются в приложение. Он просто даёт указания загрузчику начать, остановить, обновить загрузку данных и другие команды.

LoaderManager работает с объектами Loader , где D является контейнером для загружаемых данных. При этом данные не обязательно должны быть курсором. Это могут быть и List, JSONArray и т.д. В одной активности может быть несколько загрузчиков, которые являются объектами.

Класс Loader является общим. Также доступны специализированные загрузчики AsyncTaskLoader и CursorLoader.

Работа с LoaderManager происходит через три метода обратного вызова интерфейса LoaderManager.LoaderCallbacks .

Интерфейс LoaderManager.LoaderCallbacks

Интерфейс LoaderManager.LoaderCallbacks определяет порядок взаимодействия с загрузчиком через методы:

onCreateLoader()

Метод onCreateLoader() возвращает новый загрузчик. LoaderManager вызывает метод при создании Loader.

При попытке доступа к загрузчику (например, посредством метода initLoader()), он проверяет, существует ли загрузчик, указанный с помощью идентификатора. Если он не существует, он вызывает метод onCreateLoader(). Именно здесь и создаётся новый загрузчик.

onLoadFinished()

Метод onLoadFinished вызывается автоматически, когда Loader завершает загрузку данных. Загрузчик следит за поступающими данными, а менеджер получает уведомление о завершении загрузки и передаёт результат данному методу.

Этот метод гарантировано вызывается до высвобождения последних данных, которые были предоставлены этому загрузчику. К этому моменту необходимо полностью перестать использовать старые данные (поскольку они скоро будут заменены). Однако этого не следует делать самостоятельно, поскольку данными владеет загрузчик и он позаботится об этом. Загрузчик высвободит данные, как только узнает, что приложение их больше не использует. Например, если данными является курсор из CursorLoader, не следует вызывать close() самостоятельно. Если курсор размещается в CursorAdapter, следует использовать метод swapCursor() с тем, чтобы старый Cursor не закрылся.

onLoadReset()

Метод onLoadReset() перезагружает данные в загрузчике.

Этот метод вызывается, когда состояние созданного ранее загрузчика сбрасывается, в результате чего его данные теряются. Этот обратный вызов позволяет узнать, когда данные вот-вот будут высвобождены, с тем чтобы можно было удалить свою ссылку на них.

Порядок работы менеджера загрузчиков во время создания активности.

Application.onCreate()
Activity.onCreate()
LoaderManager.LoaderCallbacks.onCreateLoader()
Activity.onStart()
Activity.onResume()
LoaderManager.LoaderCallbacks.onLoadFinished()

При изменении конфигурации (поворот и т.п.):

Application:config changed
Activity.onCreate
Activity.onStart
[No call to the onCreateLoader]
LoaderManager.LoaderCallbacks.onLoadFinished
[optionally if searchview has text in it]
SearchView.onQueryChangeText (Поиск по тексту, см. примеры ниже)
RestartLoader
LoaderManager.LoaderCallbacks.onCreateLoader
LoaderManager.LoaderCallbacks.onLoadFinished

При уничтожении активности:

Activity.onStop()
Activity.onDestroy()
LoaderManager.LoaderCallbacks.onLoaderReset() //Обратите внимание, что этот метод вызывается

Загрузчик (Loader)

Менеджер загрузчиков управляет загрузчиками. Загрузчик предназначен для загрузки данных из источника: диск, база данных, контент-провайдер, сеть или другой процесс. Загрузчик производит выборку данных без блокировки главного потока в отдельном потоке и доставляет результаты стороне, которая в них заинтересована, следя за изменениями данных и информирует о всех важных изменениях менеджеру через специальные слушатели.

Таким образом, активность или фрагмент не интересуются, как загружаются данные. Они доверили эту работу загрузчику.

Существуют три встроенных типа загрузчиков: Loader, AsyncTaskLoader и CursorLoader. Loader — базовый класс, который сам по себе не очень полезен. Он определяет API, используемый LoaderManager для взаимодействия со всеми загрузчиками.

Читайте также:  Передать файл по wi fi для android

AsyncTaskLoader

Для задач асинхронной загрузки данных в отдельном потоке используется класс, наследующий AsyncTaskLoader вместо Loader . Класс AsyncTaskLoader является абстрактным и работает как AsyncTask. На основе этого класса вы можете реализовать абстрактный метод loadInBackground().

Слушатель получает информацию от загрузчика. Для этого менеджер регистрирует слушатель OnLoadCompleteListener , который прослушивает события. Когда загрузка закончилась, то вызывается метод onLoadFinished(Loader loader, D result).

Чтобы загрузчик начал работать, его надо запустить. Запущенный загрузчик следит за данными, пока его не перезапустят или остановят.

Остановленный загрузчик продолжает мониторить изменения в данных, но не сообщает о них. При необходимости вы можете заново запустить или перезапустить остановленный загрузчик.

При перезапуске загрузчик не должен запускать новую загрузку данных и мониторить изменения. Его задача — освободить лишние данные. Это состояние редко используется, но в некоторых случаях оно нужно.

Ваша задача сводится к созданию собственного загрузчика, реализации метода loadInBackground() и переопределении методов onStartLoading(), onStopLoading(), onReset(), onCanceled(), deliverResult(D results).

Небольшой пример для демонстрации.

Создадим кнопку и текстовую метку на экране.

Создадим класс загрузчика, который наследуется от AsyncTaskLoader. Наш загрузчик будет возвращать строку, поэтому укажем :

Переменная mWord будет хранить базовую строку, на основе которой будет создаваться случайная строка. Этот параметр передается при создании загрузчика в конструктор с использованием объекта Bundle. Константа RANDOM_STRING_LENGTH задает максимальную длину новой случайной строки.

Наследуясь от AsyncTaskLoader, мы переопределяем несколько методов:

  • loadInBackground() — метод, в котором собственно и должна быть создана вся работа по загрузке данных
  • onStartLoading() — срабатывает при запуске загрузчика (но это еще не означает загрузку данных)
  • onStopLoading() — срабатывает при остановке загрузчика
  • deliverResult() — получает и возвращает итоговый результат работы загрузчика
  • forceLoad() — «принудительная» загрузка новых данных

Для получения данных в методе loadInBackground() мы вызываем вспомогательный метод generateString(), который генерит случайную строку.

Класс активности реализует интерфейс LoaderManager.LoaderCallbacks с его методами, позволяющий нам «участвовать» в жизненном цикле загрузчика и взаимодействовать с LoaderManager.

Метод onCreateLoader() — вызывается при инициализации загрузчика. Если загрузчик с таким идентификатором уже был создан, то метод не вызовется. Внутри мы определяем, какой идентификатор нам был передан, чтобы создать нужный загрузчик. Если загрузчик только один, то условие можно убрать.

Метод onLoadFinished() вызывается по окончанию загрузки. В метод приходят загруженные данные, а также объект загрузчика. Полученную строку мы выводим в текстовой метке.

Метод onLoaderReset() вызывается при «сбросе» состояния загрузчика. Здесь данные обнуляются, и нам нужно удалить все имеющиеся ссылки на них.

Щелчок кнопки выполняет роль триггера для запуска новой загрузки данных. Мы используем метод onContentChanged(), чтобы сигнализировать загрузчику об изменении данных.

Как происходит первый запуск загрузчика? Мы это делаем в методе onCreate(), инициализируя загрузчик. При инициализации мы передаем в параметрах идентификатор загрузчика, Bundle — объект, содержащий передаваемые аргументы (мы передаем базовую строку) и указатель на callback-объект (в нашем случае — это сама активность).

Ну теперь запускаем приложение и смотрим логи. При нажатии на кнопку текст обновляется, но в логах мы уже не видим повторного создания загрузчика.

CursorLoader

Класс CursorLoader является наследником класса Loader и работает с курсорами в асинхронном режиме. CursorLoader расширяет AsyncTaskLoader для загрузки Cursor из ContentProvider через ContentResolver.

Источник

Loaders

In this document

Key classes

Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:

  • They are available to every Activity and Fragment .
  • They provide asynchronous loading of data.
  • They monitor the source of their data and deliver new results when the content changes.
  • They automatically reconnect to the last loader’s cursor when being recreated after a configuration change. Thus, they don’t need to re-query their data.

Loader API Summary

There are multiple classes and interfaces that may be involved in using loaders in an application. They are summarized in this table:

Class/Interface Description
LoaderManager An abstract class associated with an Activity or Fragment for managing one or more Loader instances. This helps an application manage longer-running operations in conjunction with the Activity or Fragment lifecycle; the most common use of this is with a CursorLoader , however applications are free to write their own loaders for loading other types of data.

There is only one LoaderManager per activity or fragment. But a LoaderManager can have multiple loaders.

LoaderManager.LoaderCallbacks A callback interface for a client to interact with the LoaderManager . For example, you use the onCreateLoader() callback method to create a new loader.
Loader An abstract class that performs asynchronous loading of data. This is the base class for a loader. You would typically use CursorLoader , but you can implement your own subclass. While loaders are active they should monitor the source of their data and deliver new results when the contents change.
AsyncTaskLoader Abstract loader that provides an AsyncTask to do the work.
CursorLoader A subclass of AsyncTaskLoader that queries the ContentResolver and returns a Cursor . This class implements the Loader protocol in a standard way for querying cursors, building on AsyncTaskLoader to perform the cursor query on a background thread so that it does not block the application’s UI. Using this loader is the best way to asynchronously load data from a ContentProvider , instead of performing a managed query through the fragment or activity’s APIs.

The classes and interfaces in the above table are the essential components you’ll use to implement a loader in your application. You won’t need all of them for each loader you create, but you’ll always need a reference to the LoaderManager in order to initialize a loader and an implementation of a Loader class such as CursorLoader . The following sections show you how to use these classes and interfaces in an application.

Using Loaders in an Application

This section describes how to use loaders in an Android application. An application that uses loaders typically includes the following:

  • An Activity or Fragment .
  • An instance of the LoaderManager .
  • A CursorLoader to load data backed by a ContentProvider . Alternatively, you can implement your own subclass of Loader or AsyncTaskLoader to load data from some other source.
  • An implementation for LoaderManager.LoaderCallbacks . This is where you create new loaders and manage your references to existing loaders.
  • A way of displaying the loader’s data, such as a SimpleCursorAdapter .
  • A data source, such as a ContentProvider , when using a CursorLoader .

Starting a Loader

The LoaderManager manages one or more Loader instances within an Activity or Fragment . There is only one LoaderManager per activity or fragment.

You typically initialize a Loader within the activity’s onCreate() method, or within the fragment’s onActivityCreated() method. You do this as follows:

The )»>initLoader() method takes the following parameters:

  • A unique ID that identifies the loader. In this example, the ID is 0.
  • Optional arguments to supply to the loader at construction ( null in this example).
  • A LoaderManager.LoaderCallbacks implementation, which the LoaderManager calls to report loader events. In this example, the local class implements the LoaderManager.LoaderCallbacks interface, so it passes a reference to itself, this .

The )»>initLoader() call ensures that a loader is initialized and active. It has two possible outcomes:

  • If the loader specified by the ID already exists, the last created loader is reused.
  • If the loader specified by the ID does not exist, )»>initLoader() triggers the LoaderManager.LoaderCallbacks method onCreateLoader() . This is where you implement the code to instantiate and return a new loader. For more discussion, see the section onCreateLoader.

In either case, the given LoaderManager.LoaderCallbacks implementation is associated with the loader, and will be called when the loader state changes. If at the point of this call the caller is in its started state, and the requested loader already exists and has generated its data, then the system calls , D)»>onLoadFinished() immediately (during )»>initLoader() ), so you must be prepared for this to happen. See onLoadFinished for more discussion of this callback

Note that the )»>initLoader() method returns the Loader that is created, but you don’t need to capture a reference to it. The LoaderManager manages the life of the loader automatically. The LoaderManager starts and stops loading when necessary, and maintains the state of the loader and its associated content. As this implies, you rarely interact with loaders directly (though for an example of using loader methods to fine-tune a loader’s behavior, see the LoaderThrottle sample). You most commonly use the LoaderManager.LoaderCallbacks methods to intervene in the loading process when particular events occur. For more discussion of this topic, see Using the LoaderManager Callbacks.

Restarting a Loader

When you use )»>initLoader() , as shown above, it uses an existing loader with the specified ID if there is one. If there isn’t, it creates one. But sometimes you want to discard your old data and start over.

To discard your old data, you use )»>restartLoader() . For example, this implementation of SearchView.OnQueryTextListener restarts the loader when the user’s query changes. The loader needs to be restarted so that it can use the revised search filter to do a new query:

Using the LoaderManager Callbacks

LoaderManager.LoaderCallbacks is a callback interface that lets a client interact with the LoaderManager .

Loaders, in particular CursorLoader , are expected to retain their data after being stopped. This allows applications to keep their data across the activity or fragment’s onStop() and onStart() methods, so that when users return to an application, they don’t have to wait for the data to reload. You use the LoaderManager.LoaderCallbacks methods when to know when to create a new loader, and to tell the application when it is time to stop using a loader’s data.

  • onCreateLoader() — Instantiate and return a new Loader for the given ID.
  • , D)»>onLoadFinished() — Called when a previously created loader has finished its load.
  • )»>onLoaderReset() — Called when a previously created loader is being reset, thus making its data unavailable.

These methods are described in more detail in the following sections.

onCreateLoader

When you attempt to access a loader (for example, through )»>initLoader() ), it checks to see whether the loader specified by the ID exists. If it doesn’t, it triggers the LoaderManager.LoaderCallbacks method onCreateLoader() . This is where you create a new loader. Typically this will be a CursorLoader , but you can implement your own Loader subclass.

In this example, the onCreateLoader() callback method creates a CursorLoader . You must build the CursorLoader using its constructor method, which requires the complete set of information needed to perform a query to the ContentProvider . Specifically, it needs:

  • uri — The URI for the content to retrieve.
  • projection — A list of which columns to return. Passing null will return all columns, which is inefficient.
  • selection — A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing null will return all rows for the given URI.
  • selectionArgs — You may include ?s in the selection, which will be replaced by the values from selectionArgs, in the order that they appear in the selection. The values will be bound as Strings.
  • sortOrder — How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing null will use the default sort order, which may be unordered.

onLoadFinished

This method is called when a previously created loader has finished its load. This method is guaranteed to be called prior to the release of the last data that was supplied for this loader. At this point you should remove all use of the old data (since it will be released soon), but should not do your own release of the data since its loader owns it and will take care of that.

The loader will release the data once it knows the application is no longer using it. For example, if the data is a cursor from a CursorLoader , you should not call close() on it yourself. If the cursor is being placed in a CursorAdapter , you should use the swapCursor() method so that the old Cursor is not closed. For example:

onLoaderReset

This method is called when a previously created loader is being reset, thus making its data unavailable. This callback lets you find out when the data is about to be released so you can remove your reference to it.

This implementation calls swapCursor() with a value of null :

Example

As an example, here is the full implementation of a Fragment that displays a ListView containing the results of a query against the contacts content provider. It uses a CursorLoader to manage the query on the provider.

For an application to access a user’s contacts, as shown in this example, its manifest must include the permission READ_CONTACTS .

More Examples

There are a few different samples in ApiDemos that illustrate how to use loaders:

  • LoaderCursor — A complete version of the snippet shown above.
  • LoaderThrottle — An example of how to use throttling to reduce the number of queries a content provider does when its data changes.

For information on downloading and installing the SDK samples, see Getting the Samples.

Источник

Читайте также:  Андроид виснет при включении
Оцените статью