कॉन्टेंट देने वाले के बारे में बुनियादी बातें

कॉन्टेंट प्रोवाइडर, डेटा के सेंट्रल रिपॉज़िटरी का ऐक्सेस मैनेज करता है. सेवा देने वाली कंपनी, ऐसे Android ऐप्लिकेशन का हिस्सा होती है जो डेटा के साथ काम करने के लिए अक्सर अपना खुद का यूज़र इंटरफ़ेस (यूआई) उपलब्ध कराता है. हालांकि, कॉन्टेंट देने वाली कंपनियों का इस्तेमाल मुख्य रूप से दूसरे ऐप्लिकेशन करते हैं. ये ऐप्लिकेशन, कॉन्टेंट देने वाली कंपनी के क्लाइंट ऑब्जेक्ट का इस्तेमाल करके, उसे ऐक्सेस करते हैं. डेटा के लिए, प्रोवाइडर और प्रोवाइडर क्लाइंट एक साथ मिलकर एक ऐसा स्टैंडर्ड इंटरफ़ेस उपलब्ध कराते हैं जो इंटरप्रोसेस कम्यूनिकेशन और डेटा को सुरक्षित तरीके से ऐक्सेस करने की सुविधा भी देता है.

आम तौर पर, कॉन्टेंट की सेवा देने वाली कंपनियों के साथ दो स्थितियों में काम किया जाता है: किसी दूसरे ऐप्लिकेशन में मौजूद कॉन्टेंट की सेवा देने वाली कंपनी को ऐक्सेस करने के लिए कोड लागू करना या अन्य ऐप्लिकेशन के साथ डेटा शेयर करने के लिए, अपने ऐप्लिकेशन में कॉन्टेंट की सेवा देने वाली नई कंपनी बनाना.

इस पेज पर, कॉन्टेंट उपलब्ध कराने वाली मौजूदा कंपनियों के साथ काम करने के बारे में बुनियादी जानकारी दी गई है. अपने ऐप्लिकेशन में कॉन्टेंट देने वालों को लागू करने के बारे में जानने के लिए, कॉन्टेंट देने वाला बनाएं देखें.

यह विषय इन चीज़ों के बारे में बताता है:

  • कॉन्टेंट देने वाले लोग या कंपनियां कैसे काम करती हैं.
  • यह एक ऐसा एपीआई है जिसका इस्तेमाल, कॉन्टेंट उपलब्ध कराने वाली कंपनी से डेटा पाने के लिए किया जाता है.
  • वह एपीआई जिसका इस्तेमाल, कॉन्टेंट देने वाले किसी प्लैटफ़ॉर्म में डेटा डालने, उसे अपडेट करने या मिटाने के लिए किया जाता है.
  • एपीआई की अन्य सुविधाएं, जो सेवा देने वाली कंपनियों के साथ काम करने में मदद करती हैं.

खास जानकारी

कॉन्टेंट उपलब्ध कराने वाला, बाहरी ऐप्लिकेशन को डेटा को एक या उससे ज़्यादा टेबल के तौर पर दिखाता है. ये टेबल, रिलेशनल डेटाबेस में मौजूद टेबल की तरह होती हैं. कोई पंक्ति, डेटा के किसी ऐसे इंस्टेंस को दिखाती है जिसे डेटा उपलब्ध कराने वाली कंपनी इकट्ठा करती है. साथ ही, पंक्ति का हर कॉलम, किसी इंस्टेंस के लिए इकट्ठा किए गए डेटा के अलग-अलग हिस्से को दिखाता है.

कॉन्टेंट प्रोवाइडर, कई अलग-अलग एपीआई और कॉम्पोनेंट के लिए, आपके ऐप्लिकेशन में डेटा स्टोरेज लेयर को ऐक्सेस करने का समन्वय करता है. जैसा कि पहले चित्र में दिखाया गया है, इनमें ये चीज़ें शामिल हैं:

  • अपने ऐप्लिकेशन के डेटा का ऐक्सेस, दूसरे ऐप्लिकेशन के साथ शेयर करना
  • किसी विजेट पर डेटा भेजना
  • SearchRecentSuggestionsProvider का इस्तेमाल करके खोज फ़्रेमवर्क के ज़रिए, आपके ऐप्लिकेशन के लिए पसंद के मुताबिक खोज सुझाव लौटाना
  • AbstractThreadedSyncAdapter को लागू करने के तरीके का इस्तेमाल करके, ऐप्लिकेशन डेटा को अपने सर्वर से सिंक करना
  • CursorLoader का इस्तेमाल करके, अपने यूज़र इंटरफ़ेस (यूआई) में डेटा लोड करना
कॉन्टेंट उपलब्ध कराने वाले और दूसरे कॉम्पोनेंट के बीच का संबंध.

पहली इमेज. कॉन्टेंट उपलब्ध कराने वाले और दूसरे कॉम्पोनेंट के बीच का संबंध.

सेवा देने वाली कंपनी को ऐक्सेस करना

जब आपको किसी कॉन्टेंट देने वाले के डेटा को ऐक्सेस करना हो, तो सेवा देने वाली कंपनी से क्लाइंट के तौर पर संपर्क करने के लिए, अपने ऐप्लिकेशन के Context में ContentResolver ऑब्जेक्ट का इस्तेमाल करें. ContentResolver ऑब्जेक्ट, प्रोवाइडर ऑब्जेक्ट से कम्यूनिकेट करता है. यह, ContentProvider को लागू करने वाली क्लास का एक इंस्टेंस है.

सेवा देने वाला ऑब्जेक्ट को क्लाइंट से डेटा के अनुरोध मिलते हैं, वह अनुरोध की गई कार्रवाई करता है, और नतीजे दिखाता है. इस ऑब्जेक्ट में ऐसे तरीके हैं जो प्रोवाइडर ऑब्जेक्ट में एक जैसे नाम वाले तरीकों को कॉल करते हैं. ये तरीके, ContentProvider के किसी एक सबक्लास के इंस्टेंस होते हैं. ContentResolver के तरीके, पर्सिस्टेंट स्टोरेज के लिए "सीआरयूडी" (बनाएं, वापस पाएं, अपडेट करें, और मिटाएं) फ़ंक्शन के बुनियादी फ़ंक्शन उपलब्ध कराते हैं.

अपने यूज़र इंटरफ़ेस (यूआई) से ContentProvider को ऐक्सेस करने के लिए, आम तौर पर बैकग्राउंड में एसिंक्रोनस क्वेरी चलाने के लिए, CursorLoader का इस्तेमाल किया जाता है. आपके यूज़र इंटरफ़ेस (यूआई) में मौजूद Activity या Fragment, क्वेरी के लिए CursorLoader को कॉल करता है. इसके बाद, ContentResolver का इस्तेमाल करके ContentProvider मिलता है.

इससे, क्वेरी चलने के दौरान यूज़र इंटरफ़ेस (यूआई) उपयोगकर्ता के लिए उपलब्ध रहता है. इस पैटर्न में, कई अलग-अलग ऑब्जेक्ट के साथ-साथ स्टोरेज के तरीके का इंटरैक्शन शामिल होता है, जैसा कि दूसरे चित्र में दिखाया गया है.

ContentProvider, अन्य क्लास, और स्टोरेज के बीच इंटरैक्शन.

दूसरी इमेज. ContentProvider, अन्य क्लास, और स्टोरेज के बीच इंटरैक्शन.

ध्यान दें: सेवा देने वाली किसी कंपनी का ऐक्सेस पाने के लिए, आपके ऐप्लिकेशन को आम तौर पर उसकी मेनिफ़ेस्ट फ़ाइल में खास अनुमतियों का अनुरोध करना पड़ता है. इस डेवलपमेंट पैटर्न के बारे में ज़्यादा जानकारी, कॉन्टेंट देने वाले की अनुमतियां सेक्शन में दी गई है.

Android प्लैटफ़ॉर्म में पहले से मौजूद सेवाओं में से एक, उपयोगकर्ता डिक्शनरी प्रोवाइडर है. यह उन गैर-स्टैंडर्ड शब्दों को सेव करता है जिन्हें उपयोगकर्ता अपने पास रखना चाहता है. टेबल 1 में बताया गया है कि इस सेवा देने वाली कंपनी की टेबल में डेटा कैसा दिख सकता है:

पहली टेबल: उपयोगकर्ता डिक्शनरी की सैंपल टेबल.

शब्दों वाले गेम ऐप्स id फ़्रीक्वेंसी स्थान-भाषा _ID
mapreduce user1 100 en_US 1
precompiler user14 200 fr_FR 2
applet user2 225 fr_CA 3
const user1 255 pt_BR 4
int user5 100 en_UK 5

