2012-01-18 25 views
5

Aktualizuję aplikację dla systemu Android 2.2, aby używać CursorLoader (przy użyciu biblioteki zgodności v4) i wyrywam sobie włosy, próbując zrozumieć, dlaczego metoda onLoadFinished nie jest wywoływana, gdy dostawca treści powiadamia o zmianie treści powiązanej z zapytaniem CursorLoader.Android CursorLoader nie odpowiada na powiadomienia ContentProvider

Moduł CursorLoader wysyła zapytanie do dostawcy treści klienta. Mój dostawca ustawia powiadomienia URI w swojej metodzie zapytania:

cursor.setNotificationUri(getContext().getContentResolver(), uri); 

i powiadamia zmian w jego Wstaw/aktualizacja/delete metody:

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

Sprawdziłem, że URI jest identyczna w obu przypadkach. Poprzednio korzystałem z usługi ManagedQuery z tym samym dostawcą treści, a treść zapytana została zaktualizowana dobrze, co powoduje, że wydaje mi się, że dostawca treści jest prawdopodobnie w porządku.

Przyjrzałem się przykładowi LoaderCursorSupport i, co ciekawe, po uruchomieniu go na Nexus One nie widzę, aby odzwierciedlało ono zmiany, które wprowadzam w kontaktach z nazwami (przełączanie między przykładową aplikacją a aplikacją kontaktów). Czy to powinno być? Jeśli tak, czy jest jakiś podstawowy problem, którego nie jestem świadomy?

+0

Czy możesz podać kod, który pomoże nam zidentyfikować problem? –

+0

Włączenie debugowania może pomóc, 'LoaderManager.enableDebugLogging (true)' –

+0

Dzięki za wskazówkę debugowania. Mój kod klienta jest w zasadzie taki sam, jak w przypadku wskazanego przeze mnie przykładu LoaderCursorSupport. Biorąc pod uwagę, że to samo dotyczy tego problemu, czy źle zrozumiałem, jak powinien działać CursorLoader, tzn. Czy uruchamiam przykład pomocy, aby wyświetlić listę kontaktów, a następnie edytować kontakt w książce adresowej, jeśli nie zmieni się na liście wyświetlanej przez przykładowa aplikacja? – siwatson

Odpowiedz

-1

Może należy użyć kontekstu aplikacji:

getContext().getApplicationContext().getContentResolver().notifyChange(uri, null); 
+0

To nie sprawiło żadnej różnicy, obawiam się. – siwatson

4

I wreszcie do sedna sprawy i, jak to zwykle droga, to był głupi błąd z mojej strony. Nazwałem cursor.close() w mojej metodzie onLoadFinished() - używam zwróconego kursora, aby utworzyć ArrayAdapter (muszę ręcznie wstawić element na górze listy), a zamknięcie kursora było pozostawieniem po użyciu ManagedQuery przed migracją, aby użyć CursorLoader.

W trakcie wyszukiwania tego, utworzyłem prostą klasę testową, aby wyświetlić listę zakładek i dodać losową zakładkę (za pomocą menu opcji). To zadziałało tak, jak powinno, np. Funkcja onLoadFinished() jest wywoływana po dodaniu elementu. Oto kod na wypadek, gdyby był przydatny dla kogoś innego:

package com.test; 

import android.content.ContentValues; 
import android.database.Cursor; 
import android.os.Bundle; 
import android.provider.Browser; 
import android.support.v4.app.FragmentActivity; 
import android.support.v4.app.FragmentManager; 
import android.support.v4.app.ListFragment; 
import android.support.v4.app.LoaderManager; 
import android.support.v4.content.CursorLoader; 
import android.support.v4.content.Loader; 
import android.support.v4.view.MenuItemCompat; 
import android.support.v4.widget.SimpleCursorAdapter; 
import android.util.Log; 
import android.view.Menu; 
import android.view.MenuInflater; 
import android.view.MenuItem; 

public class CursorLoaderTestActivity extends FragmentActivity 
{ 
    private static final String TAG = CursorLoaderTestActivity.class.getSimpleName(); 

    @Override 
    protected void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 

     FragmentManager fm = getSupportFragmentManager(); 

     // Create the list fragment and add it as our sole content. 
     if (fm.findFragmentById(android.R.id.content) == null) 
     { 
      CursorLoaderListFragment list = new CursorLoaderListFragment(); 
      fm.beginTransaction().add(android.R.id.content, list).commit(); 
     } 
    } 


    public static class CursorLoaderListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> 
    { 

     // This is the Adapter being used to display the list's data. 
     SimpleCursorAdapter mAdapter; 

     // If non-null, this is the current filter the user has provided. 
     String mCurFilter; 

     @Override public void onActivityCreated(Bundle savedInstanceState) 
     { 
      super.onActivityCreated(savedInstanceState); 

      // Give some text to display if there is no data. In a real 
      // application this would come from a resource. 
      setEmptyText("No data"); 

      // We have a menu item to show in action bar. 
      setHasOptionsMenu(true); 

      // Create an empty adapter we will use to display the loaded data. 
      mAdapter = new SimpleCursorAdapter(getActivity(), 
        android.R.layout.simple_list_item_1, null, 
        new String[] { Browser.BookmarkColumns.TITLE }, 
        new int[] { android.R.id.text1}, 0); 

      setListAdapter(mAdapter); 

      // Start out with a progress indicator. 
      setListShown(false); 

      // Prepare the loader. Either re-connect with an existing one, 
      // or start a new one. 
      getLoaderManager().initLoader(0, null, this); 
     } 

     //@Override 
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) 
     { 
      // Place an action bar item for searching. 
      MenuItem item = menu.add("Add Item"); 
      //item.setIcon(android.R.drawable.ic_menu_search); 
      MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_ALWAYS); 
     } 

     @Override 
     public boolean onOptionsItemSelected (MenuItem item) 
     { 
      ContentValues cv=new ContentValues(); 
      cv.put(Browser.BookmarkColumns.TITLE, "!AA " + System.currentTimeMillis()); 
      cv.put(Browser.BookmarkColumns.URL, "http://test/"); 
      cv.put(Browser.BookmarkColumns.BOOKMARK, 1); 
      getActivity().getContentResolver().insert(Browser.BOOKMARKS_URI, cv); 
      return true; 
     } 

     //columns to query 
     static final String[] PROJECTION = new String[] { Browser.BookmarkColumns.TITLE }; 


     public Loader<Cursor> onCreateLoader(int id, Bundle args) 
     { 
      Log.i(TAG, "onCreateLoader"); 

      return new CursorLoader(getActivity(), Browser.BOOKMARKS_URI, 
        PROJECTION, null, null, 
        Browser.BookmarkColumns.TITLE + " ASC"); 
     } 

     public void onLoadFinished(Loader<Cursor> loader, Cursor data) 
     { 
      Log.i(TAG, "onLoadFinished"); 

      // Swap the new cursor in. (The framework will take care of closing the 
      // old cursor once we return.) 
      mAdapter.swapCursor(data); 

      // The list should now be shown. 
      if (isResumed()) 
       setListShown(true); 
      else 
       setListShownNoAnimation(true); 
     } 

     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. 
      mAdapter.swapCursor(null); 
     } 
    } 

} 
+0

Tak. Kursor powinien pozostać przy życiu, dopóki nie zostanie dostarczony w onLoadFinished(). – f470071

Powiązane problemy