Как 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)
вставить моего поставщика контента.
Ответы
Ответ 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, где вы можете попросить загрузчика снова загрузить данные.