Użycie CursorLoader bez ContentProvider

Dokumentacja Android SDK mówi, że metoda startManagingCursor() jest przestarzała:

Ta metoda jest przestarzała. Zamiast tego użyj nowej klasy CursorLoader z LoaderManager; jest ona również dostępna na starszych platformach za pośrednictwem pakietu kompatybilności z Androidem. Ta metoda pozwala aktywności zająć się zarządzaniem cyklem życia danego kursora w oparciu o cykl życia aktywności. Oznacza to, że gdy aktywność zostanie zatrzymana, automatycznie wywoła deactivate () na podanym kursorze, a po późniejszym ponownym uruchomieniu wywoła requery () dla Ciebie. Gdy aktywność zostanie zniszczona, wszystkie zarządzane Kursory zostaną automatycznie zamknięte. Jeśli celujesz w HONEYCOMB lub później, rozważ zamiast tego użycie LoaderManager, dostępnego za pomocą getLoaderManager ()

Więc chciałbym użyć CursorLoader. Ale jak mogę go używać z custom CursorAdapter i bez ContentProvider, Kiedy potrzebuję URI w konstruktorze CursorLoader?

Author: sealskej, 2011-08-25

5 answers

Napisałem prosty CursorLoader , który nie potrzebuje dostawcy treści:

import android.content.Context;
import android.database.Cursor;
import android.support.v4.content.AsyncTaskLoader;

/**
 * Used to write apps that run on platforms prior to Android 3.0. When running
 * on Android 3.0 or above, this implementation is still used; it does not try
 * to switch to the framework's implementation. See the framework SDK
 * documentation for a class overview.
 *
 * This was based on the CursorLoader class
 */
public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> {
    private Cursor mCursor;

    public SimpleCursorLoader(Context context) {
        super(context);
    }

    /* Runs on a worker thread */
    @Override
    public abstract Cursor loadInBackground();

    /* Runs on the UI thread */
    @Override
    public void deliverResult(Cursor cursor) {
        if (isReset()) {
            // An async query came in while the loader is stopped
            if (cursor != null) {
                cursor.close();
            }
            return;
        }
        Cursor oldCursor = mCursor;
        mCursor = cursor;

        if (isStarted()) {
            super.deliverResult(cursor);
        }

        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
            oldCursor.close();
        }
    }

    /**
     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
     * will be called on the UI thread. If a previous load has been completed and is still valid
     * the result may be passed to the callbacks immediately.
     * <p/>
     * Must be called from the UI thread
     */
    @Override
    protected void onStartLoading() {
        if (mCursor != null) {
            deliverResult(mCursor);
        }
        if (takeContentChanged() || mCursor == null) {
            forceLoad();
        }
    }

    /**
     * Must be called from the UI thread
     */
    @Override
    protected void onStopLoading() {
        // Attempt to cancel the current load task if possible.
        cancelLoad();
    }

    @Override
    public void onCanceled(Cursor cursor) {
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
    }

    @Override
    protected void onReset() {
        super.onReset();

        // Ensure the loader is stopped
        onStopLoading();

        if (mCursor != null && !mCursor.isClosed()) {
            mCursor.close();
        }
        mCursor = null;
    }
}

Potrzebuje tylko klasy AsyncTaskLoader. Albo ten w Androidzie 3.0 lub nowszym, albo ten, który jest dostarczany z pakietem kompatybilności.

Ja też napisałam ListLoader który jest kompatybilny z LoadManager i służy do pobierania ogólnej kolekcji java.util.List.

 153
Author: Cristian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-22 18:38:02

Napisz własny loader, który używa twojej klasy bazy danych zamiast dostawcy treści. Najprostszym sposobem jest pobranie źródła klasy CursorLoader z biblioteki kompatybilności i zastąpienie zapytań dostawcy zapytaniami do własnej klasy pomocniczej db.

 22
Author: Nikolay Elenkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-25 02:54:07

SimpleCursorLoader jest prostym rozwiązaniem, jednak nie obsługuje aktualizacji ładowarki, gdy dane ulegną zmianie. CommonsWare ma bibliotekę loaderex, która dodaje SQLiteCursorLoader i obsługuje ponowne Zapytanie o zmiany danych.

Https://github.com/commonsguy/cwac-loaderex

 14
Author: emmby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-22 17:58:51

Trzecią opcją byłoby po prostu nadpisanie loadInBackground:

public class CustomCursorLoader extends CursorLoader {
    private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();

    @Override
    public Cursor loadInBackground() {
        Cursor cursor = ... // get your cursor from wherever you like

        if (cursor != null) {
            // Ensure the cursor window is filled
            cursor.getCount();
            cursor.registerContentObserver(mObserver);
        }

        return cursor;
    }
};

To również zajmie się ponownym zapytaniem kursora po zmianie bazy danych.

Tylko zastrzeżenie: będziesz musiał zdefiniować innego obserwatora, ponieważ Google w swojej nieskończonej mądrości postanowił uczynić swój pakiet prywatnym. Jeśli umieścisz klasę w tym samym pakiecie co oryginalna (lub compat), możesz użyć oryginalnej observer. Obserwator jest bardzo lekkim obiektem i nie jest używany nigdzie indziej, więc to nie robi wielkiej różnicy.

 12
Author: Timo Ohr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-28 18:24:11

Trzecia opcja zaproponowana przez Timo Ohra, wraz z uwagami Yeunga, daje najprostszą odpowiedź (brzytwa Occama). Poniżej znajduje się przykład kompletnej klasy, która działa dla mnie. Istnieją dwie zasady korzystania z tej klasy.

  1. Rozszerz tę klasę abstrakcyjną i zaimplementuj metody getCursor () i getContentUri ().
  2. Za każdym razem, gdy bazowa baza danych ulegnie zmianie (np. po wstawieniu lub usunięciu), upewnij się, że wywołujesz

    getContentResolver().notifyChange(myUri, null);
    

    Gdzie myUri jest tym samym zwracane z implementacji metody getContentUri ().

Oto kod dla klasy, której użyłem:

package com.example.project;

import android.content.Context;
import android.database.Cursor;
import android.content.CursorLoader;
import android.content.Loader;

public abstract class AbstractCustomCursorLoader extends CursorLoader
  {
    private final Loader.ForceLoadContentObserver mObserver = new Loader.ForceLoadContentObserver();

    public AbstractCustomCursorLoader(Context context)
      {
        super(context);
      }

    @Override
    public Cursor loadInBackground()
      {
        Cursor cursor = getCursor();

        if (cursor != null)
          {
            // Ensure the cursor window is filled
            cursor.getCount();
            cursor.registerContentObserver(mObserver);
          }

        cursor.setNotificationUri(getContext().getContentResolver(), getContentUri());
        return cursor;
      }

    protected abstract Cursor getCursor();
    protected abstract Uri getContentUri();
  }
 2
Author: John Moore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-04 23:20:36