ContentProvider के बिना CursorLoader उपयोग


107

Android SDK दस्तावेज़ीकरण कहता है कि startManagingCursor()विधि को हटा दिया गया है:

यह विधि पदावनत है। इसके बजाय LoaderManager के साथ नए CursorLoader वर्ग का उपयोग करें; यह एंड्रॉइड संगतता पैकेज के माध्यम से पुराने प्लेटफार्मों पर भी उपलब्ध है। यह विधि गतिविधि के जीवनचक्र के आधार पर आपके लिए दिए गए कर्सर के जीवनचक्र के प्रबंधन की देखभाल करने की अनुमति देती है। यही है, जब गतिविधि बंद हो जाती है, तो यह स्वचालित रूप से दिए गए कर्सर पर निष्क्रिय () को कॉल करेगा, और जब बाद में इसे फिर से चालू किया जाएगा तो यह आपके लिए आवश्यक () कॉल करेगा। जब गतिविधि नष्ट हो जाती है, तो सभी प्रबंधित कर्सर स्वचालित रूप से बंद हो जाएंगे। यदि आप HONEYCOMB को लक्षित कर रहे हैं या बाद में, इसके बजाय LoaderManager का उपयोग करने पर विचार करें, getLoaderManager के माध्यम से उपलब्ध ()

इसलिए मैं उपयोग करना चाहूंगा CursorLoader। लेकिन मैं इसे कस्टम के साथ CursorAdapterऔर बिना उपयोग कैसे कर सकता हूं ContentProvider, जब मुझे निर्माण में यूआरआई की आवश्यकता है CursorLoader?


@Alex Lockwood हम कंटेंटप्रूडर के बिना CursorAdapter का उपयोग क्यों कर रहे हैं, कृपया मुझे सुझाव दें stackoverflow.com/questions/20419278/…

कंटेंटप्रूडर के बिना हम CursorAdapter का उपयोग क्यों कर रहे हैं, कृपया मुझे सुझाव दें stackoverflow.com/questions/20419278/…

जवाबों:


155

मैंने एक सरल CursorLoader लिखा है जिसे सामग्री प्रदाता की आवश्यकता नहीं है:

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

इसके लिए केवल AsyncTaskLoaderवर्ग की जरूरत है । या तो एंड्रॉइड 3.0 में एक या उच्चतर, या संगतता पैकेज के साथ आने वाला एक।

मैंने यह भी लिखा हैListLoader कि एक के साथ संगत है LoadManagerऔर एक सामान्य java.util.Listसंग्रह को पुनः प्राप्त करने के लिए उपयोग किया जाता है ।


13
एक अच्छा कोड उदाहरण मिला जो इसका उपयोग करता है - bitbucket.org/ssutee/418496_mobileapp/src/fc5ee705a2fd/demo/… - इसे बहुत उपयोगी पाया!
शुशु

@ क्रिश्चियन उदाहरण के लिए धन्यवाद। आपकी कक्षा से संबंधित लाइसेंस क्या है। इसका पुन: उपयोग कैसे किया जा सकता है?
कोडिंग्यूसर

2
लाइसेंस अपाचे 2.0 है; आप इसे फिर से उपयोग कर सकते हैं जहां / जब भी आप चाहें। अगर आपको कोई सुधार करना है तो मुझे बताएं।
क्रिस्टियन

14
उत्तम सामग्री! उपयोगकर्ताओं को एक सीमा के बारे में पता होना चाहिए, जो यह है कि इसमें डेटा परिवर्तनों पर ताज़ा करने के लिए कोई तंत्र नहीं है (जैसा कि
लोडर्स

1
@ जदये यहाँ आपके पास आदमी है: लिस्टलॉडर और सपोर्टलिस्टलॉडर
क्रिश्चियन

23

अपने स्वयं के लोडर को लिखें जो सामग्री प्रदाता के बजाय आपके डेटाबेस वर्ग का उपयोग करता है। सबसे आसान तरीका सिर्फ CursorLoaderअनुकूलता पुस्तकालय से कक्षा के स्रोत को लेना है , और प्रदाता प्रश्नों को अपने स्वयं के db सहायक वर्ग से प्रश्नों से बदलना है।


1
यह मेरी राय में सबसे आसान तरीका है। मेरे ऐप में मैंने CursorLoaderएक SQLite कर्सर का प्रबंधन करने के लिए एक वंशज बनाया , निर्माता से मुझे केवल loadInBackgroundअपने कर्सर क्वेरी के साथ प्रदाता क्वेरी को बदलने के लिए विधि को ओवरराइड करने की आवश्यकता है
जोस_गीडी

14

SimpleCursorLoader एक सरल उपाय है, हालांकि यह डेटा को बदलने पर लोडर को अपडेट करने का समर्थन नहीं करता है। कॉमन्सवेयर में एक लोडरेक्स लाइब्रेरी है जो SQLiteCursorLoader जोड़ता है और डेटा परिवर्तनों पर फिर से क्वेरी का समर्थन करता है।

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


2
हालाँकि, स्वचालित री-क्वेरी का उपयोग करने के लिए, आपको UI के साथ ही अपडेट के लिए, पृष्ठभूमि सेवाओं के लिए इसकी उपयोगिता को सीमित करते हुए उसी लोडर का उपयोग करने की आवश्यकता है।
ge0rg

12

तीसरा विकल्प केवल ओवरराइड करना होगा 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;
    }
};

