يدير موفّر المحتوى إمكانية الوصول إلى مستودع مركزي للبيانات. الموفِّر هو جزء من تطبيق Android، وغالبًا ما يقدّم واجهة مستخدم خاصة به للعمل مع البيانات. ومع ذلك، يتم استخدام موفّري المحتوى بشكل أساسي من قِبل التطبيقات الأخرى التي تصل إلى موفّر المحتوى باستخدام عنصر عميل موفّر. يقدّم موفّرو الخدمات وعملاء موفّري الخدمات معًا واجهة قياسية ومتسقة للبيانات تتعامل أيضًا مع التواصل بين العمليات والوصول الآمن إلى البيانات.
عادةً ما تتعامل مع مقدّمي المحتوى في أحد السيناريوهَين التاليَين: تنفيذ رمز لوصول مقدّم محتوى حالي في تطبيق آخر أو إنشاء مقدّم محتوى جديد في تطبيقك لمشاركة البيانات مع التطبيقات الأخرى.
تتناول هذه الصفحة أساسيات العمل مع مقدّمي المحتوى الحاليين. للتعرّف على كيفية تنفيذ موفّري المحتوى في تطبيقاتك، اطّلِع على مقالة إنشاء موفّر محتوى.
يتناول هذا الموضوع ما يلي:
- آلية عمل موفّري المحتوى
- واجهة برمجة التطبيقات التي تستخدمها لاسترداد البيانات من موفّر محتوى
- واجهة برمجة التطبيقات التي تستخدمها لإدراج البيانات أو تعديلها أو حذفها في أحد موفّري المحتوى
- ميزات أخرى لواجهة برمجة التطبيقات تُسهل العمل مع موفّري الخدمات
نظرة عامة
يعرض موفّر المحتوى البيانات للتطبيقات الخارجية كجدول واحد أو أكثر مشابه للجداول الموجودة في قاعدة بيانات ارتباطية. يمثّل الصفّ مثيلاً لنوع معيّن من البيانات التي يجمعها الموفّر، ويمثّل كلّ عمود في الصفّ جزءًا فرديًا من البيانات التي تمّ جمعها لأحد المثيلات.
ينظّم مقدّم المحتوى الوصول إلى طبقة تخزين البيانات في تطبيقك لعددٍ من واجهات برمجة التطبيقات والمكونات المختلفة. وتشمل هذه العناصر ما يلي، كما هو موضّح في الشكل 1:
- مشاركة إمكانية الوصول إلى بيانات تطبيقك مع تطبيقات أخرى
- إرسال البيانات إلى تطبيق مصغّر
- عرض اقتراحات بحث مخصّصة لتطبيقك من خلال إطار عمل البحث
باستخدام
SearchRecentSuggestionsProvider
- مزامنة بيانات التطبيق مع الخادم باستخدام تنفيذ
AbstractThreadedSyncAdapter
- تحميل البيانات في واجهة المستخدم باستخدام
CursorLoader
الوصول إلى مقدّم خدمة
عندما تريد الوصول إلى البيانات في مقدّم محتوى، يمكنك استخدام عنصر
ContentResolver
في
Context
تطبيقك للتواصل مع الموفّر بصفتك عميلًا. يتواصل العنصر ContentResolver
مع كائن الموفّر، وهو مثال لفئة تنفّذ ContentProvider
.
يتلقّى عنصر "الموفّر"
طلبات البيانات من العملاء، وينفّذ الإجراء المطلوب، ويعرض
النتائج. يحتوي هذا الكائن على طرق تستدعي طرقًا تحمل أسماء متطابقة في كائن موفّر الخدمة، وهو مثيل لواحدة من الفئات الفرعية الملموسة من ContentProvider
. توفّر ContentResolver
وظائف "CRUD" (إنشاء واسترجاع وتعديل وحذف) الأساسية لسعة التخزين الدائمة.
هناك نمط شائع للوصول إلى ContentProvider
من واجهة المستخدم يستخدم CursorLoader
لتنفيذ طلب بحث غير متزامن في الخلفية. يطلب الرمز Activity
أو Fragment
في واجهة المستخدم CursorLoader
إلى طلب البحث، ما يؤدي بدوره إلى ظهور ContentProvider
باستخدام ContentResolver
.
ويسمح ذلك لواجهة المستخدم بمواصلة توفّرها للمستخدم أثناء تنفيذ الطلب. يتضمن هذا النموذج تفاعل عدد من العناصر المختلفة، بالإضافة إلى آلية التخزين الأساسية، كما هو موضّح في الشكل 2.
ملاحظة: للوصول إلى أحد الموفّرين، على تطبيقك عادةً طلب أذونات محدّدة في ملف البيان الخاص به. يمكنك الاطّلاع على تفاصيل أكثر حول نمط التطوير هذا في القسم أذونات مقدّمي المحتوى.
من مقدّمي الخدمات المضمّنين في نظام Android الأساسي هو مقدّم خدمة "معجم المستخدم" الذي يخزّن الكلمات غير العادية التي يريد المستخدم الاحتفاظ بها. يوضّح الجدول 1 شكل البيانات التي قد تظهر في جدول هذا الموفّر:
ألعاب الكلمات | الرقم التعريفي للتطبيق | النشر | اللغة | _ID |
---|---|---|---|---|
mapreduce |
المستخدم 1 | 100 | en_US | 1 |
precompiler |
user14 | 200 | fr_FR | 2 |
applet |
user2 | 225 | fr_CA | 3 |
const |
user1 | 255 | pt_BR | 4 |
int |
المستخدم 5 | 100 | en_UK | 5 |
في الجدول 1، يمثّل كل صف مثيلًا لكلمة لا يمكن
العثور عليها في قاموس عادي. يمثّل كل عمود جزءًا من بيانات تلك الكلمة، مثل
اللغة التي تم العثور عليها فيها لأول مرة. عناوين الأعمدة هي أسماء الأعمدة التي يتم تخزينها في
الموفِّر. للإشارة إلى لغة صف، يمكنك مثلاً الرجوع إلى عمود locale
. بالنسبة إلى
هذا الموفِّر، يعمل عمود _ID
كعمود مفتاح أساسي يديره
الموفِّر تلقائيًا.
للحصول على قائمة بالكلمات ولغاتها من موفِّر "معجم المستخدم"،
يمكنك الاتصال بالرقم ContentResolver.query()
.
تستدعي الطريقة query()
الطريقة
ContentProvider.query()
التي حدّدها
موفّر "قاموس المستخدم". تعرض سطور الرمز التالية
طلب ContentResolver.query()
:
Kotlin
// Queries the UserDictionary and returns results cursor = contentResolver.query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Selection criteria selectionArgs.toTypedArray(), // Selection criteria sortOrder // The sort order for the returned rows )
Java
// Queries the UserDictionary and returns results cursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Selection criteria selectionArgs, // Selection criteria sortOrder); // The sort order for the returned rows
يوضّح الجدول 2 كيفية مطابقة وسيطات
query(Uri,projection,selection,selectionArgs,sortOrder)
لعبارة SELECT في لغة الاستعلام البنيوية (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) للمحتوى للجدول هو إحدى الوسيطات.
في السطور السابقة من الرمز البرمجي، تحتوي القيمة الثابتة
CONTENT_URI
على معرّف الموارد المتسلسل للمحتوى الخاص بجدول
Words
في "موفِّر قاموس المستخدم". يُحلِّل العنصر ContentResolver
سلطة عنوان URL ويستخدمها لتحديد مقدّم الخدمة من خلال مقارنة السلطة بجدول نظام لمقدّمي الخدمات المعروفين. ويمكن للسمة ContentResolver
بعد ذلك إرسال وسيطات طلب البحث إلى الموفّر الصحيح.
تستخدم السمة ContentProvider
جزء المسار من معرّف الموارد المنتظم (URI) للمحتوى من أجل اختيار الجدول المطلوب الوصول إليه. عادةً ما يكون لدى موفّر الخدمة مسار لكل جدول يعرضه.
في سطور الرمز السابقة، يكون معرّف الموارد المنتظم (URI) الكامل للجدول Words
هو:
content://user_dictionary/words
- السلسلة
content://
هي المخطط، وهي متاحة دائمًا وتحدّدها على أنّها معرّف موارد منتظم (URI) للمحتوى. - سلسلة
user_dictionary
هي إذن مقدّم الخدمة. - سلسلة
words
هي مسار الجدول.
يتيح لك العديد من مزوِّدي الخدمة الوصول إلى صف واحد في جدول من خلال إلحاق قيمة معرّف بنهاية معرّف الموارد المنتظم (URI). على سبيل المثال، لاسترداد صفّ _ID
هو
4
من مقدّم "معجم المستخدم"، يمكنك استخدام معرّف الموارد الموحّد للمحتوى التالي:
Kotlin
val singleUri: Uri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI, 4)
Java
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
غالبًا ما تستخدم قيم المعرّفات عند استرداد مجموعة من الصفوف ثم تريد تعديل أحدها أو حذفه.
ملاحظة: تحتوي فئتَا Uri
وUri.Builder
على طُرق ملائمة لإنشاء عناصر عناوين URI ذات بنية سليمة من السلاسل. تحتوي فئة
ContentUris
على طرق ملائمة لإلحاق قيم المعرّفات بأحد
معرّفات الموارد المنتظمة. يستخدم المقتطف السابق withAppendedId()
لإلحاق معرّف بمعرّف الموارد المنتظم (URI) لمحتوى "مقدّم قاموس المستخدم".
استرداد البيانات من مقدّم الخدمة
يوضّح هذا القسم كيفية استرداد البيانات من أحد الموفّرين باستخدام "موفّر قاموس المستخدم" كمثال.
للتوضيح، تستدعي مقتطفات الرموز البرمجية في هذا القسم
ContentResolver.query()
في سلسلة مهام واجهة المستخدم. في
رمز البرنامج الفعلي، ومع ذلك، يمكنك إجراء طلبات البحث بشكل غير متزامن في سلسلة محادثات منفصلة. ويمكنك استخدام الفئة CursorLoader
والموضّحة
بمزيد من التفصيل في دليل
التحميلات. بالإضافة إلى ذلك، تكون سطور التعليمات البرمجية مقتطفات فقط. لا تعرض هذه التطبيقات طلبًا كاملاً.
لاسترداد البيانات من مقدّم خدمة، اتّبِع الخطوات الأساسية التالية:
- اطلب من موفِّر الخدمة إذن الوصول للقراءة.
- حدِّد الرمز الذي يُرسِل طلب بحث إلى مقدّم الخدمة.
طلب إذن الوصول للقراءة
لاسترداد البيانات من مقدّم خدمة، يحتاج تطبيقك إلى إذن بالوصول للقراءة من أجل
مقدّم الخدمة. لا يمكنك طلب هذا الإذن في وقت التشغيل. وبدلاً من ذلك، عليك تحديد أنّك بحاجة إلى هذا الإذن في ملف البيان، وذلك باستخدام العنصر <uses-permission>
واسم الإذن المحدّد الذي يحدّده مقدّم الخدمة.
عندما تحدد هذا العنصر في ملف البيان، أنت تطلب هذا الإذن لتطبيقك. عندما يُثبِّت المستخدمون تطبيقك، يمنحون ضمنيًا هذا الطلب.
للعثور على الاسم الدقيق لإذن الوصول للقراءة الخاص بالموفِّر الذي تستخدمه، بالإضافة إلى أسماء أذونات الوصول الأخرى التي يستخدمها الموفِّر، يمكنك الرجوع إلى مستندات الموفِّر.
يمكن الاطّلاع بمزيد من التفاصيل على دور الأذونات في الوصول إلى موفّري المحتوى في قسم أذونات موفّر المحتوى.
يحدّد موفِّر قاموس المستخدم الإذن android.permission.READ_USER_DICTIONARY
في ملف البيان، وبالتالي على أي تطبيق يريد القراءة من الموفِّر أن يطلب هذا الإذن.
إنشاء الاستعلام
الخطوة التالية في استرداد البيانات من مقدّم الخدمة هي إنشاء طلب بحث. تحدِّد المقتطف التالي بعض المتغيّرات للوصول إلى مقدّم "معجم المستخدم":
Kotlin
// A "projection" defines the columns that are returned for each row private val mProjection: Array<String> = arrayOf( UserDictionary.Words._ID, // Contract class constant for the _ID column name UserDictionary.Words.WORD, // Contract class constant for the word column name UserDictionary.Words.LOCALE // Contract class constant for the locale column name ) // Defines a string to contain the selection clause private var selectionClause: String? = null // Declares an array to contain selection arguments private lateinit var selectionArgs: Array<String>
Java
// A "projection" defines the columns that are returned for each row String[] mProjection = { UserDictionary.Words._ID, // Contract class constant for the _ID column name UserDictionary.Words.WORD, // Contract class constant for the word column name UserDictionary.Words.LOCALE // Contract class constant for the locale column name }; // Defines a string to contain the selection clause String selectionClause = null; // Initializes an array to contain selection arguments String[] selectionArgs = {""};
يعرض المقتطف التالي كيفية استخدام ContentResolver.query()
، مع الاستعانة بمقدِّم قاموس المستخدم كمثال. يشبه طلب بحث العميل مقدّم الخدمة طلب بحث SQL، ويحتوي على
مجموعة من الأعمدة المطلوب عرضها ومجموعة من معايير الاختيار وترتيب الترتيب.
تُعرف مجموعة الأعمدة التي يعرضها طلب البحث باسم إسقاط، ويكون المتغيّر هو mProjection
.
يتم تقسيم التعبير الذي يحدّد الصفوف التي سيتم استرجاعها إلى عبارة اختيار و
وسيطات اختيار. عبارة الاختيار هي عبارة منطقية وعبارة منطقية ثنائية القيمة،
أسماء الأعمدة والقيم. المتغيّر هو mSelectionClause
. إذا حدّدت المَعلمة القابلة للاستبدال ?
بدلاً من قيمة، تسترجع طريقة طلب البحث القيمة
من صفيف وسيطات الاختيار، وهو المتغيّر mSelectionArgs
.
في المقتطف التالي، إذا لم يُدخل المستخدم أي كلمة، سيتم ضبط عبارة الاختيار على null
ويعرض طلب البحث جميع الكلمات الواردة في مقدّم الخدمة. إذا أدخل المستخدم كلمة، يتم ضبط عبارة الاختيار على UserDictionary.Words.WORD + " = ?"
ويتم ضبط العنصر الأول في مصفوفة وسيطات الاختيار على الكلمة التي يُدخلها المستخدم.
Kotlin
/* * This declares a String array to contain the selection arguments. */ private lateinit var selectionArgs: Array<String> // Gets a word from the UI searchString = searchWord.text.toString() // Insert code here to check for invalid or malicious input // If the word is the empty string, gets everything selectionArgs = searchString?.takeIf { it.isNotEmpty() }?.let { selectionClause = "${UserDictionary.Words.WORD} = ?" arrayOf(it) } ?: run { selectionClause = null emptyArray<String>() } // Does a query against the table and returns a Cursor object mCursor = contentResolver.query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Either null or the word the user entered selectionArgs, // Either empty or the string the user entered sortOrder // The sort order for the returned rows ) // Some providers return null if an error occurs, others throw an exception when (mCursor?.count) { null -> { /* * Insert code here to handle the error. Be sure not to use the cursor! * You might want to call android.util.Log.e() to log this error. */ } 0 -> { /* * Insert code here to notify the user that the search is unsuccessful. This isn't * necessarily an error. You might want to offer the user the option to insert a new * row, or re-type the search term. */ } else -> { // Insert code here to do something with the results } }
Java
/* * This defines a one-element String array to contain the selection argument. */ String[] selectionArgs = {""}; // Gets a word from the UI searchString = searchWord.getText().toString(); // Remember to insert code here to check for invalid or malicious input // If the word is the empty string, gets everything if (TextUtils.isEmpty(searchString)) { // Setting the selection clause to null returns all words selectionClause = null; selectionArgs[0] = ""; } else { // Constructs a selection clause that matches the word that the user entered selectionClause = UserDictionary.Words.WORD + " = ?"; // Moves the user's input string to the selection arguments selectionArgs[0] = searchString; } // Does a query against the table and returns a Cursor object mCursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Either null or the word the user entered selectionArgs, // Either empty or the string the user entered sortOrder); // The sort order for the returned rows // Some providers return null if an error occurs, others throw an exception if (null == mCursor) { /* * Insert code here to handle the error. Be sure not to use the cursor! You can * call android.util.Log.e() to log this error. * */ // If the Cursor is empty, the provider found no matches } else if (mCursor.getCount() < 1) { /* * Insert code here to notify the user that the search is unsuccessful. This isn't necessarily * an error. You can offer the user the option to insert a new row, or re-type the * search term. */ } else { // Insert code here to do something with the results }
هذا الاستعلام مشابه لعبارة SQL التالية:
SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
في عبارة SQL هذه، يتم استخدام أسماء الأعمدة الفعلية بدلاً من الثوابت الخاصة بفئة العقد.
الحماية من الإدخال الضار
إذا كانت البيانات التي يديرها مقدّم المحتوى موجودة في قاعدة بيانات SQL، يمكن أن يؤدي تضمين بيانات خارجية غير موثوق بها في عبارات SQL الأوّلية إلى حدوث عملية حقن SQL.
راجِع عبارة الاختيار التالية:
Kotlin
// Constructs a selection clause by concatenating the user's input to the column name var selectionClause = "var = $mUserInput"
Java
// Constructs a selection clause by concatenating the user's input to the column name String selectionClause = "var = " + userInput;
وفي حال إجراء ذلك، ستسمح للمستخدم بربط رمز SQL ضار بعبارة SQL.
على سبيل المثال، يمكن للمستخدم إدخال "nothing; DROP TABLE *;" لـ mUserInput
، ما يؤدي إلى ظهور عبارة الاختيار var = nothing; DROP TABLE *;
.
بما أنّه تتم معالجة عبارة الاختيار كعبارة SQL، قد يؤدي ذلك إلى محو الموفِّر لجميع الجداول في قاعدة بيانات SQLite الأساسية، ما لم يتم إعداد الموفِّر لرصد محاولات إدخال رموز SQL.
لتجنُّب هذه المشكلة، استخدِم عبارة اختيار تستخدم ?
كمَعلمة قابلة للاستبدال ومجموعة منفصلة من وسيطات الاختيار. بهذه الطريقة، يتم ربط إدخال المستخدم
مباشرةً بالاستعلام بدلاً من تفسيره كجزء من عبارة SQL.
ولأنه لا يتم التعامل معه كلغة SQL، لا يمكن لإدخال المستخدم أن يقوم بإدخال لغة SQL ضارة. وبدلاً من استخدام ميزة التسلسل لتضمين البيانات التي أدخلها المستخدم، يمكنك استخدام عبارة الاختيار التالية:
Kotlin
// Constructs a selection clause with a replaceable parameter var selectionClause = "var = ?"
Java
// Constructs a selection clause with a replaceable parameter String selectionClause = "var = ?";
اضبط صفيف وسيطات الاختيار على النحو التالي:
Kotlin
// Defines a mutable list to contain the selection arguments var selectionArgs: MutableList<String> = mutableListOf()
Java
// Defines an array to contain the selection arguments String[] selectionArgs = {""};
ضع قيمة في صفيف وسيطات الاختيار على النحو التالي:
Kotlin
// Adds the user's input to the selection argument selectionArgs += userInput
Java
// Sets the selection argument to the user's input selectionArgs[0] = userInput;
إنّ عبارة الاختيار التي تستخدِم ?
كمَعلمة قابلة للاستبدال ومجموعة من
وسيطات الاختيار هي الطريقة المفضّلة لتحديد اختيار، حتى إذا لم يكن مقدّم الخدمة
يستند إلى قاعدة بيانات SQL.
عرض نتائج طلبات البحث
تُعرِض طريقة ContentResolver.query()
للعملاء دائمًا
Cursor
يحتوي على الأعمدة المحدّدة من خلال ContentResolver.query()
للصفوف التي تتطابق مع معايير اختيار ContentResolver.query()
. يقدّم عنصر
Cursor
إذن وصول عشوائي للقراءة إلى الصفوف والأعمدة التي يحتوي عليها.
باستخدام طُرق Cursor
، يمكنك تكرار الصفوف في النتائج وتحديد نوع البيانات في كل عمود واستخراج البيانات من العمود وفحص السمات الأخرى للنتائج.
تعمل بعض عمليات تنفيذ Cursor
تلقائيًا على
تعديل الكائن عند تغيُّر بيانات الموفِّر، أو تنشيط الطرق في كائن مراقب
عند تغيُّر Cursor
، أو كليهما.
ملاحظة: يمكن لموفّر الخدمة حظر الوصول إلى الأعمدة استنادًا إلى طبيعة العنصر الذي يُجري طلب البحث. على سبيل المثال، يحظر "مقدِّم جهات الاتصال" الوصول إلى بعض الأعمدة لمزامنة المحوّلات، وبذلك لا يُرجعها إلى نشاط أو خدمة.
إذا لم تتطابق أي صفوف مع معايير الاختيار، يعرض الموفِّر
عنصر Cursor
الذي يكون فيه
Cursor.getCount()
هو
0، أي مؤشر فارغ.
في حال حدوث خطأ داخلي، تعتمد نتائج طلب البحث على مقدّم الخدمة المحدّد. قد يؤدي ذلك إلى
عرض القيمة null
أو طرح Exception
.
بما أنّ Cursor
هي قائمة بالصفوف، فإنّ طريقة جيدة لعرض
محتوى Cursor
هي ربطها بجدول ListView
باستخدام SimpleCursorAdapter
.
يواصل المقتطف التالي الرمز البرمجي من المقتطف السابق. ينشئ الإجراء كائنًا
SimpleCursorAdapter
يحتوي على Cursor
الذي استرجاعه طلب البحث، ويضبط هذا الكائن ليكون المحوِّل لمحاولة
ListView
.
Kotlin
// Defines a list of columns to retrieve from the Cursor and load into an output row val wordListColumns : Array<String> = arrayOf( UserDictionary.Words.WORD, // Contract class constant containing the word column name UserDictionary.Words.LOCALE // Contract class constant containing the locale column name ) // Defines a list of View IDs that receive the Cursor columns for each row val wordListItems = intArrayOf(R.id.dictWord, R.id.locale) // Creates a new SimpleCursorAdapter cursorAdapter = SimpleCursorAdapter( applicationContext, // The application's Context object R.layout.wordlistrow, // A layout in XML for one row in the ListView mCursor, // The result from the query wordListColumns, // A string array of column names in the cursor wordListItems, // An integer array of view IDs in the row layout 0 // Flags (usually none are needed) ) // Sets the adapter for the ListView wordList.setAdapter(cursorAdapter)
Java
// Defines a list of columns to retrieve from the Cursor and load into an output row String[] wordListColumns = { UserDictionary.Words.WORD, // Contract class constant containing the word column name UserDictionary.Words.LOCALE // Contract class constant containing the locale column name }; // Defines a list of View IDs that receive the Cursor columns for each row int[] wordListItems = { R.id.dictWord, R.id.locale}; // Creates a new SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter( getApplicationContext(), // The application's Context object R.layout.wordlistrow, // A layout in XML for one row in the ListView mCursor, // The result from the query wordListColumns, // A string array of column names in the cursor wordListItems, // An integer array of view IDs in the row layout 0); // Flags (usually none are needed) // Sets the adapter for the ListView wordList.setAdapter(cursorAdapter);
ملاحظة: للرجوع إلى ListView
باستخدام
Cursor
، يجب أن يحتوي المؤشر على عمود باسم _ID
.
لهذا السبب، يسترجع الاستعلام الذي سبق عرضه عمود _ID
لجدول
Words
، على الرغم من أنّ ListView
لا يعرضه.
ويفسّر هذا الشرط أيضًا سبب تضمين عمود _ID
في كل جدول من جداول مقدّمي الخدمة.
الحصول على البيانات من نتائج طلبات البحث
بالإضافة إلى عرض نتائج طلبات البحث، يمكنك استخدامها في مهام أخرى. على سبيل المثال، يمكنك استرداد التهجئة من موفّر قاموس المستخدم، ثم البحث عنها لدى مقدّمي الخدمة الآخرين. لإجراء ذلك، يمكنك تكرار الصفوف في Cursor
، كما هو موضّح في المثال التالي:
Kotlin
/* * Only executes if the cursor is valid. The User Dictionary Provider returns null if * an internal error occurs. Other providers might throw an Exception instead of returning null. */ mCursor?.apply { // Determine the column index of the column named "word" val index: Int = getColumnIndex(UserDictionary.Words.WORD) /* * Moves to the next row in the cursor. Before the first movement in the cursor, the * "row pointer" is -1, and if you try to retrieve data at that position you get an * exception. */ while (moveToNext()) { // Gets the value from the column newWord = getString(index) // Insert code here to process the retrieved word ... // End of while loop } }
Java
// Determine the column index of the column named "word" int index = mCursor.getColumnIndex(UserDictionary.Words.WORD); /* * Only executes if the cursor is valid. The User Dictionary Provider returns null if * an internal error occurs. Other providers might throw an Exception instead of returning null. */ if (mCursor != null) { /* * Moves to the next row in the cursor. Before the first movement in the cursor, the * "row pointer" is -1, and if you try to retrieve data at that position you get an * exception. */ while (mCursor.moveToNext()) { // Gets the value from the column newWord = mCursor.getString(index); // Insert code here to process the retrieved word ... // End of while loop } } else { // Insert code here to report an error if the cursor is null or the provider threw an exception }
Cursor
تحتوي عمليات التنفيذ على عدة طرق "get" ل retrieving different types of data from the object. على سبيل المثال، يستخدم المقتطف السابق
السمة getString()
. وتتضمن أيضًا
طريقة getType()
تعرض قيمة تشير إلى
نوع البيانات في العمود.
تحرير موارد نتائج طلب البحث
يجب
إغلاق عناصر Cursor
إذا لم تعد مطلوبة، حتى يتم تحرير الموارد المرتبطة بها
في وقت أقرب. ويمكن إجراء ذلك إما من خلال استدعاء
close()
أو باستخدام
عبارة try-with-resources
في لغة البرمجة Java أو دالة
use()
في لغة البرمجة Kotlin.
أذونات موفّري المحتوى
يمكن لتطبيق الموفِّر تحديد الأذونات التي يجب أن تحصل عليها التطبيقات الأخرى ل الوصول إلى بيانات الموفِّر. تتيح هذه الأذونات للمستخدم معرفة البيانات التي يحاول التطبيق الوصول إليها. استنادًا إلى متطلبات مقدّم الخدمة، تطلب التطبيقات الأخرى الأذونات التي تحتاجها للوصول إلى مقدّم الخدمة. تظهر الأذونات المطلوبة للمستخدمين النهائيين عند تثبيت التطبيق.
إذا لم يحدِّد تطبيق مقدِّم الخدمة أي أذونات، لن تتمكّن التطبيقات الأخرى من الوصول إلى بيانات مقدِّم الخدمة، ما لم يتم تصدير مقدِّم الخدمة. بالإضافة إلى ذلك، تحصل المكوّنات في تطبيق الموفِّر دائمًا على إذن بالوصول الكامل للقراءة والكتابة، بغض النظر عن الأذونات المحدّدة.
يتطلب مقدّم "قاموس المستخدم" الإذن
android.permission.READ_USER_DICTIONARY
لاسترداد البيانات منه.
يمتلك مقدّم الخدمة إذنًا منفصلاً android.permission.WRITE_USER_DICTIONARY
لإدراج البيانات أو تعديلها أو حذفها.
للحصول على الأذونات اللازمة للوصول إلى مقدّم خدمة، يطلبها التطبيق باستخدام عنصر
<uses-permission>
في ملف البيان. عندما يُثبِّت "مدير حِزم Android" التطبيق، على المستخدم
الموافقة على جميع الأذونات التي يطلبها التطبيق. إذا وافق المستخدم على هذه الحِزم،
يواصل "مدير الحِزم" عملية التثبيت. إذا لم يوافق المستخدم على هذه التغييرات، يوقف "مدير الحِزم" عملية التثبيت.
يطلب عنصر النموذج التالي
<uses-permission>
إذن الوصول للقراءة إلى مقدّم "معجم المستخدم":
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
يمكنك الاطّلاع على المزيد من التفاصيل حول تأثير الأذونات في إمكانية وصول مقدّم الخدمة في مقالة نصائح حول الأمان.
إدراج البيانات وتعديلها وحذفها
بالطريقة نفسها التي تستردّ بها البيانات من موفّر، يمكنك أيضًا استخدام التفاعل بين
تطبيق العميل الخاص بالموفّر وContentProvider
الخاص بالموفّر لتعديل البيانات.
تستدعي طريقة ContentResolver
باستخدام وسيطات يتم تمريرها إلى
الطريقة المقابلة في ContentProvider
. يتعامل موفِّر الخدمة وموفِّر العميد
تلقائيًا مع الأمان والتواصل بين العمليات.
إدخال البيانات
لإدراج بيانات في أحد الموفّرين، يمكنك استدعاء الطريقة ContentResolver.insert()
. تُدرج هذه الطريقة صفًا جديدًا في الموفِّر وتُرجع عنوان URL للمحتوى لذلك الصف.
يوضّح المقتطف التالي كيفية إدراج كلمة جديدة في مقدّم قاموس المستخدم:
Kotlin
// Defines a new Uri object that receives the result of the insertion lateinit var newUri: Uri ... // Defines an object to contain the new values to insert val newValues = ContentValues().apply { /* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value". */ put(UserDictionary.Words.APP_ID, "example.user") put(UserDictionary.Words.LOCALE, "en_US") put(UserDictionary.Words.WORD, "insert") put(UserDictionary.Words.FREQUENCY, "100") } newUri = contentResolver.insert( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI newValues // The values to insert )
Java
// Defines a new Uri object that receives the result of the insertion Uri newUri; ... // Defines an object to contain the new values to insert ContentValues newValues = new ContentValues(); /* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value". */ newValues.put(UserDictionary.Words.APP_ID, "example.user"); newValues.put(UserDictionary.Words.LOCALE, "en_US"); newValues.put(UserDictionary.Words.WORD, "insert"); newValues.put(UserDictionary.Words.FREQUENCY, "100"); newUri = getContentResolver().insert( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI newValues // The values to insert );
يتم نقل بيانات الصف الجديد إلى عنصر ContentValues
واحد، وهو
مشابه من حيث الشكل لمؤشر صف واحد. لا يلزم أن تتضمّن الأعمدة في هذا العنصر
نوع البيانات نفسه، وإذا كنت لا تريد تحديد قيمة على الإطلاق، يمكنك ضبط عمود
على null
باستخدام ContentValues.putNull()
.
لا يضيف المقتطف السابق عمود _ID
، لأنّه يتم الاحتفاظ بهذا العمود
تلقائيًا. يخصّص مقدّم الخدمة قيمة فريدة _ID
لكل صف تتم його إضافة. يستخدم مقدّمو الخدمات هذه القيمة عادةً كمفتاح أساسي للجدول.
يحدِّد معرّف الموارد المنتظم للمحتوى الذي يتم إرجاعه في newUri
الصف الذي تمت إضافته حديثًا باستخدام
التنسيق التالي:
content://user_dictionary/words/<id_value>
تمثّل السمة <id_value>
محتوى _ID
في الصف الجديد.
يمكن لمعظم مقدّمي الخدمات رصد هذا النوع من عناوين URL للمحتوى تلقائيًا ثم تنفيذ العملية المطلوبة
على هذا الصفّ المحدّد.
للحصول على قيمة _ID
من Uri
المعروضة، يمكنك الاتصال بـ
ContentUris.parseId()
.
تحديث البيانات
لتعديل صف، استخدِم عنصر ContentValues
مع القيم المعدَّلة
، تمامًا كما تفعل مع الإدراج ومعايير الاختيار، كما تفعل مع طلب البحث.
طريقة العميل التي تستخدمها هي
ContentResolver.update()
. ما عليك سوى إضافة
قيم إلى عنصر ContentValues
للأعمدة التي تعدّلها. إذا أردت محو محتوى عمود، اضبط القيمة على null
.
يغيّر المقتطف التالي جميع الصفوف التي تم ضبط لغتها على "en"
إلى لغة null
. القيمة المعروضة هي عدد الصفوف التي تم تعديلها.
Kotlin
// Defines an object to contain the updated values val updateValues = ContentValues().apply { /* * Sets the updated value and updates the selected words. */ putNull(UserDictionary.Words.LOCALE) } // Defines selection criteria for the rows you want to update val selectionClause: String = UserDictionary.Words.LOCALE + "LIKE ?" val selectionArgs: Array<String> = arrayOf("en_%") // Defines a variable to contain the number of updated rows var rowsUpdated: Int = 0 ... rowsUpdated = contentResolver.update( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI updateValues, // The columns to update selectionClause, // The column to select on selectionArgs // The value to compare to )
Java
// Defines an object to contain the updated values ContentValues updateValues = new ContentValues(); // Defines selection criteria for the rows you want to update String selectionClause = UserDictionary.Words.LOCALE + " LIKE ?"; String[] selectionArgs = {"en_%"}; // Defines a variable to contain the number of updated rows int rowsUpdated = 0; ... /* * Sets the updated value and updates the selected words. */ updateValues.putNull(UserDictionary.Words.LOCALE); rowsUpdated = getContentResolver().update( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI updateValues, // The columns to update selectionClause, // The column to select on selectionArgs // The value to compare to );
عالج إدخال المستخدم عند استدعاء
ContentResolver.update()
. لمزيد من المعلومات حول
هذا الموضوع، يُرجى الاطّلاع على قسم الحماية من الإدخال الضار.
حذف البيانات
يشبه حذف الصفوف استرداد بيانات الصفوف. يمكنك تحديد معايير اختيار للصفوف
التي تريد حذفها، وتُعرِض طريقة العميل عدد الصفوف المحذوفة.
تحذف المقتطف التالي الصفوف التي يتطابق معرّف تطبيقها مع "user"
. تُرجع الطريقة عدد الصفوف المحذوفة.
Kotlin
// Defines selection criteria for the rows you want to delete val selectionClause = "${UserDictionary.Words.APP_ID} LIKE ?" val selectionArgs: Array<String> = arrayOf("user") // Defines a variable to contain the number of rows deleted var rowsDeleted: Int = 0 ... // Deletes the words that match the selection criteria rowsDeleted = contentResolver.delete( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI selectionClause, // The column to select on selectionArgs // The value to compare to )
Java
// Defines selection criteria for the rows you want to delete String selectionClause = UserDictionary.Words.APP_ID + " LIKE ?"; String[] selectionArgs = {"user"}; // Defines a variable to contain the number of rows deleted int rowsDeleted = 0; ... // Deletes the words that match the selection criteria rowsDeleted = getContentResolver().delete( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI selectionClause, // The column to select on selectionArgs // The value to compare to );
عالج إدخال المستخدم عند استدعاء
ContentResolver.delete()
. لمعرفة المزيد من المعلومات عن
هذا الموضوع، يُرجى الاطّلاع على القسم الحماية من الإدخال الضار.
أنواع بيانات مقدّمي الخدمات
يمكن لمزودي المحتوى توفير العديد من أنواع البيانات المختلفة. لا يوفّر مقدّم "معجم المستخدم" سوى نص، ولكن يمكن للمقدّمين أيضًا تقديم التنسيقات التالية:
- عدد صحيح
- عدد صحيح طويل (long)
- النقطة العائمة
- نقطة عائمة طويلة (مزدوجة)
هناك نوع آخر من البيانات يستخدمه مقدّمو الخدمات غالبًا، وهو عنصر ثنائي كبير (BLOB) يتم تنفيذه على هيئة ملف برمجي
يحتوي على صفيف بايت بحجم 64 كيلوبايت. يمكنك الاطّلاع على أنواع البيانات المتاحة من خلال الاطّلاع على طرق "get" في فئة
Cursor
.
عادةً ما يتم سرد نوع البيانات لكل عمود في الموفر في وثائقه.
يتم إدراج أنواع البيانات لموفّر "معجم المستخدم" في المستندات المرجعية
الخاصة بفئة العقد UserDictionary.Words
. تم شرح صفوف العقود
في قسم صفوف العقود.
يمكنك أيضًا تحديد نوع البيانات من خلال الاتصال بالرقم Cursor.getType()
.
ويحتفظ موفّرو الخدمات أيضًا بمعلومات نوع بيانات MIME لكل عنوان URI للمحتوى يتم تحديده. يمكنك استخدام معلومات نوع MIME لمعرفة ما إذا كان تطبيقك يمكنه معالجة البيانات التي يوفّرها مقدّم الخدمة أو لاختيار نوع المعالجة استنادًا إلى نوع بروتوكول MIME. تحتاج عادةً إلى نوع MIME عند العمل مع مقدّم خدمة يحتوي على هياكل أو ملفات data معقدة.
على سبيل المثال، يستخدم الجدول ContactsContract.Data
في "مقدِّم خدمات جهات الاتصال" أنواع MIME لتصنيف نوع بيانات جهة الاتصال المخزّنة في كل
صف. للحصول على نوع MIME يتوافق مع معرّف الموارد المنتظم (URI) الخاص بالمحتوى، يمكنك استدعاء ContentResolver.getType()
.
يوضّح قسم مرجع أنواع MIME نحو أنواع MIME العادية والمخصّصة.
الطرق البديلة لوصول مقدّمي الخدمات
هناك ثلاثة أشكال بديلة للوصول إلى مقدّم الخدمة مهمة في تطوير التطبيقات:
-
الوصول إلى الدفعات: يمكنك إنشاء مجموعة من طلبات الوصول باستخدام الطرق في
فئة
ContentProviderOperation
ثم تطبيقها باستخدامContentResolver.applyBatch()
. -
طلبات البحث غير المتزامنة: يمكنك إجراء طلبات البحث في سلسلة محادثات منفصلة. يمكنك
استخدام عنصر
CursorLoader
. توضّح الأمثلة الواردة في دليل أدوات التحميل كيفية تنفيذ ذلك. - الوصول إلى البيانات باستخدام النوايا: على الرغم من أنّه لا يمكنك إرسال نية مباشرةً إلى مقدّم الخدمة، يمكنك إرسال نية إلى تطبيق مقدّم الخدمة الذي يمتلك عادةً أفضل الأدوات لتعديل بيانات مقدّم الخدمة.
يتم توضيح إمكانية الوصول المجمّع والتعديل باستخدام الأهداف في الأقسام التالية.
الوصول إلى الدفعات
يكون الوصول المجمّع إلى مقدّم الخدمة مفيدًا لإدراج عدد كبير من الصفوف، لإدراج صفوف في جداول متعددة في طلب الطريقة نفسه، وبشكل عام لتنفيذ مجموعة من العمليات على مستوى حدود العملية كعملية، تُعرف باسم العملية الذرية.
للوصول إلى موفّر في وضع الحِزم،
أنشئ صفيفًا من عناصر ContentProviderOperation
ثم
أرسِله إلى موفّر محتوى باستخدام
ContentResolver.applyBatch()
. يتم تمرير
الإذن الذي يملكها موفّر المحتوى إلى هذه الطريقة، بدلاً من عنوان URI معيّن للمحتوى.
يسمح ذلك لكل عنصر ContentProviderOperation
في المصفوفة بالعمل مع جدول مختلف. تؤدي الدعوة إلى ContentResolver.applyBatch()
إلى عرض صفيف من النتائج.
يتضمّن وصف فئة العقد ContactsContract.RawContacts
مقتطف رمز يوضّح عملية الإدراج المجمّع.
الوصول إلى البيانات باستخدام عناصر intent
يمكن أن توفّر النوايا إمكانية وصول غير مباشرة إلى مقدّم محتوى. يمكنك السماح للمستخدم بالوصول إلى بيانات في مقدّم خدمة حتى إذا لم يكن تطبيقك يملك أذونات الوصول، وذلك إما من خلال استرداد نية نتيجة من تطبيق يملك أذونات أو من خلال تفعيل تطبيق يملك أذونات والسماح للمستخدم بتنفيذ مهام فيه.
الحصول على إذن الوصول باستخدام أذونات مؤقتة
يمكنك الوصول إلى البيانات في موفّر محتوى، حتى إذا لم تكن لديك أذونات الوصول المناسبة، وذلك من خلال إرسال طلب إلى تطبيق يملك الأذونات المطلوبة، ثم تلقّي طلب نتيجة يحتوي على أذونات عناوين URI. هذه هي الأذونات لعنوان URI محدد للمحتوى تستمر إلى أن ينتهي النشاط الذي يتلقّى هذه الأذونات. يمنح التطبيق الذي يمتلك أذونات دائمة أذونات مؤقتة من خلال ضبط علامة في نية النتيجة:
-
إذن القراءة:
FLAG_GRANT_READ_URI_PERMISSION
-
إذن التعديل:
FLAG_GRANT_WRITE_URI_PERMISSION
ملاحظة: لا تمنح هذه العلامات إذن وصول عامًا للقراءة أو الكتابة إلى الموفّر الذي تتضمّن سلطته عنوان URI للمحتوى. لا يشمل إذن الوصول سوى عنوان URL نفسه.
عند إرسال معرّفات موارد منتظمة للمحتوى إلى تطبيق آخر، يجب تضمين واحد على الأقل من هذه العلامات. توفّر العلامات الإمكانات التالية لأي تطبيق يتلقّى طلبًا ويستهدف الإصدار 11 من نظام التشغيل Android (المستوى 30 لواجهة برمجة التطبيقات) أو إصدارًا أحدث:
- قراءة البيانات التي يمثّلها عنوان URL للمحتوى أو الكتابة فيها، بناءً على العلامة المضمّنة في النية
- الحصول على إذن بالوصول إلى الحزمة في التطبيق الذي يحتوي على موفِّر المحتوى الذي يتطابق مع سلطة عنوان URL قد يكون التطبيق الذي يُرسِل الطلب والتطبيق الذي يحتوي على مقدّم المحتوى تطبيقَين مختلفَين.
يحدّد الموفِّر أذونات معرّف الموارد المنتظم (URI) لمعرّفات الموارد المنتظمة (URI) الخاصة بالمحتوى في ملف البيان الخاص به، وذلك باستخدام السمة
android:grantUriPermissions
للعنصر <provider>
والعنصر الفرعي
<grant-uri-permission>
للعنصر <provider>
. يمكنك الاطّلاع على مزيد من التفاصيل حول آلية أذونات عناوين URL في دليل
الأذونات على Android.
على سبيل المثال، يمكنك استرداد بيانات جهة اتصال في "مقدّم جهات الاتصال"، حتى إذا لم يكن لديك
إذن READ_CONTACTS
. يمكنك مثلاً
إجراء ذلك في تطبيق يرسل رسائل إلكترونية للتهنئة بمناسبة عيد ميلاد جهة اتصال. بدلاً من
طلب READ_CONTACTS
الذي يمنحك إذن الوصول إلى كل
جهات اتصال المستخدم وجميع معلوماته، يمكنك السماح للمستخدم بالتحكّم في
جهات الاتصال التي يستخدمها تطبيقك. لإجراء ذلك، اتّبِع العملية التالية:
-
في تطبيقك، أرسِل هدفًا يحتوي على الإجراء
ACTION_PICK
ونوع MIME في "جهات الاتصال"CONTENT_ITEM_TYPE
، وذلك باستخدام الطريقةstartActivityForResult()
. - وبما أنّ هذا الغرض يتطابق مع فلتر الغرض لنشاط "الاختيار" في تطبيق "جهات الاتصال"، يظهر النشاط في المقدّمة.
-
في نشاط الاختيار، يختار المستخدِم
جهة اتصال لتعديلها. عند حدوث ذلك، يطلب نشاط الاختيار
setResult(resultcode, intent)
لإعداد نية رد الأموال لتطبيقك. يتضمن الغرض معرّف الموارد المنتظم (URI) الخاص بمحتوى جهة الاتصال التي اختارها المستخدم، وعلامة "إضافات"FLAG_GRANT_READ_URI_PERMISSION
. وتمنح هذه العلامات إذن URI لتطبيقك لقراءة بيانات جهة الاتصال التي يشير إليها عنوان URI للمحتوى. بعد ذلك، يُطلِق نشاط الاختيارfinish()
لمحاولة إعادة التحكّم إلى تطبيقك. -
يعود نشاطك إلى المقدّمة، ويُطلِق النظام طريقة
onActivityResult()
لنشاطك. تتلقّى هذه الطريقة نية النتيجة التي تم إنشاؤها من خلال نشاط الاختيار في تطبيق "الأشخاص". - باستخدام معرّف الموارد المنتظم (URI) للمحتوى من الغرض من النتيجة، يمكنك قراءة بيانات جهة الاتصال من "مقدِّم جهات الاتصال"، على الرغم من أنّك لم تطلب إذن وصول دائم للقراءة إلى موفِّر التطبيق في بيانك. يمكنك بعد ذلك الحصول على معلومات تاريخ ميلاد جهة الاتصال أو عنوان بريدها الإلكتروني، ثم إرسال التحية الإلكترونية.
استخدام تطبيق آخر
هناك طريقة أخرى للسماح للمستخدم بتعديل البيانات التي لا تملك أذونات الوصول إليها، وهي تفعيل تطبيق لديه الأذونات والسماح للمستخدم بتنفيذ العمل هناك.
على سبيل المثال، يقبل تطبيق "تقويم Google" عنصر ACTION_INSERT
intent الذي يتيح لك تفعيل واجهة مستخدم الإدراج في التطبيق. يمكنك تمرير بيانات "الإضافات" في هذا الإجراء الذي يستخدمه التطبيق
لملء واجهة المستخدم مسبقًا. وبما أنّ الأحداث المتكرّرة لها بنية معقّدة، فإنّ الطريقة المفضّلة
لإدراج الأحداث في موفّر التقويم هي تفعيل تطبيق "تقويم Google" باستخدام
ACTION_INSERT
ثم السماح للمستخدم بإدراج الحدث فيه.
عرض البيانات باستخدام تطبيق مساعد
إذا كان التطبيق يمتلك أذونات وصول، قد يظلّ بإمكانك استخدام
intent لعرض البيانات في تطبيق آخر. على سبيل المثال، يقبل تطبيق "تقويم Google" عنصر ACTION_VIEW
يعرض تاريخًا أو حدثًا محدّدًا.
يتيح لك ذلك عرض معلومات التقويم بدون الحاجة إلى إنشاء واجهة مستخدم خاصة بك.
لمزيد من المعلومات عن هذه الميزة، يُرجى الاطّلاع على
نظرة عامة على مقدّم خدمة "تقويم Google".
ليس من الضروري أن يكون التطبيق الذي ترسل إليه intent هو التطبيق المرتبط بموفِّر الخدمة. على سبيل المثال، يمكنك استرداد جهة اتصال من
موفِّر جهات الاتصال، ثم إرسال ACTION_VIEW
نية
تحتوي على عنوان URL لمحتوى صورة جهة الاتصال إلى عارض صور.
فئات العقود
تحدِّد فئة العقد الثوابت التي تساعد التطبيقات في التعامل مع عناوين URL للمحتوى وأسماء
الأعمدة وإجراءات النية وميزات أخرى لمقدّم المحتوى. لا يتم تضمين فئات العقود
تلقائيًا مع مقدّم الخدمة. على مطوّر موفِّر الخدمة تحديد هذه الإعدادات ثم
إتاحتها للمطوّرين الآخرين. إنّ العديد من مقدّمي الخدمات المضمّنين في منصة Android
لديهم فئات عقود مقابلة في الحزمة android.provider
.
على سبيل المثال، يحتوي موفِّر "معجم المستخدم" على فئة عقد
UserDictionary
تحتوي على معرّف الموارد المنتظم للمحتوى والثوابت الخاصة بأسماء الأعمدة. يتم تحديد
معرّف الموارد المنتظم (URI) للمحتوى لجدول Words
في الثابت
UserDictionary.Words.CONTENT_URI
.
تحتوي فئة UserDictionary.Words
أيضًا على ثوابت أسماء الأعمدة،
والتي يتم استخدامها في أمثلة المقتطفات الواردة في هذا الدليل. على سبيل المثال، يمكن تحديد تصوُّر طلب بحث
على النحو التالي:
Kotlin
val projection : Array<String> = arrayOf( UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.LOCALE )
Java
String[] projection = { UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.LOCALE };
فئة العقد الأخرى هي ContactsContract
لمقدّم جهات الاتصال.
تتضمن المستندات المرجعية لهذا الصف أمثلة على مقتطفات الرموز. وإحدى فئاته الفرعية، ContactsContract.Intents.Insert
، هي فئة
عقد تحتوي على ثوابت للنوايا وبيانات النوايا.
مرجع أنواع MIME
يمكن لموفّري المحتوى عرض أنواع وسائط MIME العادية أو سلاسل أنواع MIME المخصّصة أو كليهما.
يكون لأنواع MIME التنسيق التالي:
type/subtype
على سبيل المثال، نوع 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. استجابةً لعنوان URL التالي للمحتوى للجدول Line1:
content://com.example.trains/Line1
يعرض مقدّم الخدمة نوع MIME التالي:
vnd.android.cursor.dir/vnd.example.line1
ردًا على معرف الموارد المنتظم (URI) للمحتوى التالي للصف 5 في الجدول السطر2:
content://com.example.trains/Line2/5
يعرض مقدّم الخدمة نوع MIME التالي:
vnd.android.cursor.item/vnd.example.line2
يحدّد معظم موفّري المحتوى ثوابت فئة العقد لأنواع MIME التي يستخدمونها. على سبيل المثال، تحدّد فئة ContactsContract.RawContacts
لعقد موفِّر جهات الاتصال
قيمة ثابتة
CONTENT_ITEM_TYPE
لنوع MIME
لصف واحد من جهات الاتصال الأولية.
يتم وصف معرّفات الموارد المنتظمة (URI) للمحتوى الخاصة بالصفوف الفردية في القسم معرّفات الموارد المنتظمة (URI) للمحتوى.