ContentProvider olmadan CursorLoader kullanımı


107

Android SDK belgeleri, bu startManagingCursor()yöntemin kullanımdan kaldırıldığını söylüyor :

Bu yöntem kullanımdan kaldırılmıştır. Bunun yerine LoaderManager ile yeni CursorLoader sınıfını kullanın; bu, Android uyumluluk paketi aracılığıyla eski platformlarda da mevcuttur. Bu yöntem, faaliyetin, belirli Cursor'un yaşam döngüsünü, etkinliğin yaşam döngüsüne göre sizin için yönetmesine olanak tanır. Yani, etkinlik durdurulduğunda, verilen İmleçte otomatik olarak deactivate () çağıracak ve daha sonra yeniden başlatıldığında sizin için request () çağrısı yapacaktır. Aktivite yok edildiğinde, yönetilen tüm İmleçler otomatik olarak kapatılacaktır. HONEYCOMB veya sonraki bir sürümünü hedefliyorsanız, bunun yerine getLoaderManager () aracılığıyla kullanılabilen LoaderManager kullanmayı düşünün

Bu yüzden kullanmak isterim CursorLoader. Ancak yapıcısında URI'ye ihtiyacım olduğunda, özel CursorAdapterve özel olmadan nasıl kullanabilirim ?ContentProviderCursorLoader


@Alex Lockwood ContentProvider olmadan neden CursorAdapter kullanıyoruz lütfen bana önerin stackoverflow.com/questions/20419278/…

ContentProvider olmadan neden CursorAdapter kullanıyoruz lütfen bana stackoverflow.com/questions/20419278/…

Yanıtlar:


155

Bir içerik sağlayıcıya ihtiyaç duymayan basit bir CursorLoader yazdım :

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;
    }
}

Sadece AsyncTaskLoadersınıfa ihtiyacı var . Ya Android 3.0 veya üzeri sürümdeki ya da uyumluluk paketiyle birlikte gelen.

Ayrıca , ile uyumlu olan ve genel bir koleksiyon almak için kullanılan birListLoader de yazdım .LoadManagerjava.util.List


13
Bunu kullanan güzel bir kod örneği buldum - bitbucket.org/ssutee/418496_mobileapp/src/fc5ee705a2fd/demo/… - çok faydalı buldum!
Shushu

@Cristian Örnek için teşekkürler. Sınıfınızla ilişkili lisans nedir. Nasıl yeniden kullanılabilir?
codinguser

2
Lisans Apache 2.0'dır; nerede / ne zaman isterseniz yeniden kullanabilirsiniz. Herhangi bir iyileştirmeniz varsa bana bildirin.
Cristian

14
Harika şeyler! Kullanıcılar bir sınırlamanın farkında olmalıdır, bu da veri değişikliklerini yenilemek için bir mekanizmanın olmamasıdır (Yükleyicilerin yapması gerektiği gibi)
emm

1
@Jadeye burada adamınız var: ListLoader ve SupportListLoader
Cristian

23

Bir içerik sağlayıcı yerine veritabanı sınıfınızı kullanan kendi yükleyicinizi yazın. En kolay yol, CursorLoadersınıfın kaynağını uyumluluk kitaplığından almak ve sağlayıcı sorgularını sorgularla kendi db yardımcı sınıfınıza değiştirmektir.


1
Bence bu en kolay yol. Uygulamamda CursorLoaderbir SQLite imlecini yönetmek için bir descendat oluşturdum , kurucudan ayrı olarak yalnızca loadInBackgroundsağlayıcı sorgusunu imleç
sorgumla

14

SimpleCursorLoader basit bir çözümdür, ancak veri değiştiğinde yükleyicinin güncellenmesini desteklemez. CommonsWare, bir SQLiteCursorLoader ekleyen ve veri değişikliklerinde yeniden sorgulamayı destekleyen bir loaderex kitaplığına sahiptir.

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


2
Bununla birlikte, otomatik yeniden sorgulamadan yararlanmak için, UI ve güncellemeler için aynı yükleyiciyi kullanmanız gerekir, bu da arka plan hizmetleri için kullanılabilirliğini sınırlar.
ge0rg

12

Üçüncü bir seçenek, basitçe geçersiz kılmak olacaktır 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;
    }
};

Bu, veritabanı değiştiğinde imlecinizi yeniden sorgulamanızı da sağlayacaktır.

Sadece uyarı: Google sonsuz bilgeliğiyle kendi paketini özel yapmaya karar verdiğinden, başka bir gözlemci tanımlamanız gerekecek. Sınıfı orijinaliyle (veya uyumlu olanla) aynı pakete koyarsanız, aslında orijinal gözlemciyi kullanabilirsiniz. Gözlemci çok hafif bir nesnedir ve başka hiçbir yerde kullanılmaz, bu yüzden bu pek bir fark yaratmaz.


Hızlı testte gözlemim, registerContentObserver'ın yalnızca imleç bir İçerik Sağlayıcıyı hedefliyorsa imlece karşı çağrılacağıdır. Bunu onaylayabilir / reddedebilir misiniz?
Nick Campion

1
Bir ContentProvider olması gerekmez. Ancak imlecin bir bildirim uri'sine (setNotificationUri) kaydedilmesi gerekir ve daha sonra ContentResolver.notifyChange çağrılarak birisi (genellikle bir ContentProvider olabilir, ancak herhangi bir şey olabilir) tarafından bilgilendirilmesi gerekir.
Timo Ohr

4
Evet. CustomLoader'ınızda loadInBackground() , imleci döndürmeden önce cursor.setNotificationUri(getContext().getContentResolver(), uri);, uri'nin sadece rastgele String'den olabileceğini söyleyin Uri.parse("content://query_slot1"). Uri'nin gerçekten var olup olmadığı umurunda değil gibi görünüyor. Ve bir kez DB'de ameliyat yaptım. Say getContentResolver().notifyChange(uri, null);hile yapardı. Daha sonra, az sayıda sorgu içeren uygulama için bir contant dosyasında birkaç "sorgu uri yuvası" oluşturabilirim. Çalışma zamanında DB kaydını yerleştirmeyi test ediyorum ve işe yarıyor gibi görünüyor ancak yine de bunun iyi bir uygulama olduğundan şüpheliyim. Herhangi bir öneri?
Yeung

Bu yöntemi @Yeung'un önerisiyle kullanıyorum ve veritabanı güncellemesinde imlecin otomatik olarak yeniden yüklenmesi dahil her şey çalışıyor.
DavidH

herhangi bir unregisterContentObserver'a ihtiyacı yok mu?
2016

2

Timo Ohr tarafından önerilen üçüncü seçenek, Yeung'un yorumlarıyla birlikte en basit cevabı (Occam'ın usturası) sağlıyor. Aşağıda benim için çalışan eksiksiz bir sınıf örneği bulunmaktadır. Bu sınıfı kullanmanın iki kuralı vardır.

  1. Bu soyut sınıfı genişletin ve getCursor () ve getContentUri () yöntemlerini uygulayın.
  2. Temel veritabanı her değiştiğinde (örneğin, bir ekleme veya silme işleminden sonra),

    getContentResolver().notifyChange(myUri, null);

    myUri, getContentUri () yöntemini uygulamanızdan döndürülenle aynıdır.

İşte kullandığım sınıfın kodu:

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();
  }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.