पहली टेबल में, हर पंक्ति ऐसे शब्द के उदाहरण को दिखाती है जो स्टैंडर्ड शब्दकोश में मौजूद नहीं है. हर कॉलम, उस शब्द के लिए डेटा का एक हिस्सा दिखाता है. जैसे, वह भाषा जिसमें वह शब्द पहली बार मिला था. कॉलम हेडर, कॉलम के ऐसे नाम होते हैं जिन्हें प्रोवाइडर में सेव किया जाता है. उदाहरण के लिए, किसी लाइन की स्थानीय भाषा का रेफ़रंस देने के लिए, उसके locale कॉलम का रेफ़रंस दिया जाता है. सेवा देने वाली इस कंपनी के लिए, _ID कॉलम एक प्राइमरी पासकोड कॉलम की तरह काम करता है. इसे सेवा देने वाली कंपनी अपने-आप मैनेज करती है.

उपयोगकर्ता के लिए डिक्शनरी की सेवा देने वाली कंपनी से शब्दों और उनकी स्थानीय भाषाओं की सूची पाने के लिए, ContentResolver.query() को कॉल करें. query() तरीका, उपयोगकर्ता शब्दकोश की सेवा देने वाली कंपनी के तय किए गए ContentProvider.query() तरीके को कॉल करता है. कोड की ये लाइनें एक ContentResolver.query() कॉल दिखाती हैं:

Kotlin

// Queries the UserDictionary and returns results
cursor = contentResolver.query(
        UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
        projection,                        // The columns to return for each row
        selectionClause,                   // Selection criteria
        selectionArgs.toTypedArray(),      // Selection criteria
        sortOrder                          // The sort order for the returned rows
)

Java

// Queries the UserDictionary and returns results
cursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
    projection,                        // The columns to return for each row
    selectionClause,                   // Selection criteria
    selectionArgs,                     // Selection criteria
    sortOrder);                        // The sort order for the returned rows

टेबल 2 में दिखाया गया है कि query(Uri,projection,selection,selectionArgs,sortOrder) के लिए दिए गए आर्ग्युमेंट, SQL SELECT स्टेटमेंट से कैसे मैच करते हैं:

दूसरी टेबल: एसक्यूएल क्वेरी की तुलना में query().

query() आर्ग्युमेंट SELECT कीवर्ड/पैरामीटर नोट
Uri FROM table_name Uri, table_name नाम की कंपनी की टेबल से मैप होता है.
projection col,col,col,... projection, कॉलम का कलेक्शन है. इसे वापस लाई गई हर पंक्ति के लिए शामिल किया जाता है.
selection WHERE col = value selection, पंक्तियों को चुनने की शर्तों के बारे में बताता है.
selectionArgs कोई एग्ज़ैक्ट मिलता-जुलता विकल्प नहीं है. चुनने के आर्ग्युमेंट, चुनने के क्लॉज़ में ? प्लेसहोल्डर की जगह लेते हैं.
sortOrder ORDER BY col,col,... sortOrder से यह तय होता है कि Cursor में पंक्तियां किस क्रम में दिखेंगी.

कॉन्टेंट यूआरआई

कॉन्टेंट यूआरआई एक ऐसा यूआरआई है जो किसी प्रोवाइडर में मौजूद डेटा की पहचान करता है. कॉन्टेंट के यूआरआई में, सेवा देने वाली पूरी कंपनी का सिम्बॉलिक नाम होता है. इसका मतलब है कि वह यूआरएल— और एक ऐसा नाम जो टेबल पर ले जाता है—एक पाथ. जब किसी प्रोवाइडर में टेबल को ऐक्सेस करने के लिए क्लाइंट का कोई तरीका कॉल किया जाता है, तो टेबल का कॉन्टेंट यूआरआई, एक आर्ग्युमेंट होता है.

कोड की पिछली लाइनों में, स्थिर वैल्यू CONTENT_URI में उपयोगकर्ता की डिक्शनरी देने वाली कंपनी की Words टेबल का कॉन्टेंट यूआरआई शामिल है. ContentResolver ऑब्जेक्ट, यूआरआई के एथॉरिटी को पार्स करता है और इसका इस्तेमाल करके, एथॉरिटी की तुलना, पहचाने गए प्रोवाइडर की सिस्टम टेबल से करके, प्रोवाइडर की पहचान करता है. इसके बाद, ContentResolver, क्वेरी के आर्ग्युमेंट को सेवा देने वाली सही कंपनी को भेज सकता है.

ContentProvider, कॉन्टेंट यूआरआई के पाथ वाले हिस्से का इस्तेमाल करके, टेबल को ऐक्सेस करता है. आम तौर पर, सेवा देने वाली कंपनी के पास हर उस टेबल का पाथ होता है जिसे वह एक्सपोज़ करती है.

कोड की पिछली लाइनों में, Words टेबल का पूरा यूआरआई यह है:

content://user_dictionary/words
  • content:// स्ट्रिंग एक स्कीम होती है, जो हमेशा मौजूद होती है और इसकी पहचान कॉन्टेंट यूआरआई के तौर पर करती है.
  • user_dictionary स्ट्रिंग, सेवा देने वाली कंपनी की पहचान है.
  • words स्ट्रिंग, टेबल का पाथ है.

कई सेवा देने वाली कंपनियां, यूआरआई के आखिर में आईडी वैल्यू जोड़कर, टेबल में मौजूद किसी एक पंक्ति को ऐक्सेस करने की सुविधा देती हैं. उदाहरण के लिए, उपयोगकर्ता डिक्शनरी प्रोवाइडर से वह पंक्ति वापस पाने के लिए जिसका _ID 4 है, इस कॉन्टेंट यूआरआई का इस्तेमाल किया जा सकता है:

Kotlin

val singleUri: Uri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI, 4)

Java

Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

पंक्तियों का सेट वापस पाने और फिर उनमें से किसी एक को अपडेट करने या मिटाने के लिए, अक्सर आईडी वैल्यू का इस्तेमाल किया जाता है.

ध्यान दें: Uri और Uri.Builder क्लास में, स्ट्रिंग से अच्छी तरह से बने यूआरआई ऑब्जेक्ट बनाने के आसान तरीके शामिल होते हैं. ContentUris क्लास में, यूआरआई में आईडी वैल्यू जोड़ने के लिए आसानी से इस्तेमाल किए जा सकने वाले मेथड होते हैं. पिछला स्निपेट, यूज़र डिक्शनरी प्रोवाइडर के कॉन्टेंट यूआरआई में एक आईडी जोड़ने के लिए withAppendedId() का इस्तेमाल करता है.

सेवा देने वाली कंपनी से डेटा वापस पाना

इस सेक्शन में, उदाहरण के तौर पर उपयोगकर्ता की डिक्शनरी से जुड़ी सेवा देने वाली कंपनी का इस्तेमाल करके, किसी सेवा देने वाली कंपनी से डेटा हासिल करने का तरीका बताया गया है.

साफ़ तौर पर बताने के लिए, इस सेक्शन में मौजूद कोड स्निपेट, यूज़र इंटरफ़ेस (यूआई) थ्रेड पर ContentResolver.query() को कॉल करते हैं. हालांकि, असल कोड में, अलग थ्रेड पर असाइनोक्रोनस तरीके से क्वेरी करें. CursorLoader क्लास का इस्तेमाल किया जा सकता है. इस बारे में ज़्यादा जानकारी, लोडर गाइड में दी गई है. साथ ही, कोड की लाइनें सिर्फ़ स्निपेट होती हैं. ये पूरी तरह से ऐप्लिकेशन नहीं दिखाते.

किसी कंपनी से डेटा वापस पाने के लिए, यह तरीका अपनाएं:

  1. सेवा देने वाली कंपनी के लिए, पढ़ने का ऐक्सेस देने का अनुरोध करें.
  2. वह कोड तय करें जो सेवा देने वाली कंपनी को क्वेरी भेजता है.

पढ़ने की अनुमति का अनुरोध करना

सेवा देने वाली किसी कंपनी से डेटा पाने के लिए, आपके ऐप्लिकेशन को उस कंपनी के डेटा को पढ़ने की अनुमति चाहिए. रनटाइम के दौरान, इस अनुमति का अनुरोध नहीं किया जा सकता. इसके बजाय, आपको यह बताना होगा कि आपको अपने मेनिफ़ेस्ट में यह अनुमति चाहिए. इसके लिए, <uses-permission> एलिमेंट और सेवा देने वाली कंपनी के तय किए गए अनुमति के सटीक नाम का इस्तेमाल करें.

इस एलिमेंट को अपने मेनिफ़ेस्ट में शामिल करने का मतलब है कि आपने अपने ऐप्लिकेशन के लिए इस अनुमति का अनुरोध किया है. जब उपयोगकर्ता आपका ऐप्लिकेशन इंस्टॉल करते हैं, तो वे इस अनुरोध को चुपचाप स्वीकार कर लेते हैं.

