مبانی ارائه‌دهنده محتوا

ارائه‌دهنده محتوا دسترسی به مخزن مرکزی داده‌ها را مدیریت می‌کند. ارائه‌دهنده بخشی از برنامه Android است که اغلب میانای کاربری خودش را برای کار با داده‌ها ارائه می‌دهد. بااین‌حال، ارائه‌دهندگان محتوا عمدتاً توسط برنامه‌های دیگر استفاده می‌شوند که بااستفاده از شیء کارخواه ارائه‌دهنده به ارائه‌دهنده دسترسی پیدا می‌کنند. ارائه‌دهندگان و مشتریان ارائه‌دهنده باهم یک میانای استاندارد و سازگار برای داده‌ها ارائه می‌دهند که ارتباط میان‌پردازشی و دسترسی امن به داده‌ها را نیز مدیریت می‌کند.

معمولاً در یکی از دو سناریو زیر با ارائه‌دهندگان محتوا کار می‌کنید: پیاده‌سازی کد برای دسترسی به ارائه‌دهنده محتوای موجود در برنامه‌ای دیگر یا ایجاد ارائه‌دهنده محتوای جدید در برنامه‌تان برای هم‌رسانی داده‌ها با برنامه‌های دیگر.

این صفحه مبانی کار با ارائه‌دهندگان محتوای موجود را پوشش می‌دهد. برای آشنایی با پیاده‌سازی ارائه‌دهندگان محتوا در برنامه‌های خودتان، به ایجاد ارائه‌دهنده محتوا مراجعه کنید.

این موضوع موارد زیر را شرح می‌دهد:

  • نحوه عملکرد ارائه‌دهندگان محتوا.
  • میانای برنامه‌سازی کاربردی که برای بازیابی داده‌ها از ارائه‌دهنده محتوا استفاده می‌کنید.
  • میانای برنامه‌سازی کاربردی‌ای که برای درج، به‌روزرسانی، یا حذف داده‌ها در ارائه‌دهنده محتوا استفاده می‌کنید.
  • سایر ویژگی‌های API که کار با ارائه‌دهندگان را تسهیل می‌کند.

نمای کلی

ارائه‌دهنده محتوا داده‌ها را به‌صورت یک یا چند جدول به برنامه‌های خارجی ارائه می‌دهد که شبیه به جدول‌های موجود در پایگاه داده رابطه‌ای هستند. هر ردیف نشان‌دهنده نمونه‌ای از نوعی داده است که ارائه‌دهنده جمع‌آوری می‌کند، و هر ستون در ردیف نشان‌دهنده یک بخش مجزا از داده‌های جمع‌آوری‌شده برای یک نمونه است.

ارائه‌دهنده محتوا دسترسی به لایه ذخیره‌سازی داده را در برنامه شما برای تعدادی از میاناهای برنامه‌سازی کاربردی و مؤلفه‌های مختلف هماهنگ می‌کند. همان‌طور که در شکل ۱ نشان داده شده است، این موارد شامل موارد زیر است:

  • هم‌رسانی دسترسی به داده‌های برنامه شما با برنامه‌های دیگر
  • درحال ارسال داده به ابزاره
  • برگرداندن پیشنهادهای جستجوی سفارشی برای برنامه شما ازطریق چارچوب جستجو بااستفاده از SearchRecentSuggestionsProvider
  • درحال همگام‌سازی داده‌های برنامه با سرور شما بااستفاده از پیاده‌سازی AbstractThreadedSyncAdapter
  • درحال بار کردن داده‌ها در میانای کاربر بااستفاده از CursorLoader
رابطه بین ارائه‌دهنده محتوا و مؤلفه‌های دیگر.

شکل ۱. رابطه بین ارائه‌دهنده محتوا و مؤلفه‌های دیگر.

دسترسی به ارائه‌دهنده

وقتی می‌خواهید به داده‌ها در ارائه‌دهنده محتوا دسترسی پیدا کنید، از شیء ContentResolver در Context برنامه‌تان استفاده می‌کنید تا به‌عنوان مشتری با ارائه‌دهنده ارتباط برقرار کنید. شیء ContentResolver با شیء ارائه‌دهنده ارتباط برقرار می‌کند، نمونه‌ای از کلاسی که ContentProvider را پیاده‌سازی می‌کند.

شیء ارائه‌دهنده درخواست‌های داده را از مشتریان دریافت می‌کند، کنش درخواست‌شده را انجام می‌دهد، و نتایج را برمی‌گرداند. این شیء روش‌هایی دارد که روش‌های با نام یکسان را در شیء ارائه‌دهنده فراخوانی می‌کند، نمونه‌ای از یکی از زیرکلاس‌های واقعی ContentProvider. روش‌های ContentResolver عملکردهای پایه «CRUD» (ایجاد، بازیابی، به‌روزرسانی، و حذف) فضای ذخیره‌سازی پایدار را ارائه می‌دهند.

الگوی رایج برای دسترسی به ContentProvider از واسط کاربر شما از CursorLoader برای اجرای پُرسمان ناهمگام در پس‌زمینه استفاده می‌کند. ‫Activity یا Fragment در واسط کاربر شما CursorLoader را برای پُرسمان فراخوانی می‌کند که به‌نوبه خود ContentProvider را بااستفاده از ContentResolver دریافت می‌کند.

