یک ارائه دهنده محتوا دسترسی به یک مخزن مرکزی داده ها را مدیریت می کند. یک ارائه دهنده بخشی از یک برنامه اندروید است که اغلب رابط کاربری خود را برای کار با داده ها ارائه می دهد. با این حال، ارائه دهندگان محتوا در درجه اول توسط برنامه های کاربردی دیگر استفاده می شوند، که با استفاده از شی مشتری ارائه دهنده به ارائه دهنده دسترسی پیدا می کنند. ارائهدهندگان و مشتریان ارائهدهنده با هم یک رابط استاندارد و سازگار برای دادهها ارائه میکنند که همچنین ارتباطات بین فرآیندی و دسترسی امن به دادهها را مدیریت میکند.
معمولاً شما با ارائه دهندگان محتوا در یکی از دو سناریو کار می کنید: پیاده سازی کد برای دسترسی به یک ارائه دهنده محتوای موجود در برنامه دیگر یا ایجاد یک ارائه دهنده محتوای جدید در برنامه خود برای به اشتراک گذاری داده ها با سایر برنامه ها.
این صفحه اصول کار با ارائه دهندگان محتوای موجود را پوشش می دهد. برای آشنایی با پیاده سازی ارائه دهندگان محتوا در برنامه های خود، به ایجاد یک ارائه دهنده محتوا مراجعه کنید.
این موضوع موارد زیر را شرح می دهد:
- نحوه کار ارائه دهندگان محتوا
- API که برای بازیابی داده ها از یک ارائه دهنده محتوا استفاده می کنید.
- API که برای درج، بهروزرسانی یا حذف دادهها در ارائهدهنده محتوا استفاده میکنید.
- سایر ویژگی های API که کار با ارائه دهندگان را تسهیل می کند.
نمای کلی
یک ارائهدهنده محتوا، دادهها را به عنوان یک یا چند جدول به برنامههای خارجی ارائه میکند که مشابه جداول موجود در یک پایگاه داده رابطهای هستند. یک ردیف نمونه ای از انواع داده هایی را نشان می دهد که ارائه دهنده جمع آوری می کند، و هر ستون در ردیف نشان دهنده یک قطعه جداگانه از داده های جمع آوری شده برای یک نمونه است.
یک ارائه دهنده محتوا دسترسی به لایه ذخیره سازی داده در برنامه شما را برای تعدادی از APIها و مؤلفه های مختلف هماهنگ می کند. همانطور که در شکل 1 نشان داده شده است، این موارد شامل موارد زیر است:
- اشتراک گذاری دسترسی به داده های برنامه خود با سایر برنامه ها
- ارسال داده به ویجت
- بازگرداندن پیشنهادهای جستجوی سفارشی برای برنامه شما از طریق چارچوب جستجو با استفاده از
SearchRecentSuggestionsProvider
- همگام سازی داده های برنامه با سرور خود با استفاده از اجرای
AbstractThreadedSyncAdapter
- بارگیری داده ها در رابط کاربری با استفاده از
CursorLoader
دسترسی به یک ارائه دهنده
هنگامی که می خواهید به داده های یک ارائه دهنده محتوا دسترسی داشته باشید، از شی ContentResolver
در Context
برنامه خود برای برقراری ارتباط با ارائه دهنده به عنوان مشتری استفاده می کنید. شی ContentResolver
با شی ارائه دهنده ارتباط برقرار می کند، نمونه ای از کلاسی که ContentProvider
پیاده سازی می کند.
شی ارائه دهنده درخواست های داده از کلاینت ها را دریافت می کند، عمل درخواستی را انجام می دهد و نتایج را برمی گرداند. این شی دارای متدهایی است که متدهایی با نام یکسان در شی ارائه دهنده، نمونه ای از یکی از زیرکلاس های مشخص ContentProvider
را فرا می خواند. متدهای ContentResolver
توابع اصلی "CRUD" (ایجاد، بازیابی، به روز رسانی و حذف) ذخیره سازی دائمی را ارائه می کنند.
یک الگوی رایج برای دسترسی به ContentProvider
از رابط کاربری شما از CursorLoader
برای اجرای یک پرس و جو ناهمزمان در پسزمینه استفاده میکند. Activity
یا Fragment
در UI شما یک CursorLoader
به کوئری فراخوانی می کند که به نوبه خود ContentProvider
با استفاده از ContentResolver
دریافت می کند.
این اجازه میدهد تا زمانی که پرس و جو در حال اجرا است، رابط کاربری همچنان در دسترس کاربر باشد. این الگو شامل تعامل تعدادی از اشیاء مختلف و همچنین مکانیسم ذخیره سازی زیرین است که در شکل 2 نشان داده شده است.
توجه: برای دسترسی به یک ارائه دهنده، برنامه شما معمولاً باید مجوزهای خاصی را در فایل مانیفست خود درخواست کند. این الگوی توسعه با جزئیات بیشتر در بخش مجوزهای ارائه دهنده محتوا توضیح داده شده است.
یکی از ارائه دهندگان داخلی در پلتفرم اندروید، ارائه دهنده دیکشنری کاربر است که کلمات غیر استانداردی را که کاربر می خواهد نگه دارد، ذخیره می کند. جدول 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 مطابقت دارند:
آرگومان 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 با جزئیات بیشتر توضیح داده شده است. همچنین، خطوط کد فقط قطعه قطعه هستند. آنها یک برنامه کامل را نشان نمی دهند.
برای بازیابی داده ها از یک ارائه دهنده، این مراحل اساسی را دنبال کنید:
- درخواست مجوز دسترسی خواندن برای ارائه دهنده.
- کدی را تعریف کنید که درخواستی را برای ارائه دهنده ارسال می کند.
درخواست مجوز دسترسی خواندن
برای بازیابی داده ها از یک ارائه دهنده، برنامه شما نیاز به مجوز دسترسی خواندن برای ارائه دهنده دارد. در زمان اجرا نمی توانید این مجوز را درخواست کنید. در عوض، باید با استفاده از عنصر <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 محتوای خاص هستند که تا پایان فعالیتی که آنها را دریافت می کند، ادامه می یابد. برنامهای که دارای مجوزهای دائمی است، مجوزهای موقت را با تنظیم یک پرچم در هدف نتیجه اعطا میکند:
- مجوز خواندن:
FLAG_GRANT_READ_URI_PERMISSION
- اجازه نوشتن:
FLAG_GRANT_WRITE_URI_PERMISSION
توجه: این پرچمها به ارائهدهندهای که مجوز آن در 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
، که به شما امکان دسترسی به تمام مخاطبین کاربر و همه اطلاعات آنها را می دهد، به کاربر اجازه دهید کنترل کند که برنامه شما از کدام مخاطبین استفاده می کند. برای این کار از روند زیر استفاده کنید:
- در برنامه خود، با استفاده از روش
startActivityForResult()
یک intent حاوی عملACTION_PICK
و نوع MIME "مخاطبین"CONTENT_ITEM_TYPE
ارسال کنید. - از آنجایی که این هدف با فیلتر قصد برای فعالیت «انتخاب» برنامه افراد مطابقت دارد، فعالیت در پیشزمینه قرار میگیرد.
- در فعالیت انتخاب، کاربر مخاطبی را برای به روز رسانی انتخاب می کند. وقتی این اتفاق میافتد، فعالیت انتخاب
setResult(resultcode, intent)
را فراخوانی میکند تا قصدی را برای بازگرداندن به برنامه شما تنظیم کند. هدف شامل URI محتوای مخاطبی است که کاربر انتخاب کرده و پرچمهای «اضافی»FLAG_GRANT_READ_URI_PERMISSION
. این پرچمها اجازه URI را به برنامه شما میدهند تا دادههای مخاطبی را که توسط URI محتوا به آن اشاره شده است بخواند. سپس اکتیویتی انتخاب،finish()
را فراخوانی می کند تا کنترل را به برنامه شما بازگرداند. - اکتیویتی شما به پیش زمینه باز می گردد و سیستم متد
onActivityResult()
فعالیت شما را فراخوانی می کند. این روش هدف نتیجه ایجاد شده توسط فعالیت انتخاب در برنامه People را دریافت می کند. - با 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 های محتوا توضیح داده شده است.