आप जिस सेवा देने वाली कंपनी का इस्तेमाल कर रहे हैं उसके लिए, पढ़ने का ऐक्सेस देने की अनुमति का सटीक नाम जानने के साथ-साथ, सेवा देने वाली कंपनी की ओर से इस्तेमाल की जाने वाली दूसरी ऐक्सेस अनुमतियों के नाम देखने के लिए, कंपनी का दस्तावेज़ देखें.

सेवा देने वाली कंपनियों को ऐक्सेस करने में अनुमतियों की भूमिका के बारे में ज़्यादा जानकारी, कॉन्टेंट देने वाली कंपनी से जुड़ी अनुमतियां सेक्शन में दी गई है.

उपयोगकर्ता शब्दकोश की सेवा देने वाली कंपनी, अपनी मेनिफ़ेस्ट फ़ाइल में अनुमति android.permission.READ_USER_DICTIONARY तय करती है. इसलिए, सेवा देने वाली कंपनी से पढ़ने के लिए, ऐप्लिकेशन को इस अनुमति का अनुरोध करना होगा.

क्वेरी बनाएं

सेवा देने वाली कंपनी से डेटा वापस पाने के लिए अगला कदम है क्वेरी बनाना. यहां दिए गए स्निपेट में, उपयोगकर्ता के डिक्शनरी प्रोवाइडर को ऐक्सेस करने के लिए कुछ वैरिएबल तय किए गए हैं:

Kotlin

// A "projection" defines the columns that are returned for each row
private val mProjection: Array<String> = arrayOf(
        UserDictionary.Words._ID,    // Contract class constant for the _ID column name
        UserDictionary.Words.WORD,   // Contract class constant for the word column name
        UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
)

// Defines a string to contain the selection clause
private var selectionClause: String? = null

// Declares an array to contain selection arguments
private lateinit var selectionArgs: Array<String>

Java

// A "projection" defines the columns that are returned for each row
String[] mProjection =
{
    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
    UserDictionary.Words.WORD,   // Contract class constant for the word column name
    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
};

// Defines a string to contain the selection clause
String selectionClause = null;

// Initializes an array to contain selection arguments
String[] selectionArgs = {""};

अगले स्निपेट में, ContentResolver.query() को इस्तेमाल करने का तरीका बताया गया है. इसके लिए, उदाहरण के तौर पर उपयोगकर्ता डिक्शनरी उपलब्ध कराने वाले टूल का इस्तेमाल किया गया है. प्रोवाइडर क्लाइंट क्वेरी, SQL क्वेरी जैसी होती है. इसमें, दिखाए जाने वाले कॉलम का सेट, चुनने की शर्तों का सेट, और क्रम से लगाने का क्रम होता है.

क्वेरी से मिलने वाले कॉलम के सेट को प्रोजेक्शन कहा जाता है और वैरिएबल को mProjection कहा जाता है.

एक्सप्रेशन में, पंक्तियों को चुनने के लिए, चुनने के नियम और चुनने के लिए पैरामीटर का इस्तेमाल किया जाता है. सिलेक्शन क्लॉज़, लॉजिकल और बूलियन एक्सप्रेशन, कॉलम के नाम, और वैल्यू का कॉम्बिनेशन होता है. वैरिएबल mSelectionClause है. अगर आपने वैल्यू के बजाय, वैल्यू बदलने वाला पैरामीटर ? डाला है, तो क्वेरी का तरीका, चुनने की शर्तों के ऐरे से वैल्यू हासिल करता है. यह वैरिएबल mSelectionArgs होता है.

अगले स्निपेट में, अगर उपयोगकर्ता कोई शब्द नहीं डालता है, तो चुनने का क्लॉज़ null पर सेट होता है और क्वेरी, प्रोवाइडर में मौजूद सभी शब्द दिखाती है. अगर उपयोगकर्ता कोई शब्द डालता है, तो चुनने का क्लॉज़ UserDictionary.Words.WORD + " = ?" पर सेट हो जाता है और चुनने के लिए दिए गए आर्ग्युमेंट के ऐरे का पहला एलिमेंट, उपयोगकर्ता के डाले गए शब्द पर सेट हो जाता है.

Kotlin

/*
 * This declares a String array to contain the selection arguments.
 */
private lateinit var selectionArgs: Array<String>

// Gets a word from the UI
searchString = searchWord.text.toString()

// Insert code here to check for invalid or malicious input

// If the word is the empty string, gets everything
selectionArgs = searchString?.takeIf { it.isNotEmpty() }?.let {
    selectionClause = "${UserDictionary.Words.WORD} = ?"
    arrayOf(it)
} ?: run {
    selectionClause = null
    emptyArray<String>()
}

// Does a query against the table and returns a Cursor object
mCursor = contentResolver.query(
        UserDictionary.Words.CONTENT_URI, // The content URI of the words table
        projection,                       // The columns to return for each row
        selectionClause,                  // Either null or the word the user entered
        selectionArgs,                    // Either empty or the string the user entered
        sortOrder                         // The sort order for the returned rows
)

// Some providers return null if an error occurs, others throw an exception
when (mCursor?.count) {
    null -> {
        /*
         * Insert code here to handle the error. Be sure not to use the cursor!
         * You might want to call android.util.Log.e() to log this error.
         */
    }
    0 -> {
        /*
         * Insert code here to notify the user that the search is unsuccessful. This isn't
         * necessarily an error. You might want to offer the user the option to insert a new
         * row, or re-type the search term.
         */
    }
    else -> {
        // Insert code here to do something with the results
    }
}

Java

/*
 * This defines a one-element String array to contain the selection argument.
 */
String[] selectionArgs = {""};

// Gets a word from the UI
searchString = searchWord.getText().toString();

// Remember to insert code here to check for invalid or malicious input

// If the word is the empty string, gets everything
if (TextUtils.isEmpty(searchString)) {
    // Setting the selection clause to null returns all words
    selectionClause = null;
    selectionArgs[0] = "";

} else {
    // Constructs a selection clause that matches the word that the user entered
    selectionClause = UserDictionary.Words.WORD + " = ?";

    // Moves the user's input string to the selection arguments
    selectionArgs[0] = searchString;

}

// Does a query against the table and returns a Cursor object
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI, // The content URI of the words table
    projection,                       // The columns to return for each row
    selectionClause,                  // Either null or the word the user entered
    selectionArgs,                    // Either empty or the string the user entered
    sortOrder);                       // The sort order for the returned rows

// Some providers return null if an error occurs, others throw an exception
if (null == mCursor) {
    /*
     * Insert code here to handle the error. Be sure not to use the cursor! You can
     * call android.util.Log.e() to log this error.
     *
     */
// If the Cursor is empty, the provider found no matches
} else if (mCursor.getCount() < 1) {

    /*
     * Insert code here to notify the user that the search is unsuccessful. This isn't necessarily
     * an error. You can offer the user the option to insert a new row, or re-type the
     * search term.
     */

} else {
    // Insert code here to do something with the results

}

यह क्वेरी, नीचे दिए गए SQL स्टेटमेंट से मिलती-जुलती है:

SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;

इस SQL स्टेटमेंट में, कॉन्ट्रैक्ट क्लास के कॉन्स्टेंट के बजाय, कॉलम के असल नामों का इस्तेमाल किया गया है.

नुकसान पहुंचाने वाले इनपुट से सुरक्षा

अगर कॉन्टेंट प्रोवाइडर के मैनेज किए जा रहे डेटा को एसक्यूएल डेटाबेस में रखा गया है, तो रॉ एसक्यूएल स्टेटमेंट में बाहरी और भरोसेमंद न होने वाले डेटा को शामिल करने पर, एसक्यूएल इंजेक्शन हो सकता है.

चुनने के लिए यह क्लॉज़ देखें:

Kotlin

// Constructs a selection clause by concatenating the user's input to the column name
var selectionClause = "var = $mUserInput"

Java

// Constructs a selection clause by concatenating the user's input to the column name
String selectionClause = "var = " + userInput;

ऐसा करने पर, उपयोगकर्ता आपके एसक्यूएल स्टेटमेंट में नुकसान पहुंचाने वाले एसक्यूएल को जोड़ सकता है. उदाहरण के लिए, उपयोगकर्ता mUserInput के लिए "nothing; DROP TABLE *;" डाल सकता है. इससे, सिलेक्शन क्लॉज़ var = nothing; DROP TABLE *; बन जाता है.

सेलेक्शन क्लॉज़ को एसक्यूएल स्टेटमेंट के तौर पर माना जाता है. इसलिए, हो सकता है कि प्रोवाइडर, SQLite डेटाबेस में मौजूद सभी टेबल मिटा दे. ऐसा तब तक होगा, जब तक प्रोवाइडर को एसक्यूएल इंजेक्शन के प्रयासों को रोकने के लिए सेट अप नहीं किया जाता.