این کار باعث می‌شود که واسط کاربر همچنان درحین اجرای پُرسمان برای کاربر دردسترس باشد. این الگو شامل تعامل تعدادی از اشیاء مختلف و همچنین سازوکار ذخیره‌سازی زیربنایی است، همان‌طور که در شکل ۲ نشان داده شده است.

تعامل بین ContentProvider، کلاس‌های دیگر، و فضای ذخیره‌سازی.

شکل ۲. تعامل بین ContentProvider، کلاس‌های دیگر، و فضای ذخیره‌سازی.

توجه: برای دسترسی به ارائه‌دهنده، برنامه شما معمولاً باید اجازه‌های خاصی را در فایل مانیفست خود درخواست کند. این الگوی توسعه با جزئیات بیشتر در بخش اجازه‌های ارائه‌دهنده محتوا شرح داده شده است.

یکی از ارائه‌دهندگان داخلی در پلاتفرم Android، «ارائه‌دهنده فرهنگ لغت کاربر» است که کلمات غیراستانداردی را که کاربر می‌خواهد نگه دارد ذخیره می‌کند. جدول ۱ نشان می‌دهد که داده‌ها در جدول این ارائه‌دهنده ممکن است چگونه باشد:

جدول ۱: جدول نمونه فرهنگ لغت کاربر.

کلمه شناسه برنامه تعداد دفعات منطقه زبانی _ID
mapreduce کاربر ۱ ۱۰۰ en_US 1
precompiler user14 ۲۰۰ fr_FR 2
applet کاربر ۲ ۲۲۵ fr_CA 3
const کاربر ۱ 255 pt_BR 4
int user5 ۱۰۰ en_UK 5

در جدول ۱، هر ردیف نشان‌دهنده نمونه‌ای از واژه‌ای است که در فرهنگ لغت استاندارد پیدا نشده است. هر ستون نشان‌دهنده بخشی از داده‌های مربوط به آن کلمه است، مثلاً منطقه‌ای که کلمه برای اولین‌بار در آن دیده شده است. سرصفحه‌های ستون نام‌های ستونی هستند که در ارائه‌دهنده ذخیره شده‌اند. بنابراین برای ارجاع به منطقه زبانی یک ردیف، برای مثال، به ستون locale آن ارجاع می‌دهید. برای این ارائه‌دهنده، ستون _ID به‌عنوان ستون کلید اصلی عمل می‌کند که ارائه‌دهنده به‌طور خودکار آن را حفظ می‌کند.

برای دریافت فهرست واژه‌ها و زبان‌های آن‌ها از «ارائه‌دهنده فرهنگ لغت کاربر»، ContentResolver.query() را فراخوانی می‌کنید. روش query() روش ContentProvider.query() را که توسط «ارائه‌دهنده واژه‌نامه کاربر» تعریف شده است فراخوانی می‌کند. خطوط کد زیر یک فراخوانی ContentResolver.query() را نشان می‌دهد:

کاتلین

// 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
)

جاوا

// 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

جدول ۲ نشان می‌دهد که چگونه متغیرهای مستقلِ query(Uri,projection,selection,selectionArgs,sortOrder) با عبارت SQL SELECT مطابقت دارند:

جدول ۲: query() درمقایسه با پُرسمان SQL.

‫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 مرجع URI را تجزیه می‌کند و با مقایسه مرجع با جدول سیستم ارائه‌دهندگان شناخته‌شده، از آن برای حل ارائه‌دهنده استفاده می‌کند. سپس ContentResolver می‌تواند آرگومان‌های پُرسمان را به ارائه‌دهنده صحیح ارسال کند.

‫ContentProvider از بخش مسیر شناسه منبع یکسان محتوا برای انتخاب جدول موردنظر برای دسترسی استفاده می‌کند. ارائه‌دهنده معمولاً برای هر جدولی که نمایش می‌دهد یک مسیر دارد.

در خطوط کد قبلی، شناسه منبع یکسان کامل برای جدول Words عبارت است از:

content://user_dictionary/words
  • رشته content:// طرح است که همیشه وجود دارد و این را به‌عنوان شناسه منبع یکسان محتوا شناسایی می‌کند.
  • رشته user_dictionary مرجع ارائه‌دهنده است.
  • رشته words مسیر جدول است.

بسیاری‌از ارائه‌دهندگان به شما اجازه می‌دهند با افزودن مقدار شناسه به انتهای URI، به یک ردیف در جدول دسترسی پیدا کنید. برای مثال، برای بازیابی ردیفی که _ID آن 4 است از «ارائه‌دهنده فرهنگ لغت کاربر»، می‌توانید از این شناسه منبع یکسان محتوا استفاده کنید:

کاتلین

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

جاوا

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

اغلب زمانی از مقادیر شناسه استفاده می‌کنید که مجموعه‌ای از ردیف‌ها را بازیابی می‌کنید و سپس می‌خواهید یکی از آن‌ها را به‌روزرسانی یا حذف کنید.

توجه: کلاس‌های Uri و Uri.Builder حاوی روش‌های راحتی برای ساختن اشیای URI خوش‌ساخت از رشته‌ها هستند. کلاس ContentUris حاوی روش‌های تسهیلات برای افزودن مقادیر شناسه به URI است. تکه کد قبلی از withAppendedId() برای افزودن شناسه به شناسه منبع یکسان محتوای ارائه‌دهنده فرهنگ لغت کاربر استفاده می‌کند.

