Страница 1 из 1

обновление ListFragment заполненного через LoaderManager

Добавлено: 31 мар 2013, 22:32
ivied
Всем привет. Не могу накапать как правильно обновлять ListFragment. Вроде при заполнение с помощью LoaderManager курсор при изменении базы данных должен обновляться сам судя из инфы с http://developer.android.com/reference/ ... backs.html :
public abstract void onLoadFinished (Loader<D> loader, D data)

Added in API level 11
Called when a previously created loader has finished its load. Note that normally an application is not allowed to commit fragment transactions while in this call, since it can happen after an activity's state is saved. See FragmentManager.openTransaction() for further discussion on this.

This function 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 take care of management of its data so you don't have to. In particular:

The Loader will monitor for changes to the data, and report them to you through new calls here. You should not monitor the data yourself. For example, if the data is a Cursor and you place it in a CursorAdapter, use the CursorAdapter(android.content.Context, android.database.Cursor, int) constructor without passing in either FLAG_AUTO_REQUERY or FLAG_REGISTER_CONTENT_OBSERVER (that is, use 0 for the flags argument). This prevents the CursorAdapter from doing its own observing of the Cursor, which is not needed since when a change happens you will get a new Cursor throw another call here.

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(android.database.Cursor) method so that the old Cursor is not closed.
Но он не обновляется, он считывает данные из базы только один раз и при изменении базы данных курсор не обновляется. Максимум что у меня получилось добиться это запустить обновление при переключении между вкладками(фрагмент служит содержим одной из вкладок FragmetActivity). Для этого я инициализировал LoaderManager каждый раз разными ид через рандомное число. Из более менее осмысленного еще пытался перевызывать LoaderManager в AsynkTask .Еще пробовал adapter.notifyDataSetChanged(). Но это как и метод тыка и гугл не помогли.

Код: Выделить всё

.......
public class ChatList extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
	final  String LOG_TAG = "myLogs";
	final Uri CONTACT_URI = Uri.parse("content://ivied.p001astreamchat/chats");
	String chatName;
	ChatCursorAdapter adapter;
	 private LoaderManager.LoaderCallbacks<Cursor> mCallbacks;
	 private static final int LOADER_ID = 1;
	
	 @Override
	  public void onActivityCreated(Bundle savedInstanceState) {
	    super.onActivityCreated(savedInstanceState);
	    Log.d(LOG_TAG, this.toString()); 
	  
	    String[] from = new String[] { MyContentProvider.MESSAGES_CHANEL, MyContentProvider.MESSAGES_MESSAGE };
	    int[] to = new int[] { R.id.ivImg, R.id.tvText };
	 // Initialize the adapter.
	    adapter = new ChatCursorAdapter(getActivity(), 
	    		R.layout.message, null, from, to, 0);
	 // Associate the (now empty) adapter with the ListView.
	    setListAdapter(adapter); 

	    mCallbacks = this;
	    LoaderManager lm = getLoaderManager();
	    lm.initLoader(1, null, mCallbacks);
	    
	 } 	
		
	 
	 @Override
	 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
				
		 String [] selectionArgs = new String[] { "67822" };
		  
		   Loader<Cursor> mLoader = new CursorLoader(getActivity(), CONTACT_URI, null, "channel = ?",
	        		selectionArgs, null);
		    return mLoader;
		  
	 }

		@Override
		public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
			
			// Swap the new cursor in. (The framework will take care of closing the
		    // old cursor once we return.)
			 Log.i(LOG_TAG, "чтение курсора2");
			adapter.swapCursor(data);
			setListAdapter(adapter); 
		    // The list should now be shown.
		    if (isResumed()) {
		        setListShown(true);
		    } else {
		        setListShownNoAnimation(true);
		    }
		}
		
		@Override
		public void onLoaderReset(Loader<Cursor> loader) {
			
		    // This is called when the last Cursor provided to onLoadFinished()
		    // above is about to be closed. We need to make sure we are no
		    // longer using it.
			adapter.swapCursor(null);
		}
		
}

Re: обновление ListFragment заполненного через LoaderManager

Добавлено: 31 мар 2013, 23:03
Foenix
Они получают уведомление об изменении курсора, но принимать действия, обрабатывать это нужно самому.
вот тут объяснено как http://www.androiddesignpatterns.com/20 ... aders.html
An observer to receive notifications when the data source has changed. Loaders should implement an observer of some sort (i.e. a ContentObserver, a BroadcastReceiver, etc.) to monitor the underlying data source for changes. When a change is detected, the observer should call Loader#onContentChanged(), which will either (a) force a new load if the Loader is in a started state or, (b) raise a flag indicating that a change has been made so that if the Loader is ever started again, it will know that it should reload its data

Re: обновление ListFragment заполненного через LoaderManager

Добавлено: 31 мар 2013, 23:36
neoksi
После чтения данных в курсоры, вам необходимо зарегистрировать в них собственные Observer'ы, это определенные CallBack методы, которые выполняются при изменении данных в курсоре. Далее в этих CallBack методах вам необходимо задать процедуру обновления ваших элементов.

Re: обновление ListFragment заполненного через LoaderManager