इस समस्या से बचने के लिए, ऐसे सेलेक्शन क्लॉज़ का इस्तेमाल करें जिसमें ? का इस्तेमाल, बदले जा सकने वाले पैरामीटर और चुनने की शर्तों के अलग-अलग कलेक्शन के तौर पर किया गया हो. इस तरह, उपयोगकर्ता का इनपुट एसक्यूएल स्टेटमेंट के हिस्से के तौर पर इंटरप्रेट करने के बजाय, सीधे क्वेरी से जुड़ा होता है. इसे SQL के तौर पर नहीं माना जाता, इसलिए उपयोगकर्ता इनपुट से नुकसान पहुंचाने वाला SQL इंजेक्ट नहीं किया जा सकता. उपयोगकर्ता के इनपुट को शामिल करने के लिए, कनकैनेटेशन का इस्तेमाल करने के बजाय, इस सिलेक्शन क्लॉज़ का इस्तेमाल करें:

Kotlin

// Constructs a selection clause with a replaceable parameter
var selectionClause = "var = ?"

Java

// Constructs a selection clause with a replaceable parameter
String selectionClause =  "var = ?";

चुनने की शर्तों के ऐरे को इस तरह सेट अप करें:

Kotlin

// Defines a mutable list to contain the selection arguments
var selectionArgs: MutableList<String> = mutableListOf()

Java

// Defines an array to contain the selection arguments
String[] selectionArgs = {""};

चुनिंदा आर्ग्युमेंट कलेक्शन में वैल्यू इस तरह डालें:

Kotlin

// Adds the user's input to the selection argument
selectionArgs += userInput

Java

// Sets the selection argument to the user's input
selectionArgs[0] = userInput;

एक सेलेक्शन क्लॉज़, जिसमें ? का इस्तेमाल बदले जा सकने वाले पैरामीटर के रूप में किया जाता है और आर्ग्युमेंट के कलेक्शन की कलेक्शन, किसी चुने गए विकल्प के बारे में बताने का पसंदीदा तरीका है. सेवा देने वाली कंपनी, एसक्यूएल डेटाबेस के आधार पर न हो.

क्वेरी के नतीजे दिखाना

ContentResolver.query() क्लाइंट तरीका हमेशा ऐसा Cursor दिखाता है जिसमें क्वेरी के प्रोजेक्शन में, उन पंक्तियों के कॉलम शामिल होते हैं जो क्वेरी की चुनी गई शर्तों से मेल खाती हैं. Cursor ऑब्जेक्ट, उसमें मौजूद पंक्तियों और कॉलम को बिना किसी क्रम के पढ़ने का ऐक्सेस देता है.

Cursor तरीकों का इस्तेमाल करके, नतीजों में मौजूद लाइनों को दोहराया जा सकता है. साथ ही, हर कॉलम के डेटा टाइप का पता लगाया जा सकता है, किसी कॉलम से डेटा पाया जा सकता है, और नतीजों की अन्य प्रॉपर्टी की जांच की जा सकती है.

Cursor के कुछ लागू होने के तरीके, प्रोवाइडर के डेटा में बदलाव होने पर ऑब्जेक्ट को अपने-आप अपडेट करते हैं. साथ ही, Cursor में बदलाव होने पर ऑब्ज़र्वर ऑब्जेक्ट में तरीके ट्रिगर करते हैं या दोनों काम करते हैं.

ध्यान दें: सेवा देने वाली कंपनी, क्वेरी बनाने वाले ऑब्जेक्ट के हिसाब से कॉलम के ऐक्सेस पर पाबंदी लगा सकती है. उदाहरण के लिए, Contacts Provider, कुछ कॉलम के ऐक्सेस पर पाबंदी लगाता है, ताकि वे सिंक करने वाले अडैप्टर के लिए उपलब्ध न हों. इससे, उन्हें किसी गतिविधि या सेवा के लिए वापस नहीं लाया जाता.

अगर कोई भी पंक्ति चुनने की शर्त से मेल नहीं खाती, तो सेवा देने वाली कंपनी एक Cursor ऑब्जेक्ट दिखाती है, जिसके लिए Cursor.getCount(), 0 है. इसका मतलब है कि एक खाली कर्सर है.

अगर कोई अंदरूनी गड़बड़ी होती है, तो क्वेरी के नतीजे, सेवा देने वाली कंपनी पर निर्भर करते हैं. यह null दिखा सकता है या Exception दिखा सकता है.

Cursor, पंक्तियों की सूची होती है. इसलिए, Cursor के कॉन्टेंट को दिखाने का एक अच्छा तरीका यह है कि SimpleCursorAdapter का इस्तेमाल करके, Cursor को ListView से लिंक करें.

नीचे दिया गया स्निपेट, पिछले स्निपेट के कोड को जारी रखता है. यह एक ऐसा SimpleCursorAdapter ऑब्जेक्ट बनाता है जिसमें क्वेरी से हासिल किया गया Cursor होता है. साथ ही, इस ऑब्जेक्ट को ListView के लिए अडैप्टर के तौर पर सेट करता है.

Kotlin

// Defines a list of columns to retrieve from the Cursor and load into an output row
val wordListColumns : Array<String> = arrayOf(
        UserDictionary.Words.WORD,      // Contract class constant containing the word column name
        UserDictionary.Words.LOCALE     // Contract class constant containing the locale column name
)

// Defines a list of View IDs that receive the Cursor columns for each row
val wordListItems = intArrayOf(R.id.dictWord, R.id.locale)

// Creates a new SimpleCursorAdapter
cursorAdapter = SimpleCursorAdapter(
        applicationContext,             // The application's Context object
        R.layout.wordlistrow,           // A layout in XML for one row in the ListView
        mCursor,                        // The result from the query
        wordListColumns,                // A string array of column names in the cursor
        wordListItems,                  // An integer array of view IDs in the row layout
        0                               // Flags (usually none are needed)
)

// Sets the adapter for the ListView
wordList.setAdapter(cursorAdapter)

Java

// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] wordListColumns =
{
    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
};

// Defines a list of View IDs that receive the Cursor columns for each row
int[] wordListItems = { R.id.dictWord, R.id.locale};

// Creates a new SimpleCursorAdapter
cursorAdapter = new SimpleCursorAdapter(
    getApplicationContext(),               // The application's Context object
    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
    mCursor,                               // The result from the query
    wordListColumns,                       // A string array of column names in the cursor
    wordListItems,                         // An integer array of view IDs in the row layout
    0);                                    // Flags (usually none are needed)

// Sets the adapter for the ListView
wordList.setAdapter(cursorAdapter);

ध्यान दें: ListView को Cursor पर वापस ले जाने के लिए, कर्सर में _ID नाम का कॉलम होना चाहिए. इस वजह से, पहले दिखाई गई क्वेरी, Words टेबल के लिए _ID कॉलम को फिर से हासिल करती है. हालांकि, ListView में वह कॉलम नहीं दिखता है. इस पाबंदी से यह भी पता चलता है कि ज़्यादातर डेटा प्रोवाइडर की हर टेबल में _ID कॉलम क्यों होता है.

क्वेरी के नतीजों से डेटा हासिल करना

क्वेरी के नतीजे दिखाने के अलावा, इनका इस्तेमाल अन्य कामों के लिए भी किया जा सकता है. उदाहरण के लिए, उपयोगकर्ता के डिक्शनरी प्रोवाइडर से वर्तनी वापस पाई जा सकती है और फिर उन्हें अन्य प्रोवाइडर में खोजा जा सकता है. ऐसा करने के लिए, Cursor में पंक्तियों को दोहराएं, जैसा कि इस उदाहरण में दिखाया गया है:

Kotlin

/*
* Only executes if the cursor is valid. The User Dictionary Provider returns null if
* an internal error occurs. Other providers might throw an Exception instead of returning null.
*/
mCursor?.apply {
    // Determine the column index of the column named "word"
    val index: Int = getColumnIndex(UserDictionary.Words.WORD)

    /*
     * Moves to the next row in the cursor. Before the first movement in the cursor, the
     * "row pointer" is -1, and if you try to retrieve data at that position you get an
     * exception.
     */
    while (moveToNext()) {
        // Gets the value from the column
        newWord = getString(index)

        // Insert code here to process the retrieved word
        ...
        // End of while loop
    }
}

Java

// Determine the column index of the column named "word"
int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);

/*
 * Only executes if the cursor is valid. The User Dictionary Provider returns null if
 * an internal error occurs. Other providers might throw an Exception instead of returning null.
 */

if (mCursor != null) {
    /*
     * Moves to the next row in the cursor. Before the first movement in the cursor, the
     * "row pointer" is -1, and if you try to retrieve data at that position you get an
     * exception.
     */
    while (mCursor.moveToNext()) {

        // Gets the value from the column
        newWord = mCursor.getString(index);

        // Insert code here to process the retrieved word
        ...
        // End of while loop
    }
} else {

    // Insert code here to report an error if the cursor is null or the provider threw an exception
}