بازیابی داده‌ها از ارائه‌دهنده

این بخش نحوه بازیابی داده‌ها از ارائه‌دهنده را بااستفاده از «ارائه‌دهنده فرهنگ لغت کاربر» به‌عنوان نمونه شرح می‌دهد.

برای شفافیت بیشتر، تکه‌کدهای این بخش ContentResolver.query() را در رشته میانای کاربر فراخوانی می‌کنند. اما در کد واقعی، پُرسمان‌ها را به‌صورت ناهمگام در رشته‌ای جداگانه انجام دهید. می‌توانید از کلاس CursorLoader استفاده کنید که در راهنمای بارکننده‌ها با جزئیات بیشتر شرح داده شده است. همچنین، خطوط کد فقط تکه‌کد هستند. آن‌ها درخواست کاملی را نشان نمی‌دهند.

برای بازیابی داده‌ها از ارائه‌دهنده، این مراحل اصلی را دنبال کنید:

  1. درخواست اجازه دسترسی خواندن برای ارائه‌دهنده.
  2. کدی را که پُرسمان را به ارائه‌دهنده ارسال می‌کند تعریف کنید.

درخواست اجازه دسترسی خواندن

برای بازیابی داده‌ها از ارائه‌دهنده، برنامه شما به اجازه دسترسی خواندن برای ارائه‌دهنده نیاز دارد. نمی‌توانید این اجازه را در زمان اجرا درخواست کنید. درعوض، باید بااستفاده از عنصر <uses-permission> و نام دقیق اجازه تعریف‌شده توسط ارائه‌دهنده، در مانیفست خود مشخص کنید که به این اجازه نیاز دارید.

وقتی این عنصر را در مانیفست خود مشخص می‌کنید، این اجازه را برای برنامه خود درخواست می‌کنید. وقتی کاربران برنامه شما را نصب می‌کنند، به‌طور ضمنی این درخواست را اعطا می‌کنند.

برای پیدا کردن نام دقیق اجازه دسترسی خواندن برای ارائه‌دهنده‌ای که استفاده می‌کنید، و همچنین نام‌های سایر اجازه‌های دسترسی که ارائه‌دهنده استفاده می‌کند، به اسناد ارائه‌دهنده مراجعه کنید.

نقش اجازه‌ها در دسترسی به ارائه‌دهندگان با جزئیات بیشتر در بخش اجازه‌های ارائه‌دهنده محتوا شرح داده شده است.

«ارائه‌دهنده فرهنگ لغت کاربر» اجازه android.permission.READ_USER_DICTIONARY را در فایل مانیفست خود تعریف می‌کند، بنابراین برنامه‌ای که می‌خواهد از ارائه‌دهنده بخواند باید این اجازه را درخواست کند.

ساختن پُرسمان

مرحله بعدی در بازیابی داده‌ها از ارائه‌دهنده، ساختن پُرسمان است. قطعه کد زیر برخی‌از متغیرها را برای دسترسی به «ارائه‌دهنده فرهنگ لغت کاربر» تعریف می‌کند:

کاتلین

// 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>

جاوا

// 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 + " = ?" تنظیم می‌شود و اولین عنصر آرایه آرگومان‌های انتخاب روی کلمه‌ای که کاربر وارد کرده است تنظیم می‌شود.

کاتلین