Добавлено: 01 апр 2013, 15:18
ivied
Спасибо за ответы!
Они получают уведомление об изменении курсора, но принимать действия, обрабатывать это нужно самому.
вот тут объяснено как http://www.androiddesignpatterns.com/20 ... aders.html
Цитата:
An observer to receive notifications when the data source has changed. Loaders should implement an observer of some sort (i.e. a ContentObserver, a BroadcastReceiver, etc.) to monitor the underlying data source for changes. When a change is detected, the observer should call Loader#onContentChanged(), which will either (a) force a new load if the Loader is in a started state or, (b) raise a flag indicating that a change has been made so that if the Loader is ever started again, it will know that it should reload its data
Именно по второй части этого туториала я делал свой LoaderManager, пытаюсь создать свой ContentObserver. пока даже не пойму как и где его надо регестрировать по хорошему. То что хоть ошибок не выдает:

Код: Выделить всё

...
 public void onActivityCreated(Bundle savedInstanceState) {
....
                    Handler handler=null; // [b]Откуда брать этот Handler?[/b]
		    MyObserver myObs = new MyObserver(handler);
		    getActivity().getContentResolver().registerContentObserver( CONTACT_URI, true, myObs);

....
Но даже при если все правильно удастся зарегистрировать, loader.onContentChanged() вызывает onLoadFinished(Loader<Cursor> loader, Cursor data) где cursor старый остается и FragmentList перезаполняется старыми данными(хотя может все же нужно просто пересоздавать loader?)

Было написано что можно воспользоваться BroadcastReceiver. Я могу создать Action при вставке данных в мою бд, а что потом? Зарегистрировать в onActivityCreated ресивер который будет убивать старый Loader(как?) и вызывать новый?
После чтения данных в курсоры, вам необходимо зарегистрировать в них собственные Observer'ы, это определенные CallBack методы, которые выполняются при изменении данных в курсоре. Далее в этих CallBack методах вам необходимо задать процедуру обновления ваших элементов.
Я создал класс Обсервера:

Код: Выделить всё

class MyObserver extends ContentObserver {      
			
			   public MyObserver(Handler handler) {
			
			      super(handler);         
			
			   }
			
			   @Override
			
			   public void onChange(boolean selfChange) {
			
			      
			   }     
			
			   
			
			
			}
где мне его теперь регестрировать?
После чтения данных в курсоры, вам необходимо зарегистрировать в них собственные Observer'ы
в курсорах? как это сделать?

На самом деле мне бы хватило обновлять мой ListFragment новым курсором из моей Базы данных раз в 5 секунд. Судя из уроков на сайте для этого раньше можно было бы запустить TimeUnit.SECONDS.sleep(5); -> cursor.requery() в асинхронном потоке. Как это реализовать через Loader?(обновление курсора раз в 5 секунд не в основном потоке)

Re: обновление ListFragment заполненного через LoaderManager

Добавлено: 01 апр 2013, 16:18
neoksi
Cursor.register... дальше на память сейчас не помню, но думаю найдете нужный метод.

Re: обновление ListFragment заполненного через LoaderManager

Добавлено: 01 апр 2013, 16:36
ivied
neoksi писал(а):Cursor.register... дальше на память сейчас не помню, но думаю найдете нужный метод.
Спасибо! Единственное место где мы передаем курсор это метод onLoadFinished , значит должно выглядеть так? :

Код: Выделить всё

Handler handler=null;
MyObserver myObs = new MyObserver(handler);
.....
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
			
			
			data.registerContentObserver(myObs);
                        adapter.swapCursor(data);}
откуда тут брать handler? тут же явно не null надо передавать.
И еще. как бы вы организовали посоветовали действовать если требуется обновлять ListFragment новым курсором раз в 5 секунд, вне зависимости от того обновлялась ли база данных или нет.

Re: обновление ListFragment заполненного через LoaderManager

Добавлено: 01 апр 2013, 16:58
ivied
Такая идея.
При добавлении записи в дб (или через каждые 5 секунд например) создаем Action. Этот Action будем ловить в onLoadFinished , после чего пересчитывть cursor и обновлять adapter. Так это должно работать или это велосипед и все проще?
Как я понял мы все же должны вручную пересчитывать курсор и это не зашито где-то в функциях LoaderManager. или я ошибиаюсь?

Re: обновление ListFragment заполненного через LoaderManager

Добавлено: 01 апр 2013, 18:05
ivied
Что-то получилось.. Создал Action при обновлении базы, и ловлю в своем FragmentList:

Код: Выделить всё

....
public void onActivityCreated(Bundle savedInstanceState) {
.....
newMessage = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
		        	 LoaderManager lm = getLoaderManager();
		        	 lm.restartLoader(LOADER_ID, null, mCallbacks);   }};
....
Это велосипед или так и должно быть?

Re: обновление ListFragment заполненного через LoaderManager

Добавлено: 01 апр 2013, 18:24
ivied
Омг... Ну и жесть. 2 дня убил на непойми что(
Все решалось подписиью одной строчки в ContentProvider : cursor.setNotificationUri(getContext().getContentResolver(),CONTACT_CONTENT_URI);

Re: обновление ListFragment заполненного через LoaderManager

Добавлено: 02 апр 2013, 03:04
neoksi
Ты забывал сообщить что курсор обновился %)