Cursor लागू करने के लिए, ऑब्जेक्ट से अलग-अलग तरह के डेटा को फिर से पाने के लिए कई "get" तरीके होते हैं. उदाहरण के लिए, पिछले स्निपेट में getString() का इस्तेमाल किया गया है. इनमें एक getType() तरीका भी होता है, जो कॉलम के डेटा टाइप की जानकारी देने वाली वैल्यू दिखाता है.

क्वेरी के नतीजे के रिसॉर्स रिलीज़ करना

अगर Cursor ऑब्जेक्ट की अब ज़रूरत नहीं है, तो उन्हें बंद कर दिया जाना चाहिए, ताकि उनसे जुड़े संसाधन जल्द रिलीज़ किए जा सकें. ऐसा करने के लिए, close() को कॉल करें या Java प्रोग्रामिंग लैंग्वेज में try-with-resources स्टेटमेंट या Kotlin प्रोग्रामिंग लैंग्वेज में use() फ़ंक्शन का इस्तेमाल करें.

कॉन्टेंट देने वाले ऐप्लिकेशन को दी जाने वाली अनुमतियां

सेवा देने वाली कंपनी का ऐप्लिकेशन, उन अनुमतियों के बारे में बता सकता है जो सेवा देने वाली कंपनी के डेटा को ऐक्सेस करने के लिए, अन्य ऐप्लिकेशन के पास होनी चाहिए. इन अनुमतियों से उपयोगकर्ता को यह पता चलता है कि कोई ऐप्लिकेशन कौनसा डेटा ऐक्सेस करने की कोशिश कर रहा है. सेवा देने वाली कंपनी की ज़रूरी शर्तों के आधार पर, अन्य ऐप्लिकेशन सेवा देने वाली कंपनी को ऐक्सेस करने के लिए, ज़रूरी अनुमतियों का अनुरोध करते हैं. असली उपयोगकर्ताओं को ऐप्लिकेशन इंस्टॉल करते समय, अनुरोध की गई अनुमतियां दिखती हैं.

अगर सेवा देने वाली कंपनी का ऐप्लिकेशन किसी अनुमति के बारे में नहीं बताता है, तो दूसरे ऐप्लिकेशन के पास सेवा देने वाली कंपनी के डेटा का ऐक्सेस तब तक नहीं होता, जब तक सेवा देने वाली कंपनी को एक्सपोर्ट नहीं किया जाता. इसके अलावा, सेवा देने वाले के ऐप्लिकेशन के कॉम्पोनेंट के पास हमेशा पढ़ने और लिखने का पूरा ऐक्सेस होता है, चाहे दी गई अनुमतियां कुछ भी हों.

उपयोगकर्ता के डिक्शनरी प्रोवाइडर को, इससे डेटा पाने के लिए android.permission.READ_USER_DICTIONARY की अनुमति चाहिए. डेटा डालने, अपडेट करने या मिटाने के लिए, सेवा देने वाली कंपनी के पास android.permission.WRITE_USER_DICTIONARY की अलग से अनुमति होती है.

किसी सेवा देने वाली कंपनी को ऐक्सेस करने के लिए ज़रूरी अनुमतियां पाने के लिए, ऐप्लिकेशन अपनी मेनिफ़ेस्ट फ़ाइल में <uses-permission> एलिमेंट का इस्तेमाल करके अनुरोध करता है. जब Android पैकेज मैनेजर इस ऐप्लिकेशन को इंस्टॉल करता है, तो उपयोगकर्ता को ऐप्लिकेशन के लिए अनुरोध की जाने वाली सभी अनुमतियों को मंज़ूरी देनी होगी. अगर उपयोगकर्ता उन्हें स्वीकार करता है, तो Package Manager इंस्टॉलेशन जारी रखता है. अगर उपयोगकर्ता उन्हें अनुमति नहीं देता है, तो Package Manager इंस्टॉलेशन को रोक देता है.

यहां दिए गए सैंपल <uses-permission> एलिमेंट में, उपयोगकर्ता के शब्दकोश की सेवा देने वाली कंपनी के डेटा को पढ़ने का अनुरोध किया गया है:

<uses-permission android:name="android.permission.READ_USER_DICTIONARY">

सेवा देने वाली कंपनी के ऐक्सेस पर अनुमतियों के असर के बारे में ज़्यादा जानकारी के लिए, सुरक्षा से जुड़े सुझाव देखें.

डेटा डालना, अपडेट करना, और मिटाना

डेटा में बदलाव करने के लिए, उसी तरह से सेवा देने वाली कंपनी के क्लाइंट और सेवा देने वाली कंपनी के ContentProvider के बीच इंटरैक्शन का इस्तेमाल किया जाता है जिस तरह से सेवा देने वाली कंपनी से डेटा हासिल किया जाता है. ContentResolver के किसी तरीके को उन आर्ग्युमेंट के साथ कॉल किया जाता है जिन्हें ContentProvider के उसी तरीके में पास किया जाता है. सेवा देने वाली कंपनी और उसका क्लाइंट, सुरक्षा और इंटरप्रोसेस कम्यूनिकेशन को अपने-आप मैनेज करता है.

डेटा डालना

किसी कंपनी में डेटा डालने के लिए, आपको ContentResolver.insert() तरीके का इस्तेमाल करना होगा. यह तरीका, कॉन्टेंट की जानकारी देने वाली कंपनी में एक नई लाइन जोड़ता है और उस लाइन के लिए कॉन्टेंट का यूआरआई दिखाता है. नीचे दिए गए स्निपेट में, उपयोगकर्ता शब्दकोश की सेवा देने वाली कंपनी में नया शब्द डालने का तरीका बताया गया है:

Kotlin

// Defines a new Uri object that receives the result of the insertion
lateinit var newUri: Uri
...
// Defines an object to contain the new values to insert
val newValues = ContentValues().apply {
    /*
     * Sets the values of each column and inserts the word. The arguments to the "put"
     * method are "column name" and "value".
     */
    put(UserDictionary.Words.APP_ID, "example.user")
    put(UserDictionary.Words.LOCALE, "en_US")
    put(UserDictionary.Words.WORD, "insert")
    put(UserDictionary.Words.FREQUENCY, "100")

}

newUri = contentResolver.insert(
        UserDictionary.Words.CONTENT_URI,   // The UserDictionary content URI
        newValues                           // The values to insert
)

Java

// Defines a new Uri object that receives the result of the insertion
Uri newUri;
...
// Defines an object to contain the new values to insert
ContentValues newValues = new ContentValues();

/*
 * Sets the values of each column and inserts the word. The arguments to the "put"
 * method are "column name" and "value".
 */
newValues.put(UserDictionary.Words.APP_ID, "example.user");
newValues.put(UserDictionary.Words.LOCALE, "en_US");
newValues.put(UserDictionary.Words.WORD, "insert");
newValues.put(UserDictionary.Words.FREQUENCY, "100");

newUri = getContentResolver().insert(
    UserDictionary.Words.CONTENT_URI,   // The UserDictionary content URI
    newValues                           // The values to insert
);

नई लाइन का डेटा, एक ContentValues ऑब्जेक्ट में जाता है, जो एक पंक्ति वाले कर्सर के फ़ॉर्म में होता है. इस ऑब्जेक्ट के कॉलम में एक ही तरह का डेटा टाइप होना ज़रूरी नहीं है. अगर आपको कोई वैल्यू नहीं देनी है, तो ContentValues.putNull() का इस्तेमाल करके किसी कॉलम को null पर सेट किया जा सकता है.

पिछला स्निपेट, _ID कॉलम नहीं जोड़ता, क्योंकि यह कॉलम अपने-आप मैनेज होता है. सेवा देने वाली कंपनी, जोड़ी गई हर लाइन के लिए _ID की एक यूनीक वैल्यू असाइन करती है. आम तौर पर, प्रोवाइडर इस वैल्यू का इस्तेमाल टेबल की प्राइमरी कुंजी के तौर पर करते हैं.

newUri में दिखाया गया कॉन्टेंट यूआरआई, जोड़ी गई नई पंक्ति की पहचान करने के लिए इस फ़ॉर्मैट का इस्तेमाल करता है:

content://user_dictionary/words/<id_value>

<id_value>, नई लाइन के लिए _ID का कॉन्टेंट है. ज़्यादातर सेवा देने वाली कंपनियां, कॉन्टेंट यूआरआई के इस फ़ॉर्म का पता अपने-आप लगा सकती हैं. इसके बाद, उस खास पंक्ति पर अनुरोध किया गया ऑपरेशन कर सकती हैं.