/*
 * 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
    }
}

جاوا

/*
 * 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، به‌جای ثابت‌های کلاس قرارداد، از نام‌های واقعی ستون استفاده می‌شود.

محافظت دربرابر ورودی مخرب

اگر داده‌های مدیریت‌شده توسط ارائه‌دهنده محتوا در پایگاه داده SQL باشد، گنجاندن داده‌های غیرقابل‌اعتماد خارجی در بیانیه‌های SQL خام می‌تواند به تزریق SQL منجر شود.

بند انتخاب زیر را درنظر بگیرید:

کاتلین

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

جاوا

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

اگر این کار را انجام دهید، به کاربر اجازه می‌دهید SQL مخرب را به بیانیه SQL شما الحاق کند. برای مثال، کاربر می‌تواند «nothing; DROP TABLE *;» را برای mUserInput وارد کند که منجر به عبارت انتخاب var = nothing; DROP TABLE *; می‌شود.

ازآنجایی‌که عبارت انتخاب به‌عنوان دستور SQL درنظر گرفته می‌شود، این امر ممکن است باعث شود ارائه‌دهنده همه جدول‌های موجود در پایگاه داده SQLite زیربنایی را پاک کند، مگراینکه ارائه‌دهنده برای شناسایی تلاش‌های تزریق SQL راه‌اندازی شده باشد.

برای جلوگیری از این مشکل، از عبارت انتخابی استفاده کنید که از ? به‌عنوان پارامتر قابل‌جایگزین و آرایه‌ای جدا از متغیرهای مستقلِ انتخاب استفاده می‌کند. به‌این‌ترتیب، ورودی کاربر مستقیماً به پُرسمان محدود می‌شود و به‌عنوان بخشی از عبارت SQL تفسیر نمی‌شود. چون با ورودی کاربر به‌عنوان SQL رفتار نمی‌شود، ورودی کاربر نمی‌تواند SQL مخرب تزریق کند. به‌جای استفاده از الحاق برای گنجاندن ورودی کاربر، از این عبارت انتخاب استفاده کنید:

کاتلین

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

جاوا

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

آرایه آرگومان‌های انتخاب را به این صورت تنظیم کنید:

کاتلین

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

جاوا

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

مقداری را در آرایه آرگومان‌های انتخاب قرار دهید، مانند:

کاتلین

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

جاوا

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

عبارت انتخابی که از ? به‌عنوان پارامتری قابل‌جایگزین و آرایه‌ای از آرایه‌های متغیرهای مستقلِ انتخاب استفاده می‌کند، روش ترجیحی برای مشخص کردن انتخاب است، حتی اگر ارائه‌دهنده مبتنی بر پایگاه داده SQL نباشد.

نمایش نتایج پُرسمان

روش کارخواه ContentResolver.query() همیشه Cursor حاوی ستون‌های مشخص‌شده توسط طرح پُرسمان را برای ردیف‌هایی که با معیارهای انتخاب پُرسمان مطابقت دارند برمی‌گرداند. یک شیء Cursor دسترسی خواندن تصادفی به ردیف‌ها و ستون‌هایی که دربردارد ارائه می‌دهد.

بااستفاده از روش‌های Cursor، می‌توانید ردیف‌های نتایج را تکرار کنید، نوع داده هر ستون را تعیین کنید، داده‌ها را از ستون خارج کنید، و سایر ویژگی‌های نتایج را بررسی کنید.

برخی‌از پیاده‌سازی‌های Cursor به‌طور خودکار شیء را هنگام تغییر داده‌های ارائه‌دهنده به‌روز می‌کنند، روش‌های راه‌انداز را در شیء ناظر هنگام تغییر Cursor فعال می‌کنند، یا هر دو کار را انجام می‌دهند.

توجه: ارائه‌دهنده می‌تواند دسترسی به ستون‌ها را براساس ماهیت شیء پرس‌وجوکننده محدود کند. برای مثال، «ارائه‌دهنده مخاطبین» دسترسی به برخی‌از ستون‌ها را برای وفق‌دهنده‌های همگام‌سازی محدود می‌کند، بنابراین آن‌ها را به فعالیت یا سرویس برنمی‌گرداند.

اگر هیچ ردیفی با معیارهای انتخاب مطابقت نداشته باشد، ارائه‌دهنده یک شیء Cursor برمی‌گرداند که Cursor.getCount() آن 0 است—یعنی یک مکان‌نمای خالی.

اگر خطای داخلی رخ دهد، نتایج پُرسمان به ارائه‌دهنده خاص بستگی دارد. ممکن است null را برگرداند یا Exception را ایجاد کند.

ازآنجایی‌که Cursor فهرستی از ردیف‌ها است، روشی خوب برای نمایش محتوای Cursor پیوند دادن آن به ListView بااستفاده از SimpleCursorAdapter است.

تکه‌کد زیر ادامه کد تکه‌کد قبلی است. این کد یک شیء SimpleCursorAdapter ایجاد می‌کند که حاوی Cursor بازیابی‌شده توسط پرسمان است و این شیء را به‌عنوان آداپتور برای ListView تنظیم می‌کند.

کاتلین

// 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)

جاوا

// 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 باشد. به‌همین دلیل، پُرسمان نشان‌داده‌شده در بالا ستون _ID را برای جدول Words بازیابی می‌کند، هرچند ListView آن را نمایش نمی‌دهد. این محدودیت همچنین توضیح می‌دهد که چرا اکثر ارائه‌دهندگان برای هریک از جدول‌هایشان ستون _ID دارند.

دریافت داده از نتایج پُرسمان

علاوه‌بر نمایش نتایج پُرسمان، می‌توانید از آن‌ها برای کارهای دیگر نیز استفاده کنید. برای مثال، می‌توانید املاها را از «ارائه‌دهنده فرهنگ لغت کاربر» بازیابی کنید و سپس آن‌ها را در ارائه‌دهندگان دیگر جستجو کنید. برای انجام این کار، ردیف‌های Cursor را تکرار می‌کنید، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

/*
* 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
    }
}

جاوا

// 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 شامل چندین روش «دریافت» برای بازیابی انواع مختلف داده از شیء است. برای مثال، قطعه کد قبلی از getString() استفاده می‌کند. آن‌ها همچنین دارای یک getType() روش هستند که مقداری را برمی‌گرداند که نشان‌دهنده نوع داده ستون است.

انتشار منابع نتیجه پُرسمان

Cursor شیء باید بسته شود اگر دیگر به آن‌ها نیازی نیست، تا منابع مرتبط با آن‌ها زودتر آزاد شود. این کار را می‌توان با فراخوانی close() یا بااستفاده از عبارت try-with-resources در زبان برنامه‌نویسی Java یا تابع use() در زبان برنامه‌نویسی Kotlin انجام داد.

اجازه‌های ارائه‌دهنده محتوا

برنامه ارائه‌دهنده می‌تواند اجازه‌هایی را مشخص کند که برنامه‌های دیگر برای دسترسی به داده‌های ارائه‌دهنده باید داشته باشند. این اجازه‌ها به کاربر اطلاع می‌دهند که برنامه سعی دارد به چه داده‌هایی دسترسی پیدا کند. براساس الزامات ارائه‌دهنده، برنامه‌های دیگر اجازه‌هایی را که برای دسترسی به ارائه‌دهنده نیاز دارند درخواست می‌کنند. کاربران نهایی هنگام نصب برنامه، اجازه‌های درخواست‌شده را می‌بینند.

اگر برنامه ارائه‌دهنده هیچ اجازه‌ای را مشخص نکند، برنامه‌های دیگر به داده‌های ارائه‌دهنده دسترسی ندارند، مگراینکه ارائه‌دهنده صادر شود. علاوه‌براین، مؤلفه‌های برنامه ارائه‌دهنده همیشه دسترسی کامل خواندن و نوشتن دارند، صرف‌نظر از اجازه‌های مشخص‌شده.

«ارائه‌دهنده فرهنگ لغت کاربر» برای بازیابی داده‌ها از آن به اجازه android.permission.READ_USER_DICTIONARY نیاز دارد. ارائه‌دهنده android.permission.WRITE_USER_DICTIONARY اجازه جداگانه‌ای برای درج، به‌روزرسانی، یا حذف داده‌ها دارد.

برای دریافت اجازه‌های لازم برای دسترسی به ارائه‌دهنده، برنامه آن‌ها را با عنصر <uses-permission> در فایل مانیفست خود درخواست می‌کند. وقتی «مدیر بسته Android» برنامه را نصب می‌کند، کاربر باید همه اجازه‌هایی را که برنامه درخواست می‌کند تأیید کند. اگر کاربر آن‌ها را تأیید کند، «مدیر بسته» نصب را ادامه می‌دهد. اگر کاربر آن‌ها را تأیید نکند، Package Manager نصب را متوقف می‌کند.

نمونه عنصر <uses-permission> زیر دسترسی خواندن به «ارائه‌دهنده واژه‌نامه کاربر» را درخواست می‌کند:

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

تأثیر اجازه‌ها بر دسترسی ارائه‌دهنده با جزئیات بیشتر در نکات امنیتی توضیح داده شده است.

درج، به‌روزرسانی، و حذف داده‌ها

همان‌طور که داده‌ها را از ارائه‌دهنده بازیابی می‌کنید، از تعامل بین کلاینت ارائه‌دهنده و ContentProvider ارائه‌دهنده نیز برای اصلاح داده‌ها استفاده می‌کنید. شما روشی از ContentResolver را با آرگومان‌هایی که به روش مربوطه ContentProvider منتقل می‌شوند فراخوانی می‌کنید. ارائه‌دهنده و مشتری ارائه‌دهنده به‌طور خودکار امنیت و ارتباط میان پردازشی را مدیریت می‌کنند.

درج داده‌ها

برای درج داده‌ها در ارائه‌دهنده، ContentResolver.insert() را فراخوانی می‌کنید. این روش ردیف جدیدی را در ارائه‌دهنده درج می‌کند و شناسه منبع یکسان محتوا را برای آن ردیف برمی‌گرداند. قطعه کد زیر نشان می‌دهد که چگونه یک کلمه جدید را در User Dictionary Provider درج کنید:

کاتلین

// 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
)

جاوا

// 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 برای ردیف جدید است. اکثر ارائه‌دهندگان می‌توانند این شکل از شناسه منبع یکسان محتوا را به‌طور خودکار شناسایی کنند و سپس عملیات درخواستی را روی آن ردیف خاص انجام دهند.

برای دریافت مقدار _ID از Uri برگردانده‌شده، ContentUris.parseId() را فراخوانی کنید.

به‌روزرسانی داده‌ها

برای به‌روزرسانی ردیف، از شیء ContentValues با مقادیر به‌روزشده استفاده می‌کنید، درست همان‌طور که برای درج و معیارهای انتخاب، همان‌طور که برای پُرسمان انجام می‌دهید. روش کارخواهی که استفاده می‌کنید ContentResolver.update() است. فقط باید مقادیر را به شیء ContentValues برای ستون‌هایی که به‌روزرسانی می‌کنید اضافه کنید. اگر می‌خواهید محتوای ستونی را پاک کنید، مقدار را روی null تنظیم کنید.

قطعه کد زیر همه ردیف‌هایی را که زبان محلی آن‌ها "en" است به محلی null تغییر می‌دهد. مقدار برگشتی تعداد ردیف‌هایی است که به‌روز شده‌اند.

کاتلین

// 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
)

جاوا

// 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" مطابقت دارد حذف می‌کند. این روش تعداد ردیف‌های حذف‌شده را برمی‌گرداند.

کاتلین

// 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
)

جاوا

// 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()، ورودی کاربر را پاک‌سازی کنید. برای کسب اطلاعات بیشتر درباره این موضوع، بخش محافظت دربرابر ورودی مخرب را بخوانید.

انواع داده‌های ارائه‌دهنده

ارائه‌دهندگان محتوا می‌توانند انواع مختلفی از داده‌ها را ارائه دهند. ‫User Dictionary Provider فقط نوشتار ارائه می‌دهد، اما ارائه‌دهندگان می‌توانند قالب‌های زیر را نیز ارائه دهند:

  • عدد صحیح
  • عدد صحیح بلند (بلند)
  • ممیز شناور
  • ممیز شناور طولانی (دوگانه)

نوع داده دیگری که ارائه‌دهندگان اغلب استفاده می‌کنند شیء بزرگ دودویی (BLOB) است که به‌عنوان آرایه بایت ۶۴ کیلوبایتی پیاده‌سازی شده است. می‌توانید انواع داده‌های دردسترس را با نگاه کردن به روش‌های «دریافت» کلاس Cursor ببینید.

نوع داده برای هر ستون در ارائه‌دهنده معمولاً در مستندات آن فهرست می‌شود. انواع داده برای User Dictionary Provider در اسناد مرجع برای کلاس قرارداد آن، UserDictionary.Words، فهرست شده است. کلاس‌های قرارداد در بخش کلاس‌های قرارداد شرح داده شده‌اند. همچنین می‌توانید با فراخوانی Cursor.getType()، نوع داده را تعیین کنید.

ارائه‌دهندگان همچنین اطلاعات نوع داده MIME را برای هر شناسه منبع یکسان محتوایی که تعریف می‌کنند حفظ می‌کنند. می‌توانید از اطلاعات نوع MIME استفاده کنید تا متوجه شوید که آیا برنامه‌تان می‌تواند داده‌هایی را که ارائه‌دهنده ارائه می‌دهد مدیریت کند یا نه، یا نوعی مدیریت را براساس نوع MIME انتخاب کنید. معمولاً وقتی با ارائه‌دهنده‌ای کار می‌کنید که حاوی ساختارها یا فایل‌های داده پیچیده است، به نوع MIME نیاز دارید.

برای مثال، جدول ContactsContract.Data در «ارائه‌دهنده مخاطبین» از انواع MIME برای برچسب‌گذاری نوع داده‌های مخاطب ذخیره‌شده در هر ردیف استفاده می‌کند. برای دریافت نوع MIME مربوط به شناسه منبع یکسان محتوا، ContentResolver.getType() را فراخوانی کنید.

بخش مرجع نوع MIME نحو انواع MIME استاندارد و سفارشی را شرح می‌دهد.

اشکال جایگزین دسترسی ارائه‌دهنده

سه شکل جایگزین دسترسی ارائه‌دهنده در توسعه برنامه مهم است:

  • دسترسی دسته‌ای: می‌توانید بااستفاده از روش‌های موجود در کلاس ContentProviderOperation، دسته‌ای از تماس‌های دسترسی ایجاد کنید و سپس آن‌ها را با ContentResolver.applyBatch() اعمال کنید.
  • پُرسمان‌های ناهمگام: پُرسمان‌ها را در رشته‌ای جداگانه انجام دهید. می‌توانید از شیء CursorLoader استفاده کنید. مثال‌های موجود در راهنمای بارکننده‌ها نشان می‌دهد چگونه این کار را انجام دهید.
  • دسترسی به داده‌ها بااستفاده از هدف‌ها: اگرچه نمی‌توانید هدفی را مستقیماً به ارائه‌دهنده ارسال کنید، می‌توانید هدفی را به برنامه ارائه‌دهنده ارسال کنید که معمولاً بهترین ابزار برای اصلاح داده‌های ارائه‌دهنده است.

دسترسی و اصلاح دسته‌ای بااستفاده از قصدها در بخش‌های زیر شرح داده شده است.

دسترسی دسته‌ای

دسترسی دسته‌ای به ارائه‌دهنده برای درج تعداد زیادی ردیف، برای درج ردیف‌ها در چند جدول در یک فراخوانی روش، و به‌طور کلی برای انجام مجموعه‌ای از عملیات در مرزهای فرایند به‌عنوان یک تراکنش، که عملیات اتمی نامیده می‌شود، مفید است.

برای دسترسی به ارائه‌دهنده در حالت دسته‌ای، آرایه‌ای از اشیاء ContentProviderOperation ایجاد کنید و سپس آن‌ها را با ContentResolver.applyBatch() به ارائه‌دهنده محتوا ارسال کنید. مرجع ارائه‌دهنده محتوا را به این روش منتقل می‌کنید، نه شناسه منبع یکسان محتوای خاصی را.

این کار به هر شیء ContentProviderOperation در آرایه اجازه می‌دهد در جدول متفاوتی کار کند. فراخوانی ContentResolver.applyBatch() آرایه‌ای از نتایج را برمی‌گرداند.

شرح کلاس قرارداد ContactsContract.RawContacts شامل تکه‌کدی است که درج دسته‌ای را نشان می‌دهد.

دسترسی به داده بااستفاده از قصدها

«هدف‌ها» می‌توانند دسترسی غیرمستقیم به ارائه‌دهنده محتوا را فراهم کنند. می‌توانید به کاربر اجازه دهید به داده‌های ارائه‌دهنده دسترسی داشته باشد، حتی اگر برنامه شما اجازه دسترسی نداشته باشد. این کار را می‌توانید با دریافت هدف نتیجه از برنامه‌ای که اجازه دارد یا با فعال کردن برنامه‌ای که اجازه دارد و اجازه دادن به کاربر برای انجام کار در آن انجام دهید.

دسترسی با اجازه‌های موقت

می‌توانید به داده‌های ارائه‌دهنده محتوا دسترسی پیدا کنید، حتی اگر اجازه‌های دسترسی مناسب را نداشته باشید، با ارسال هدف به برنامه‌ای که اجازه‌ها را دارد و دریافت هدف نتیجه حاوی اجازه‌های URI. این‌ها اجازه‌هایی برای شناسه منبع یکسان محتوای خاصی هستند که تا پایان فعالیتی که آن‌ها را دریافت می‌کند ادامه دارند. برنامه‌ای که اجازه‌های دائمی دارد، با تنظیم پرچم در هدف نتیجه، اجازه‌های موقت اعطا می‌کند:

توجه: این پرچم‌ها دسترسی خواندن یا نوشتن عمومی به ارائه‌دهنده‌ای که مرجع آن در شناسه منبع یکسان محتوا قرار دارد نمی‌دهند. دسترسی فقط برای خود URI است.

وقتی شناسه منبع یکسان محتوا را به برنامه دیگری ارسال می‌کنید، حداقل یکی از این پرچم‌ها را اضافه کنید. پرچم‌ها قابلیت‌های زیر را برای هر برنامه‌ای که قصدی را دریافت می‌کند و Android 11 (سطح میانای برنامه‌سازی کاربردی ۳۰) یا بالاتر را هدف قرار می‌دهد، فراهم می‌کنند:

  • بسته به پرچم موجود در هدف، از داده‌هایی که شناسه منبع یکسان محتوا نشان می‌دهد بخواند یا در آن‌ها بنویسد.
  • رؤیت‌پذیری بسته را در برنامه‌ای که حاوی ارائه‌دهنده محتوای منطبق با مرجع URI است به‌دست آورید. برنامه‌ای که قصد را ارسال می‌کند و برنامه‌ای که حاوی ارائه‌دهنده محتوا است ممکن است دو برنامه متفاوت باشند.

ارائه‌دهنده اجازه‌های URI را برای URIهای محتوا در مانیفست خود بااستفاده از android:grantUriPermissions ویژگی <provider> عنصر و همچنین <grant-uri-permission> عنصر فرزند <provider> عنصر تعریف می‌کند. سازوکار اجازه‌های URI با جزئیات بیشتر در راهنمای اجازه‌ها در Android توضیح داده شده است.

برای مثال، می‌توانید داده‌های مخاطبی را در «ارائه‌دهنده مخاطبین» بازیابی کنید، حتی اگر اجازه READ_CONTACTS را نداشته باشید. ممکن است بخواهید این کار را در برنامه‌ای انجام دهید که در روز تولد مخاطب، تبریک الکترونیکی برای او ارسال می‌کند. به‌جای درخواست READ_CONTACTS، که به شما امکان دسترسی به همه مخاطبین کاربر و همه اطلاعات آن‌ها را می‌دهد، به کاربر اجازه دهید کنترل کند که برنامه شما از کدام مخاطبین استفاده کند. برای انجام این کار، از فرایند زیر استفاده کنید:

  1. در برنامه‌تان، هدفی حاوی کنش ACTION_PICK و نوع MIME «مخاطبین» CONTENT_ITEM_TYPE را بااستفاده از روش startActivityForResult() ارسال کنید.
  2. چون این قصد با فیلتر قصد برای فعالیت «انتخاب» برنامه «افراد» مطابقت دارد، فعالیت به پیش‌زمینه می‌آید.
  3. در فعالیت انتخاب، کاربر مخاطبی را برای به‌روزرسانی انتخاب می‌کند. وقتی این اتفاق می‌افتد، فعالیت انتخاب setResult(resultcode, intent) را فراخوانی می‌کند تا هدفی را برای بازگرداندن به برنامه شما تنظیم کند. هدف حاوی شناسه منبع یکسان محتوای مخاطبی است که کاربر انتخاب کرده است و پرچم‌های «اضافی» FLAG_GRANT_READ_URI_PERMISSION. این پرچم‌ها به برنامه شما اجازه URI می‌دهند تا داده‌های مخاطبی را که شناسه منبع یکسان محتوا به آن اشاره می‌کند بخواند. سپس فعالیت انتخاب finish() را فرامی‌خواند تا کنترل را به برنامه‌تان برگرداند.
  4. فعالیت شما به پیش‌زمینه برمی‌گردد و سیستم روش onActivityResult() فعالیت شما را فراخوانی می‌کند. این روش قصد نتیجه ایجادشده توسط فعالیت انتخاب در برنامه «افراد» را دریافت می‌کند.
  5. با شناسه منبع یکسان محتوا از هدف نتیجه، می‌توانید داده‌های مخاطب را از «ارائه‌کننده مخاطبین» بخوانید، حتی اگر در مانیفست خود درخواست اجازه دسترسی خواندن دائمی به ارائه‌کننده نکرده باشید. سپس می‌توانید اطلاعات تاریخ تولد یا نشانی ایمیل مخاطب را دریافت کنید و تبریک الکترونیکی را ارسال کنید.

استفاده از برنامه‌ای دیگر

روش دیگر برای اینکه به کاربر اجازه دهید داده‌هایی را که به آن‌ها اجازه دسترسی ندارید اصلاح کند این است که برنامه‌ای را که این اجازه‌ها را دارد فعال کنید و به کاربر اجازه دهید کار را در آنجا انجام دهد.

برای مثال، برنامه «تقویم» قصد ACTION_INSERT را می‌پذیرد که به شما امکان می‌دهد واسط کاربر درج برنامه را فعال کنید. می‌توانید داده‌های «اضافی» را در این هدف ارسال کنید، که برنامه از آن برای ازپیش تکمیل کردن واسط کاربر استفاده می‌کند. ازآنجایی‌که رویدادهای تکرارشونده نحو پیچیده‌ای دارند، روش ترجیحی برای درج رویدادها در «ارائه‌دهنده تقویم» این است که برنامه «تقویم» را با ACTION_INSERT فعال کنید و سپس به کاربر اجازه دهید رویداد را در آنجا درج کند.

نمایش داده‌ها بااستفاده از برنامه کمکی

اگر برنامه شما اجازه دسترسی دارد، همچنان می‌توانید از هدف برای نمایش داده‌ها در برنامه دیگری استفاده کنید. برای مثال، برنامه «تقویم» ACTION_VIEW قصدی را می‌پذیرد که تاریخ یا رویداد خاصی را نمایش می‌دهد. این کار به شما امکان می‌دهد اطلاعات تقویم را بدون نیاز به ایجاد واسط کاربر خودتان نمایش دهید. برای کسب اطلاعات بیشتر درباره این ویژگی، نمای کلی ارائه‌دهنده تقویم را ببینید.

برنامه‌ای که قصد را به آن ارسال می‌کنید لازم نیست برنامه‌ای باشد که با ارائه‌دهنده مرتبط است. برای مثال، می‌توانید مخاطبی را از «ارائه‌دهنده مخاطب» بازیابی کنید، سپس ACTION_VIEW قصدی حاوی شناسه منبع یکسان محتوا برای تصویر مخاطب به نمایشگر تصویر ارسال کنید.

کلاس‌های قرارداد

کلاس قرارداد ثابت‌هایی را تعریف می‌کند که به برنامه‌ها کمک می‌کند با شناسه منبع یکسان محتوا، نام ستون‌ها، کنش‌های هدف، و دیگر ویژگی‌های ارائه‌دهنده محتوا کار کنند. کلاس‌های قرارداد به‌طور خودکار با ارائه‌دهنده اضافه نمی‌شوند. توسعه‌دهنده ارائه‌دهنده باید آن‌ها را تعریف کند و سپس آن‌ها را برای توسعه‌دهندگان دیگر دردسترس قرار دهد. بسیاری از ارائه‌دهندگان موجود در پلاتفرم Android کلاس‌های قرارداد متناظر در بسته android.provider دارند.

برای مثال، «ارائه‌دهنده فرهنگ لغت کاربر» دارای کلاس قرارداد UserDictionary است که حاوی شناسه منبع یکسان محتوا و ثابت‌های نام ستون است. شناسه منبع یکسان محتوا برای جدول Words در ثابت UserDictionary.Words.CONTENT_URI تعریف شده است. کلاس UserDictionary.Words همچنین حاوی ثابت‌های نام ستون است، که در قطعه‌های نمونه در این راهنما استفاده می‌شود. برای مثال، طرح‌ریزی پُرسمان را می‌توان به‌صورت زیر تعریف کرد:

کاتلین

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

جاوا

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

کلاس قرارداد دیگری ContactsContract برای «ارائه‌کننده مخاطبین» است. اسناد مرجع این کلاس شامل تکه‌کدهای نمونه است. یکی از زیرکلاس‌های آن، ContactsContract.Intents.Insert، کلاس قراردادی است که شامل ثابت‌هایی برای هدف‌ها و داده‌های هدف است.

مرجع نوع MIME

ارائه‌دهندگان محتوا می‌توانند انواع رسانه MIME استاندارد، رشته‌های نوع MIME سفارشی، یا هر دو را برگردانند.

انواع MIME قالب زیر را دارند:

type/subtype

برای مثال، نوع MIME شناخته‌شده text/html دارای نوع text و زیرنوع html است. اگر ارائه‌دهنده این نوع را برای URI برگرداند، به این معنی است که پرسمان بااستفاده از آن URI نوشتاری حاوی برچسب‌های HTML برمی‌گرداند.

رشته‌های نوع MIME سفارشی، که به آن‌ها انواع MIME مختص فروشنده نیز گفته می‌شود، مقادیر type و subtype پیچیده‌تری دارند. برای ردیف‌های چندگانه، مقدار نوع همیشه به‌صورت زیر است:

vnd.android.cursor.dir

برای یک ردیف، مقدار نوع همیشه به‌صورت زیر است:

vnd.android.cursor.item

subtype مختص ارائه‌دهنده است. ارائه‌دهندگان داخلی Android معمولاً زیرنوع ساده‌ای دارند. برای مثال، وقتی برنامه «مخاطبین» ردیفی برای شماره تلفن ایجاد می‌کند، نوع 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:

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

ارائه‌دهنده نوع رسانه : نوع MIME زیر را برمی‌گرداند:

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

اکثر ارائه‌دهندگان محتوا ثابت‌های کلاس قرارداد را برای انواع MIME که استفاده می‌کنند تعریف می‌کنند. برای مثال، کلاس قرارداد ارائه‌کننده مخاطبین ContactsContract.RawContacts ثابت CONTENT_ITEM_TYPE را برای نوع رسانه یک ردیف مخاطب خام تعریف می‌کند.

شناسه‌های منبع یکسان محتوا برای ردیف‌های تکی در بخش شناسه‌های منبع یکسان محتوا شرح داده شده است.