اصول ارائه دهنده محتوا

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

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

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

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

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

نمای کلی

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

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

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

شکل 1. رابطه بین ارائه دهنده محتوا و سایر اجزا.

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

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

شی ارائه دهنده درخواست های داده از کلاینت ها را دریافت می کند، عمل درخواستی را انجام می دهد و نتایج را برمی گرداند. این شی دارای متدهایی است که متدهایی با نام یکسان در شی ارائه دهنده، نمونه ای از یکی از زیرکلاس های مشخص ContentProvider را فرا می خواند. متدهای ContentResolver توابع اصلی "CRUD" (ایجاد، بازیابی، به روز رسانی و حذف) ذخیره سازی دائمی را ارائه می کنند.

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

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

تعامل بین ContentProvider، سایر کلاس ها و ذخیره سازی.

شکل 2. تعامل بین ContentProvider ، سایر کلاس ها و ذخیره سازی.

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

یکی از ارائه دهندگان داخلی در پلتفرم اندروید، ارائه دهنده دیکشنری کاربر است که کلمات غیر استانداردی را که کاربر می خواهد نگه دارد، ذخیره می کند. جدول 1 نشان می دهد که داده ها در جدول این ارائه دهنده چگونه ممکن است به نظر برسند:

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

کلمه شناسه برنامه فرکانس محل _شناسه
mapreduce کاربر 1 100 en_US 1
precompiler کاربر 14 200 fr_FR 2
applet user2 225 fr_CA 3
const کاربر 1 255 pt_BR 4
int user5 100 en_UK 5

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

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

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

آرگومان query() کلمه کلیدی/پارامتر را انتخاب کنید یادداشت ها
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 نما را مشخص می کند.

URI های محتوا

URI محتوا یک URI است که داده ها را در یک ارائه دهنده شناسایی می کند. URI های محتوا شامل نام نمادین کل ارائه دهنده - اختیارات آن - و نامی که به جدول اشاره می کند - یک مسیر است . وقتی یک متد کلاینت را برای دسترسی به جدول در یک ارائه دهنده فراخوانی می کنید، URI محتوای جدول یکی از آرگومان ها است.

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

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

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

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

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

کاتلین

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

جاوا

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

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

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

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

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

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

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

  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 است. اگر پارامتر قابل تعویض را مشخص کنید ? به جای یک مقدار، متد query مقدار را از آرایه آرگومان های انتخاب، که متغیر 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 شما الحاق کند. به عنوان مثال، کاربر می تواند "هیچ چیز؛ 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 تغییر می‌کند، روش‌ها را در یک شی ناظر فعال می‌کنند، یا هر دو را.

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

اگر هیچ ردیفی با معیارهای انتخاب مطابقت نداشته باشد، ارائه‌دهنده یک شی 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 در زبان برنامه نویسی جاوا یا تابع use() در زبان برنامه نویسی Kotlin انجام داد.

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

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

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

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

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

نمونه زیر عنصر <uses-permission> دسترسی خواندن به ارائه دهنده فرهنگ لغت کاربر را درخواست می کند:

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

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

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

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

درج داده ها

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

کاتلین

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

URI محتوایی که در newUri بازگردانده شده است، ردیف جدید اضافه شده را با فرمت زیر شناسایی می کند:

content://user_dictionary/words/<id_value>

<id_value> محتویات _ID برای ردیف جدید است. اکثر ارائه دهندگان می توانند این فرم از URI محتوا را به طور خودکار شناسایی کرده و سپس عملیات درخواستی را در آن ردیف خاص انجام دهند.

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

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

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

قطعه زیر تمام ردیف هایی را که زبان آنها دارای زبان "en" را به a دارای محلی 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() ورودی کاربر را پاکسازی کنید. برای کسب اطلاعات بیشتر در این مورد، بخش Protect against malicious input را بخوانید.

داده ها را حذف کنید

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

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

ارائه دهندگان محتوا می توانند انواع مختلفی از داده ها را ارائه دهند. ارائه دهنده فرهنگ لغت کاربر فقط متن را ارائه می دهد، اما ارائه دهندگان می توانند قالب های زیر را نیز ارائه دهند:

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

نوع داده دیگری که اغلب ارائه دهندگان از آن استفاده می کنند، یک شی بزرگ باینری (BLOB) است که به عنوان یک آرایه بایتی 64 کیلوبایتی پیاده سازی شده است. می توانید انواع داده های موجود را با نگاه کردن به روش های "get" کلاس Cursor مشاهده کنید.

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

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

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

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

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

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

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

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

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

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

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

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

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

دسترسی به داده ها با استفاده از intent

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

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

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

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

وقتی URI محتوا را به برنامه دیگری ارسال می‌کنید، حداقل یکی از این پرچم‌ها را اضافه کنید. پرچم‌ها قابلیت‌های زیر را برای هر برنامه‌ای که intent دریافت می‌کند و Android 11 (سطح API 30) یا بالاتر را هدف قرار می‌دهد ارائه می‌کند:

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

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

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

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

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

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

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

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

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

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

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

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

برای مثال، User Dictionary Provider دارای کلاس قرارداد UserDictionary است که حاوی URI محتوا و ثابت‌های نام ستون است. URI محتوا برای جدول 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 ، یک کلاس قراردادی است که شامل ثابت‌هایی برای intent و داده‌های intent است.

مرجع نوع 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 برای ارائه دهنده خاص است. ارائه دهندگان داخلی اندروید معمولاً یک نوع فرعی ساده دارند. به عنوان مثال، هنگامی که برنامه Contacts یک ردیف برای یک شماره تلفن ایجاد می کند، نوع MIME زیر را در ردیف تنظیم می کند:

vnd.android.cursor.item/phone_v2

مقدار زیرنوع phone_v2 است.

سایر توسعه دهندگان ارائه دهنده می توانند الگوی خود را از انواع فرعی بر اساس اختیارات ارائه دهنده و نام جدول ایجاد کنند. به عنوان مثال، ارائه دهنده ای را در نظر بگیرید که دارای جدول زمانی قطار است. اختیار ارائه دهنده com.example.trains است و شامل جداول Line1، Line2 و Line3 است. در پاسخ به URI محتوای زیر برای جدول Line1:

content://com.example.trains/Line1

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

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

در پاسخ به URI محتوای زیر برای ردیف 5 در جدول Line2:

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

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

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

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

URI های محتوا برای ردیف های تک در بخش URI های محتوا توضیح داده شده است.