Uri से _ID की वैल्यू पाने के लिए, ContentUris.parseId() को कॉल करें.

डेटा अपडेट करना

किसी पंक्ति को अपडेट करने के लिए, अपडेट की गई वैल्यू के साथ ContentValues ऑब्जेक्ट का इस्तेमाल किया जाता है. ठीक उसी तरह जैसे किसी क्वेरी के साथ इंसर्शन और चुनने की शर्तों का इस्तेमाल किया जाता है. आपने क्लाइंट के लिए जो तरीका इस्तेमाल किया है वह ContentResolver.update() है. आपको सिर्फ़ उन कॉलम के लिए ContentValues ऑब्जेक्ट में वैल्यू जोड़नी हैं जिन्हें अपडेट किया जा रहा है. अगर आपको किसी कॉलम का कॉन्टेंट मिटाना है, तो वैल्यू को null पर सेट करें.

नीचे दिया गया स्निपेट, उन सभी पंक्तियों की जगह-भाषा को बदल देता है जिनकी जगह-भाषा "en" है और इसे null पर सेट कर देता है. रिटर्न वैल्यू, अपडेट की गई लाइनों की संख्या होती है.

Kotlin

// Defines an object to contain the updated values
val updateValues = ContentValues().apply {
    /*
     * Sets the updated value and updates the selected words.
     */
    putNull(UserDictionary.Words.LOCALE)
}

// Defines selection criteria for the rows you want to update
val selectionClause: String = UserDictionary.Words.LOCALE + "LIKE ?"
val selectionArgs: Array<String> = arrayOf("en_%")

// Defines a variable to contain the number of updated rows
var rowsUpdated: Int = 0
...
rowsUpdated = contentResolver.update(
        UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
        updateValues,                      // The columns to update
        selectionClause,                   // The column to select on
        selectionArgs                      // The value to compare to
)

Java

// Defines an object to contain the updated values
ContentValues updateValues = new ContentValues();

// Defines selection criteria for the rows you want to update
String selectionClause = UserDictionary.Words.LOCALE +  " LIKE ?";
String[] selectionArgs = {"en_%"};

// Defines a variable to contain the number of updated rows
int rowsUpdated = 0;
...
/*
 * Sets the updated value and updates the selected words.
 */
updateValues.putNull(UserDictionary.Words.LOCALE);

rowsUpdated = getContentResolver().update(
    UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
    updateValues,                      // The columns to update
    selectionClause,                   // The column to select on
    selectionArgs                      // The value to compare to
);

ContentResolver.update() को कॉल करते समय, उपयोगकर्ता के इनपुट को सुरक्षित करें. इस बारे में ज़्यादा जानने के लिए, मैलवेयर वाले इनपुट से सुरक्षा सेक्शन पढ़ें.

डेटा मिटाएं

पंक्तियों को मिटाने की प्रोसेस, पंक्तियों का डेटा वापस पाने की प्रोसेस जैसी ही होती है. आपको उन पंक्तियों के लिए चुनने की शर्तें तय करनी होती हैं जिन्हें मिटाना है. इसके बाद, क्लाइंट का तरीका मिटाई गई पंक्तियों की संख्या दिखाता है. नीचे दिया गया स्निपेट, उन पंक्तियों को मिटा देता है जिनका ऐप्लिकेशन आईडी "user" से मेल खाता है. यह तरीका, मिटाई गई पंक्तियों की संख्या दिखाता है.

Kotlin

// Defines selection criteria for the rows you want to delete
val selectionClause = "${UserDictionary.Words.APP_ID} LIKE ?"
val selectionArgs: Array<String> = arrayOf("user")

// Defines a variable to contain the number of rows deleted
var rowsDeleted: Int = 0
...
// Deletes the words that match the selection criteria
rowsDeleted = contentResolver.delete(
        UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
        selectionClause,                   // The column to select on
        selectionArgs                      // The value to compare to
)

Java

// Defines selection criteria for the rows you want to delete
String selectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
String[] selectionArgs = {"user"};

// Defines a variable to contain the number of rows deleted
int rowsDeleted = 0;
...
// Deletes the words that match the selection criteria
rowsDeleted = getContentResolver().delete(
    UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
    selectionClause,                   // The column to select on
    selectionArgs                      // The value to compare to
);

ContentResolver.delete() को कॉल करते समय, उपयोगकर्ता के इनपुट को सुरक्षित करें. इस बारे में ज़्यादा जानने के लिए, मैलवेयर वाले इनपुट से सुरक्षा सेक्शन पढ़ें.

डेटा उपलब्ध करवाने वाली कंपनी के डेटा टाइप

कॉन्टेंट देने वाली कंपनियां, कई अलग-अलग तरह के डेटा टाइप की सुविधा दे सकती हैं. उपयोगकर्ता के लिए डिक्शनरी उपलब्ध कराने वाली कंपनी सिर्फ़ टेक्स्ट उपलब्ध कराती है. हालांकि, कंपनी ये फ़ॉर्मैट भी उपलब्ध करा सकती है:

  • पूर्णांक
  • लंबा पूर्णांक (लंबा)
  • फ़्लोटिंग पॉइंट
  • लंबा फ़्लोटिंग पॉइंट (डबल)

डेटा प्रोवाइडर अक्सर एक और तरह के डेटा का इस्तेमाल करते हैं. इसे बाइनरी लार्ज ऑब्जेक्ट (बीएलओबी) कहा जाता है. इसे 64 केबी बाइट कलेक्शन के तौर पर लागू किया जाता है. उपलब्ध डेटा टाइप देखने के लिए, Cursor क्लास के "get" तरीके देखें.

आम तौर पर, किसी डेटा प्रोवाइडर के हर कॉलम का डेटा टाइप, उसके दस्तावेज़ में दिया होता है. उपयोगकर्ता डिक्शनरी देने वाली कंपनी के डेटा के टाइप, उसकी कॉन्ट्रैक्ट क्लास, UserDictionary.Words के लिए रेफ़रंस दस्तावेज़ में दिए गए हैं. कॉन्ट्रैक्ट क्लास के बारे में कॉन्ट्रैक्ट क्लास सेक्शन में बताया गया है. Cursor.getType() को कॉल करके भी डेटा टाइप का पता लगाया जा सकता है.

सेवा देने वाली कंपनियां, अपने तय किए गए हर कॉन्टेंट यूआरआई के लिए MIME डेटा टाइप की जानकारी भी संभालकर रखती हैं. एमआईएमई टाइप की जानकारी का इस्तेमाल करके, यह पता लगाया जा सकता है कि आपका ऐप्लिकेशन, सेवा देने वाली कंपनी के दिए गए डेटा को मैनेज कर सकता है या नहीं. इसके अलावा, एमआईएमई टाइप के हिसाब से यह चुना जा सकता है कि डेटा को किस तरह मैनेज करना है. आम तौर पर, आपको MIME टाइप की ज़रूरत तब होती है, जब किसी ऐसे सेवा देने वाले के साथ काम किया जाता है जिसके पास जटिल डेटा स्ट्रक्चर या फ़ाइलें होती हैं.

उदाहरण के लिए, Contacts Provider में मौजूद ContactsContract.Data टेबल, हर पंक्ति में सेव किए गए संपर्क डेटा के टाइप को लेबल करने के लिए MIME टाइप का इस्तेमाल करती है. किसी कॉन्टेंट यूआरआई से जुड़ा MIME टाइप पाने के लिए, ContentResolver.getType() को कॉल करें.

MIME टाइप के रेफ़रंस सेक्शन में, स्टैंडर्ड और कस्टम एमआईएमई, दोनों टाइप के सिंटैक्स के बारे में जानकारी दी जाती है.

सेवा देने वाली कंपनी के ऐक्सेस के अन्य तरीके

ऐप्लिकेशन डेवलपमेंट में, सेवा देने वाली कंपनी के ऐक्सेस के तीन अन्य तरीके अहम हैं:

  • एक साथ कई ऐक्सेस: ContentProviderOperation क्लास में मौजूद तरीकों की मदद से, एक साथ कई ऐक्सेस कॉल बनाए जा सकते हैं. इसके बाद, उन्हें ContentResolver.applyBatch() की मदद से लागू किया जा सकता है.
  • एसिंक्रोनस क्वेरी: अलग थ्रेड में क्वेरी करें. आपके पास CursorLoader ऑब्जेक्ट का इस्तेमाल करने का विकल्प है. लोडर गाइड में दिए गए उदाहरणों से, ऐसा करने का तरीका पता चलता है.
  • इंटेंट का इस्तेमाल करके डेटा ऐक्सेस करना: किसी सेवा देने वाली कंपनी को सीधे तौर पर इंटेंट नहीं भेजा जा सकता. हालांकि, सेवा देने वाली कंपनी के ऐप्लिकेशन को इंटेंट भेजा जा सकता है. आम तौर पर, यह ऐप्लिकेशन सेवा देने वाली कंपनी के डेटा में बदलाव करने के लिए सबसे सही होता है.