डेटाबेस बदलने पर यह आपके कर्सर को फिर से क्वेरी करने का भी ध्यान रखेगा।

केवल चेतावनी: आपको एक और पर्यवेक्षक को परिभाषित करना होगा, क्योंकि अनंत ज्ञान में Google ने उनके पैकेज को निजी बनाने का फैसला किया है। यदि आप कक्षा को मूल पैकेज (या कॉम्पिटिटर वन) के समान पैकेज में रखते हैं तो आप वास्तव में मूल पर्यवेक्षक का उपयोग कर सकते हैं। प्रेक्षक एक बहुत ही हल्की वस्तु है और इसका उपयोग कहीं और नहीं किया जाता है, इसलिए इससे बहुत फर्क नहीं पड़ता है।


त्वरित परीक्षण में मेरा अवलोकन यह है कि रजिस्टरकंटेंटऑब्जर्वर को केवल कर्सर के खिलाफ बुलाया जाएगा यदि कर्सर किसी सामग्री प्रदाता को लक्षित है। क्या आप इसकी पुष्टि / खंडन कर सकते हैं?
निक कैंपियन

1
यह जरूरी नहीं कि एक ContentProvider हो। लेकिन कर्सर को अधिसूचना uri (setNotificationUri) में पंजीकृत होने की आवश्यकता होती है, और इसके बाद ContentResolver.notifyChange पर कॉल करके किसी व्यक्ति (आमतौर पर एक ContentProvider, लेकिन कुछ भी हो सकता है) को सूचित किया जाना चाहिए।
तिमोर ओह

4
हाँ। अपने CustomLoader पर loadInBackground() , कर्सर को वापस करने से पहले, कहें cursor.setNotificationUri(getContext().getContentResolver(), uri);कि uri जैसे यादृच्छिक स्ट्रिंग से हो सकता है Uri.parse("content://query_slot1")। ऐसा लगता है कि यह वास्तव में मौजूद है या नहीं यूरिया परवाह नहीं है। और एक बार मैंने डीबी पर ऑपरेशन किया। कहते getContentResolver().notifyChange(uri, null);हैं चाल चलेंगे। फिर मैं कम संख्या में क्वेरी के साथ ऐप के लिए एक प्रतियोगी फ़ाइल में कुछ "क्वेरी उड़ी स्लॉट" बना सकता हूं। मैं रनटाइम में DB रिकॉर्ड सम्मिलित करता हूं और यह काम लगता है लेकिन मुझे अभी भी संदेह है कि यह अच्छा अभ्यास है। कोई उपाय?
युंग

मैं इस विधि का उपयोग कर रहा हूं @Yeung का सुझाव और सब कुछ काम करता है, जिसमें डेटाबेस अपडेट पर कर्सर का स्वत: पुनः लोड करना शामिल है।
डेविड एचएच

यह किसी भी unregisterContentObserver की जरूरत नहीं है?
जीपैक

2

तिमो ओह्र द्वारा प्रस्तावित तीसरा विकल्प, येओंग द्वारा टिप्पणियों के साथ मिलकर, सबसे सरल उत्तर प्रदान करता है (ओकाम का उस्तरा)। नीचे एक पूर्ण वर्ग का एक उदाहरण है जो मेरे लिए काम करता है। इस वर्ग का उपयोग करने के दो नियम हैं।

  1. इस अमूर्त वर्ग को बढ़ाएँ और तरीके लागू करें getCursor () और getContentUri ()।
  2. किसी भी समय अंतर्निहित डेटाबेस बदलता है (जैसे, सम्मिलित या हटाने के बाद), कॉल करना सुनिश्चित करें

    getContentResolver().notifyChange(myUri, null);

    जहां myUri वही है जो आपके तरीके के कार्यान्वयन से लौटा है getContentUri ()।

यहाँ उस वर्ग का कोड दिया गया है जिसका मैंने उपयोग किया था:

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();
  }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.