Подтвердить что ты не робот

Как CursorLoader автоматически обновляет представление, даже если приложение неактивно?

Я работаю над небольшим списком приложений To-Do. Я использовал CursorLoader для обновления ToDolistview у поставщика контента. У меня есть написанная функция onNewItemAdded(), которая вызывается, когда пользователь вводит новый элемент в текстовое представление и нажимает кнопку ввода. См. Ниже:

public void onNewItemAdded(String newItem) {
    ContentResolver cr = getContentResolver();

    ContentValues values = new ContentValues();
    values.put(ToDoContentProvider.KEY_TASK, newItem);

    cr.insert(ToDoContentProvider.CONTENT_URI, values);
    // getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing
}

@Override
protected void onResume() {
    super.onResume();
    //getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing
}

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

    CursorLoader loader = new CursorLoader(this,
            ToDoContentProvider.CONTENT_URI, null, null, null, null);
    Log.e("GOPAL", "In the onCreateLoader");
    return loader;
}

public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    int keyTaskIndex = cursor.getColumnIndexOrThrow(ToDoContentProvider.KEY_TASK);
    Log.e("GOPAL", "In the onLoadFinished");
    todoItems.clear();
    if (cursor.moveToNext() == false) Log.e("GOPAL", "Empty Cursor");
    else {
        while (cursor.moveToNext()) {
            ToDoItem newItem = new ToDoItem(cursor.getString(keyTaskIndex));
            todoItems.add(newItem);
        }
        aa.notifyDataSetChanged(); // aa is arrayadapter used for the listview
    }
}

Я прочитал, CursorLoader автоматически обновляет представление, всякий раз, когда происходит изменение данных в db поставщика контента. Это означает, что я полагаю, getLoaderManager().restartLoader(0, null, this) следует называть неявно всякий раз, когда происходит изменение данных, верно?         Но этого не происходит. Всякий раз, когда я добавляю новый элемент (элемент добавляется в db из onNewItemAdded, но restartLoader явно не вызывается), приостановите это действие и возобновите его. Я не вижу никакого неявного вызова restartLoader (даже если db изменен), и listview также не обновляется с добавленным добавленным элементом. Почему это? Как CursorLoader автоматически обновляет представление, даже если приложение неактивно??? Спасибо:)

EDIT: я также использовал getContext().getContentResolver().notifyChange(insertedId, null) вставить моего поставщика контента.

4b9b3361

Ответ 1

Я нашел ответ на свой вопрос. В общем случае CursorLoader автоматически не обнаруживает изменения данных и не загружает их для просмотра. Нам нужно отслеживать URI для изменений. Это можно сделать, выполнив следующие шаги:

  • Регистрация наблюдателя в разрешении контента с помощью курсора с помощью: (Сделано в методе запроса ContentProvider)
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

  • Теперь, когда есть какие-либо изменения в базовых данных URI с помощью insert()/delete()/update(),, мы уведомляем contentresolver об изменении, используя:

    getContext().getContentResolver().notifyChange(insertedId, null);

  • Это принимается наблюдателем, зарегистрированным на шаге 1, и это вызывает ContentResolver.query(), который вызывает вызовы Contentent providers query(), чтобы вернуть свежий курсор в loaderManager. LoaderManager вызывает onLoadFinished передачу этого курсора вместе с Cursorloader, где мы обновляем представление (используя adapter.swapcursor()) со свежими данными.

Для пользовательских AsyncTaskLoaders:

Иногда нам нужен наш пользовательский загрузчик вместо CursorLoader. Здесь мы можем использовать другой объект, отличный от курсора, чтобы указать на загруженные данные (например, список и т.д.). В этом случае мы не будем иметь preilige, чтобы уведомлять ContentResolver с помощью курсора. Приложение может также не иметь поставщика контента, чтобы отслеживать изменения URI. В этом сценарии мы используем BroadcastReceiver или явный ContentObserver для автоматического обновления вида. Это выглядит следующим образом:

  • Нам нужно определить наш пользовательский загрузчик, который расширяет AsyncTaskLoader и реализует все его абстрактные методы. В отличие от Cursorloader, наш пользовательский загрузчик может использовать или не использовать поставщик содержимого, и конструктор не может вызвать ContentResolver.query(), когда этот загрузчик будет установлен. Поэтому мы используем широковещательный приемник для выполнения этой цели.
  • Нам нужно создать экземпляр метода BroadCastReceiver или ContentObserver в OnStartLoading() абстрактного класса AsyncTaskLoader.
  • Этот приемник BroadCast должен быть определен для приема широковещательных данных, изменяющих данные, из поставщика контента или любых системных событий (как и новое приложение), и он должен вызвать метод loader onContentChanged(), чтобы уведомить загрузчика об изменении данных. Загружатель автоматически делает остальные для загрузки обновленных данных и вызывает onLoadFinished(), чтобы обновить представление.

Подробнее см. здесь: http://developer.android.com/reference/android/content/AsyncTaskLoader.html

Я нашел это очень полезным для ясного объяснения: http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html

Ответ 2

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

Я использую следующий код в родительской активности onActivityResult()

getLoaderManager().restartLoader(0, null, this);

Он отлично работает для меня. Пожалуйста, поделитесь, если есть лучший подход.

спасибо
iuq

Ответ 3

получить ссылку на ваш загрузчик при инициализации следующим образом

 Loader dayWeatherLoader = getLoaderManager().initLoader(LOADER_DAY_WEATHER, null, this);

затем создайте класс, который расширяет ContentObserver следующим образом

 class DataObserver extends ContentObserver {

    public DataObserver(Handler handler) {
        super(handler);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        dayWeatherLoader.forceLoad();
    }
}

Затем зарегистрируйте наблюдателя содержимого внутри метода жизненного цикла onResume следующим образом

@Override
public void onResume() {
    super.onResume();
    getContext().getContentResolver().registerContentObserver(CONTENTPROVIDERURI,true,new DayWeatherDataObserver(new Handler()));
}

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