इंटेंट का इस्तेमाल करके, एक साथ कई आइटम ऐक्सेस करने और उनमें बदलाव करने के बारे में नीचे दिए गए सेक्शन में बताया गया है.

एक साथ कई फ़ाइलों का ऐक्सेस

किसी प्रोवाइडर का एक साथ कई बार ऐक्सेस करने की सुविधा, बड़ी संख्या में पंक्तियां डालने के लिए काम की होती है. साथ ही, एक ही तरीके के कॉल में कई टेबल में पंक्तियां डालने के लिए भी यह सुविधा काम की होती है. आम तौर पर, प्रोसेस की सीमाओं के बीच लेन-देन के तौर पर कई कार्रवाइयां करने के लिए भी यह सुविधा काम की होती है. इसे एटॉमिक ऑपरेशन कहा जाता है.

किसी सेवा देने वाली कंपनी को एक साथ ऐक्सेस करने के लिए, ContentProviderOperation ऑब्जेक्ट का कलेक्शन बनाएं. इसके बाद, ContentResolver.applyBatch() की मदद से उन्हें कॉन्टेंट की सेवा देने वाली कंपनी को भेजें. इस तरीके में, किसी कॉन्टेंट के यूआरआई के बजाय, कॉन्टेंट उपलब्ध कराने वाली कंपनी की अधिकार वाली जानकारी दी जाती है.

इससे ऐरे में मौजूद हर ContentProviderOperation ऑब्जेक्ट, अलग-अलग टेबल के साथ काम कर सकता है. ContentResolver.applyBatch() को कॉल करने पर, नतीजों की कैटगरी तय होती है.

ContactsContract.RawContacts कॉन्ट्रैक्ट क्लास के ब्यौरे में, एक कोड स्निपेट शामिल है. इसमें एक साथ कई डेटा डालने का तरीका बताया गया है.

इंटेंट का इस्तेमाल करके डेटा ऐक्सेस करना

इंटेंट की मदद से, कॉन्टेंट उपलब्ध कराने वाली कंपनी को सीधे तौर पर ऐक्सेस नहीं दिया जा सकता. आपके पास उपयोगकर्ता को किसी सेवा देने वाली कंपनी के डेटा को ऐक्सेस करने की अनुमति देने का विकल्प होता है. भले ही, आपके ऐप्लिकेशन के पास डेटा ऐक्सेस करने की अनुमतियां न हों. इसके लिए, आपको अनुमतियां रखने वाले किसी ऐप्लिकेशन से नतीजा पाने वाला इंटेंट वापस पाना होगा या अनुमतियां रखने वाले किसी ऐप्लिकेशन को चालू करके, उपयोगकर्ता को उसमें काम करने की अनुमति देनी होगी.

कुछ समय के लिए दी गई अनुमतियों की मदद से ऐक्सेस पाना

आपके पास कॉन्टेंट देने वाली कंपनी के डेटा को ऐक्सेस करने का विकल्प होता है. भले ही, आपके पास ऐक्सेस करने की सही अनुमतियां न हों. इसके लिए, आपको उस ऐप्लिकेशन को इंटेंट भेजना होगा जिसके पास अनुमतियां हैं. इसके बाद, आपको यूआरआई की अनुमतियां वाला नतीजा इंटेंट मिलेगा. ये किसी खास कॉन्टेंट यूआरआई के लिए अनुमतियां होती हैं. ये तब तक रहती हैं, जब तक उन्हें पाने वाली गतिविधि पूरी नहीं हो जाती. जिस ऐप्लिकेशन के पास हमेशा के लिए अनुमतियां हैं वह नतीजे के इंटेंट में फ़्लैग सेट करके, कुछ समय के लिए अनुमतियां देता है:

ध्यान दें: ये फ़्लैग, कॉन्टेंट यूआरआई में मौजूद अनुमति वाले प्रोवाइडर को, सामान्य रीड या राइट ऐक्सेस नहीं देते. यह ऐक्सेस सिर्फ़ यूआरआई के लिए है.

किसी दूसरे ऐप्लिकेशन को कॉन्टेंट यूआरआई भेजते समय, इनमें से कम से कम एक फ़्लैग शामिल करें. फ़्लैग, उन सभी ऐप्लिकेशन को ये सुविधाएं देते हैं जिन्हें कोई इंटेंट मिलता है और जो Android 11 (एपीआई लेवल 30) या उसके बाद के वर्शन को टारगेट करते हैं:

  • कॉन्टेंट यूआरआई से मिले डेटा को पढ़ना या उसमें कुछ लिखना. ऐसा, इंटेंट में शामिल फ़्लैग के आधार पर किया जाता है.
  • यूआरआई के एथॉरिटी से मैच करने वाले कॉन्टेंट की सेवा देने वाली कंपनी वाले ऐप्लिकेशन में, पैकेज को दिखने की अनुमति पाएं. ऐसा हो सकता है कि इंटेंट भेजने वाला ऐप्लिकेशन और कॉन्टेंट उपलब्ध कराने वाला ऐप्लिकेशन, दोनों अलग-अलग हों.

कोई भी प्रोवाइडर, अपने मेनिफ़ेस्ट में कॉन्टेंट यूआरआई के लिए यूआरआई की अनुमतियां तय करता है. इसके लिए, वह <provider> एलिमेंट के android:grantUriPermissions एट्रिब्यूट के साथ-साथ <provider> एलिमेंट के <grant-uri-permission> चाइल्ड एलिमेंट का इस्तेमाल करता है. यूआरआई की अनुमतियों के बारे में ज़्यादा जानकारी के लिए, Android पर अनुमतियां गाइड देखें.

उदाहरण के लिए, READ_CONTACTS की अनुमति न होने पर भी, 'संपर्क की सेवा देने वाली कंपनी' में किसी संपर्क का डेटा वापस लाया जा सकता है. ऐसा हो सकता है कि आप किसी ऐसे ऐप्लिकेशन में ऐसा करना चाहें जो संपर्क के जन्मदिन पर उन्हें ई-शुभकामनाएं भेजता हो. READ_CONTACTS का अनुरोध करने के बजाय, उपयोगकर्ता के सभी संपर्कों और उनकी पूरी जानकारी का ऐक्सेस पाने के बजाय, उपयोगकर्ता को यह कंट्रोल करने दें कि आपका ऐप्लिकेशन किन संपर्कों का इस्तेमाल करता है. ऐसा करने के लिए, यह तरीका अपनाएं:

  1. अपने ऐप्लिकेशन में, startActivityForResult() तरीके का इस्तेमाल करके, कार्रवाई ACTION_PICK और "संपर्क" MIME टाइप CONTENT_ITEM_TYPE वाला इंटेंट भेजें.
  2. यह इंटेंट, 'लोग' ऐप्लिकेशन की "चुनी गई" गतिविधि के इंटेंट फ़िल्टर से मेल खाता है. इसलिए, यह गतिविधि फ़ोरग्राउंड में आ जाती है.
  3. चुनने की गतिविधि में, उपयोगकर्ता अपडेट करने के लिए कोई संपर्क चुनता है. ऐसा होने पर, चुनने की गतिविधि, आपके ऐप्लिकेशन को वापस देने के लिए, इंटेंट सेट अप करने के लिए setResult(resultcode, intent) को कॉल करती है. इंटेंट में, उपयोगकर्ता के चुने गए संपर्क का कॉन्टेंट यूआरआई और "अतिरिक्त" फ़्लैग FLAG_GRANT_READ_URI_PERMISSION शामिल होता है. ये फ़्लैग, आपके ऐप्लिकेशन को यूआरआई की अनुमति देते हैं, ताकि वह कॉन्टेंट यूआरआई से दिखाए गए संपर्क का डेटा पढ़ सके. इसके बाद, चुनने की गतिविधि, आपके ऐप्लिकेशन को कंट्रोल वापस देने के लिए finish() को कॉल करती है.
  4. आपकी ऐक्टिविटी फ़ोरग्राउंड में वापस आ जाती है और सिस्टम आपकी ऐक्टिविटी के onActivityResult() तरीके को कॉल करता है. इस तरीके से, People ऐप्लिकेशन में चुनने की गतिविधि से जनरेट किया गया नतीजा इंटेंट मिलता है.
  5. नतीजे के इंटेंट से मिले डेटा यूआरआई की मदद से, Contacts की सेवा देने वाली कंपनी से संपर्क का डेटा पढ़ा जा सकता है. भले ही, आपने अपने मेनिफ़ेस्ट में, सेवा देने वाली कंपनी से डेटा पढ़ने की अनुमति का हमेशा के लिए ऐक्सेस पाने का अनुरोध न किया हो. इसके बाद, संपर्क के जन्मदिन की जानकारी या ईमेल पता पाकर, उसे ईमेल भेजा जा सकता है.

