Каковы преимущества CursorLoaders? - PullRequest
25 голосов
/ 25 августа 2011

Я широко использую Cursors в своем приложении, чтобы загружать и иногда записывать информацию из базы данных и в нее. Я видел, что у Honeycomb и пакета Compatibility Package есть новые классы Loader, разработанные, чтобы помочь с «хорошей» загрузкой данных.

По существу, эти новые классы (в частности, CursorLoader) значительно лучше, чем предыдущие методы управления данными? В чем преимущество, например, CursorLoader по сравнению с Cursors?

И я использую ContentProvider для работы с данными, что, очевидно, занимает Uris, но как это работает с методом initLoader()? Должен ли я настроить каждый из моих Fragments на индивидуальное использование загрузчиков? И насколько уникальным должен быть идентификатор для каждого загрузчика, он выходит за рамки моего приложения или просто фрагмент? Есть ли простой способ просто передать Uri в CursorLoader для запроса моих данных?

Все, что я сейчас вижу, это то, что Загрузчики добавляют ненужный дополнительный шаг для добавления моих данных в мое приложение, так что может кто-нибудь объяснить мне их лучше?

Ответы [ 2 ]

44 голосов
/ 25 августа 2011

Существует два ключевых преимущества использования CursorLoader в вашем приложении над Activity.managedQuery():

  1. Запрос обрабатывается для вас в фоновом потоке (из-за того, что он построен на AsyncTaskLoader), поэтому запросы больших данных не блокируют пользовательский интерфейс. Это то, что рекомендовано в документах для себя при использовании простого Cursor, но теперь это делается под капотом.
  2. CursorLoader - автообновление. В дополнение к выполнению начального запроса, CursorLoader регистрирует ContentObserver с запрашиваемым набором данных и вызывает forceLoad() для себя, когда набор данных изменяется. Это приводит к тому, что вы получаете асинхронные обратные вызовы в любое время, когда данные изменяются для обновления представления.

Каждый экземпляр Loader также обрабатывается через единственное число LoaderManager, поэтому вам по-прежнему не нужно управлять курсором напрямую, и теперь соединение может сохраняться даже после одного Activity. LoaderManager.initLoader() и LoaderManager.restartLoader() позволяют повторно подключиться к существующему Loader, уже настроенному для вашего запроса, и, в некоторых случаях, мгновенно получать последние данные, если они доступны.

Ваш Activity или Fragment, вероятно, теперь будет реализовывать интерфейс LoaderManager.Callback. Вызов initLoader() приведет к методу onCreateLoader(), в котором вы создадите запрос и новый экземпляр CursorLoader, если это необходимо. Метод onLoadFinished() будет запускаться каждый раз, когда доступны новые данные, и будет включать в себя самую последнюю версию Cursor, которую вы можете присоединить к представлению или иным образом выполнить итерацию.

Кроме того, на странице документации класса LoaderManager есть довольно хороший пример того, как все это подходит: http://developer.android.com/reference/android/app/LoaderManager.html

Надеюсь, что поможет!

10 голосов
/ 20 сентября 2011

Если кто-то окажется в подобной ситуации, вот что я сделал:

  • Создал класс, который реализует LoaderCallbacks и обрабатывает все запросов, которые вы будете выполнятьneed.
  • Укажите для этого Context и Adapter для вопроса.
  • Создайте уникальные идентификаторы для каждого запроса, который вы будете использовать (если вы используете UriMatcher, может быть какхорошо используйте те же самые)
  • Создайте удобный метод, который передает запросы в пакет, требуемый для LoaderCallbacks
  • Вот и все :) Я поместил часть своего кода ниже, чтобы показать точночто я сделал

В моем GlobalCallbacks классе:

public static final String PROJECTION = "projection";
public static final String SELECTION = "select";
public static final String SELECTARGS = "sargs";
public static final String SORT = "sort";

Context mContext;
SimpleCursorAdapter mAdapter;

public GlobalCallbacks(Context context, SimpleCursorAdapter adapter) {
    mContext = context;
    mAdapter = adapter;
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    Uri contentUri = AbsProvider.customIntMatch(id);
    if (contentUri != null) {
        return new CursorLoader(mContext, contentUri, args.getStringArray(PROJECTION), args.getString(SELECTION), 
                args.getStringArray(SELECTARGS), args.getString(SORT));
    } else return null;

}

@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
    mAdapter.swapCursor(arg1);      
}

@Override
public void onLoaderReset(Loader<Cursor> arg0) {
    mAdapter.swapCursor(null);
}

И когда я хотел использовать CursorLoader (Helper.bundleArgs() - это удобный метод связывания):

scAdapt = new Adapters.NewIndexedAdapter(mHost, getMenuType(), 
                null, new String[] { "name" }, new int[] { android.R.id.text1 });
        getLoaderManager().initLoader(
                GlobalCallbacks.GROUP,
                Helper.bundleArgs(new String[] { "_id", "name" }),
                new GlobalCallbacks(mHost, scAdapt));
        setListAdapter(scAdapt);

И в Помощнике:

public static Bundle bundleArgs(String[] projection, String selection, String[] selectionArgs) {
    Bundle b = new Bundle();
    b.putStringArray(GlobalCallbacks.PROJECTION, projection);
    b.putString(GlobalCallbacks.SELECTION, selection);
    b.putStringArray(GlobalCallbacks.SELECTARGS, selectionArgs);
    return b;
}

Надеюсь, это поможет кому-то еще:)

РЕДАКТИРОВАТЬ

Чтобы объяснить большеполностью:

  • Сначала инициализируется адаптер с нулевым значением Cursor.Мы не предоставляем Cursor, потому что GlobalCallbacks даст адаптеру правильный Cursor в onLoadFinished(..)
  • Далее, мы говорим LoaderManager, что мы хотим инициализировать новый CursorLoader,Мы предоставляем новый экземпляр GlobalCallbacks (который реализует Loader.Callbacks), который затем будет отслеживать загрузку курсора.Мы также должны снабдить его адаптером, чтобы он мог заменить новый Cursor после завершения загрузки.В какой-то момент LoaderManager (встроенный в ОС) вызовет onCreateLoader(..) из GlobalCallbacks и начнет асинхронную загрузку данных
  • Helper.bundleArgs(..) просто помещает аргументы для запроса в Bundle(например, проекция столбцов, порядок сортировки, предложение WHERE)
  • Затем мы устанавливаем Fragment s ListAdapter.Курсор все еще будет нулевым в этой точке, поэтому он покажет знак загрузки или пустое сообщение, пока onLoadFinished() не будет вызван
...