किसी दूसरे ऐप्लिकेशन का इस्तेमाल करें

उपयोगकर्ता को उस डेटा में बदलाव करने की अनुमति देने का एक और तरीका है जिसके ऐक्सेस की अनुमतियां आपके पास नहीं हैं. इसके लिए, ऐसे ऐप्लिकेशन को चालू करें जिसके पास अनुमतियां हैं और उपयोगकर्ता को उसमें काम करने दें.

उदाहरण के लिए, Calendar ऐप्लिकेशन, ACTION_INSERT इंटेंट स्वीकार करता है. इससे, ऐप्लिकेशन के इंसर्ट यूज़र इंटरफ़ेस (यूआई) को चालू किया जा सकता है. इस इंटेंट में "अतिरिक्त" डेटा पास किया जा सकता है. इसका इस्तेमाल ऐप्लिकेशन, यूज़र इंटरफ़ेस (यूआई) में अपने-आप जानकारी भरने के लिए करता है. बार-बार होने वाले इवेंट का सिंटैक्स मुश्किल होता है. इसलिए, Calendar प्रोवाइडर में इवेंट डालने का सबसे सही तरीका यह है कि ACTION_INSERT की मदद से Calendar ऐप्लिकेशन को चालू करें और फिर उपयोगकर्ता को वहां इवेंट डालने दें.

हेल्पर ऐप्लिकेशन का इस्तेमाल करके डेटा दिखाना

अगर आपके ऐप्लिकेशन के पास ऐक्सेस की अनुमतियां है, तब भी आप किसी दूसरे ऐप्लिकेशन में डेटा दिखाने के लिए इंटेंट का इस्तेमाल कर सकते हैं. उदाहरण के लिए, Calendar ऐप्लिकेशन, ACTION_VIEW इंटेंट स्वीकार करता है, जो किसी खास तारीख या इवेंट को दिखाता है. इससे, अपना यूज़र इंटरफ़ेस (यूआई) बनाए बिना कैलेंडर की जानकारी दिखाई जा सकती है. इस सुविधा के बारे में ज़्यादा जानने के लिए, Calendar की सेवा देने वाली कंपनी की खास जानकारी देखें.

यह ज़रूरी नहीं है कि जिस ऐप्लिकेशन को इंटेंट भेजा जा रहा है वह सेवा देने वाली कंपनी से जुड़ा हो. उदाहरण के लिए, किसी संपर्क की जानकारी को कॉन्टैक्ट प्रोवाइडर से पाकर, इमेज व्यूअर को उस संपर्क की इमेज का कॉन्टेंट यूआरआई शामिल करके ACTION_VIEW इंटेंट भेजा जा सकता है.

कॉन्ट्रैक्ट क्लास

कॉन्ट्रैक्ट क्लास, ऐसे कॉन्सटेंट के बारे में बताती है जो ऐप्लिकेशन को कॉन्टेंट यूआरआई, कॉलम के नाम, इंटेंट ऐक्शन, और कॉन्टेंट देने वाले की अन्य सुविधाओं के साथ काम करने में मदद करते हैं. कॉन्ट्रैक्ट क्लास, सेवा देने वाली कंपनी के साथ अपने-आप शामिल नहीं होती हैं. सेवा देने वाली कंपनी के डेवलपर को इन्हें तय करना होगा और इसके बाद इन्हें अन्य डेवलपर के लिए उपलब्ध कराना होगा. Android प्लैटफ़ॉर्म के साथ शामिल कई सेवा देने वाली कंपनियों के पास, पैकेज android.provider में उसी तरह के कॉन्ट्रैक्ट क्लास हैं.

उदाहरण के लिए, उपयोगकर्ता डिक्शनरी प्रोवाइडर के पास एक कॉन्ट्रैक्ट क्लास UserDictionary है, जिसमें कॉन्टेंट यूआरआई और कॉलम के नाम के कॉन्स्टेंट शामिल हैं. Words टेबल के लिए कॉन्टेंट यूआरआई, कॉन्स्टेंट UserDictionary.Words.CONTENT_URI में तय किया गया है. UserDictionary.Words क्लास में कॉलम के नाम के कॉन्सटेंट भी शामिल होते हैं, जिनका इस्तेमाल इस गाइड में, उदाहरण के तौर पर दिए गए स्निपेट में किया जाता है. उदाहरण के लिए, क्वेरी प्रोजेक्शन को इस तरह से परिभाषित किया जा सकता है:

Kotlin

val projection : Array<String> = arrayOf(
        UserDictionary.Words._ID,
        UserDictionary.Words.WORD,
        UserDictionary.Words.LOCALE
)

Java

String[] projection =
{
    UserDictionary.Words._ID,
    UserDictionary.Words.WORD,
    UserDictionary.Words.LOCALE
};

संपर्कों की जानकारी देने वाली कंपनी के लिए, अनुबंध की एक और क्लास ContactsContract है. इस क्लास के रेफ़रंस दस्तावेज़ में, कोड स्निपेट के उदाहरण शामिल हैं. इसकी एक सबसेट, ContactsContract.Intents.Insert एक कॉन्ट्रैक्ट क्लास है. इसमें इंटेंट और इंटेंट डेटा के लिए कॉन्स्टेंट होते हैं.

MIME टाइप का रेफ़रंस

कॉन्टेंट की सेवा देने वाली कंपनियां, स्टैंडर्ड MIME मीडिया टाइप, कस्टम MIME टाइप स्ट्रिंग या दोनों को दिखा सकती हैं.

MIME टाइप का फ़ॉर्मैट यह है:

type/subtype

उदाहरण के लिए, text/html MIME टाइप में text टाइप और html सबटाइप होता है. अगर कोई प्रोवाइडर किसी यूआरआई के लिए यह टाइप दिखाता है, तो इसका मतलब है कि उस यूआरआई का इस्तेमाल करके की गई क्वेरी से एचटीएमएल टैग वाला टेक्स्ट मिलता है.

कस्टम MIME टाइप की स्ट्रिंग, जिन्हें वेंडर के हिसाब से MIME टाइप भी कहा जाता है, में type और subtype की ज़्यादा जटिल वैल्यू होती हैं. एक से ज़्यादा पंक्तियों के लिए, टाइप की वैल्यू हमेशा यहां दी गई होती है:

vnd.android.cursor.dir

किसी एक लाइन के लिए, टाइप की वैल्यू हमेशा यहां दी गई होती है:

vnd.android.cursor.item

subtype, सेवा देने वाली कंपनी के हिसाब से अलग-अलग होता है. Android में पहले से मौजूद सेवा देने वाली कंपनियों का सब-टाइप आम तौर पर आसान होता है. उदाहरण के लिए, जब Contacts ऐप्लिकेशन किसी टेलीफ़ोन नंबर के लिए लाइन बनाता है, तो वह लाइन में यह MIME टाइप सेट करता है:

vnd.android.cursor.item/phone_v2

सब-टाइप की वैल्यू phone_v2 है.

सेवा देने वाली अन्य कंपनियों के डेवलपर, सेवा देने वाली कंपनी के ऐथॉरिटी और टेबल के नाम के आधार पर, सब-टाइप का अपना पैटर्न बना सकते हैं. उदाहरण के लिए, ट्रेन के समयावधि की जानकारी देने वाली सेवा देने वाली कंपनी. सेवा देने वाली कंपनी का अधिकार com.example.trains है. इसमें टेबल Line1, Line2, और Line3 शामिल हैं. तालिका Line1 के लिए नीचे दिए गए कॉन्टेंट यूआरआई के जवाब में:

content://com.example.trains/Line1

प्रोवाइडर इस तरह का MIME टाइप देता है:

vnd.android.cursor.dir/vnd.example.line1

टेबल Line2 की पंक्ति 5 के लिए, कॉन्टेंट के इस यूआरआई के जवाब में:

content://com.example.trains/Line2/5

प्रोवाइडर इस तरह का MIME टाइप देता है:

vnd.android.cursor.item/vnd.example.line2

ज़्यादातर कॉन्टेंट प्रोवाइडर, इस्तेमाल किए जाने वाले MIME टाइप के लिए कॉन्ट्रैक्ट क्लास के कॉन्स्टेंट तय करते हैं. उदाहरण के लिए, कॉन्टैक्ट की जानकारी देने वाली कंपनी के समझौते की क्लास ContactsContract.RawContacts, किसी एक रॉ कॉन्टैक्ट पंक्ति के MIME टाइप के लिए, स्थिर वैल्यू CONTENT_ITEM_TYPE तय करती है.

एक पंक्ति के लिए कॉन्टेंट यूआरआई के बारे में जानकारी, कॉन्टेंट यूआरआई सेक्शन में दी गई है.