أساسيات موفّر المحتوى

يدير موفر المحتوى إمكانية الوصول إلى مستودع مركزي للبيانات. مقدّم خدمة جزءًا من تطبيق Android، والذي يوفر غالبًا واجهة مستخدم خاصة به للعمل مع البيانات. ومع ذلك، يتم استخدام مقدمي المحتوى بشكل أساسي بواسطة التطبيقات التي يمكنها الوصول إلى الموفِّر باستخدام عنصر عميل موفّر يقدّم مقدّمو الخدمات ويوفّر عملاء الموفّرين واجهة متسقة وعادية للبيانات تعالج أيضًا الاتصال البيني للعمليات والوصول الآمن إلى البيانات.

عادةً ما تعمل مع موفّري المحتوى في أحد حالتين: تنفيذ المستخدم للوصول إلى موفِّر محتوى حالي في تطبيق آخر أو إنشاء موفر محتوى جديد في تطبيقك لمشاركة البيانات مع التطبيقات الأخرى.

هذه الصفحة أساسيات العمل مع موفّري المحتوى الحاليين للتعرف على تنفيذ موفري المحتوى في تطبيقاتك، راجع إنشاء موفّر محتوى

يتناول هذا الموضوع ما يلي:

  • آلية عمل موفّري المحتوى
  • واجهة برمجة التطبيقات التي تستخدمها لاسترداد البيانات من موفّر محتوى.
  • واجهة برمجة التطبيقات التي تستخدمها لإدراج البيانات أو تعديلها أو حذفها في أحد موفّري المحتوى
  • ميزات أخرى لواجهة برمجة التطبيقات تُسهل العمل مع موفّري الخدمات

نظرة عامة

يقدم موفر المحتوى البيانات إلى التطبيقات الخارجية في صورة جدول واحد أو أكثر مشابهة للجداول الموجودة في قاعدة بيانات ارتباطية. يمثل الصف مثيلاً من نوع ما البيانات التي يجمعها الموفر، ويمثل كل عمود في الصف جزءًا فرديًا من البيانات التي تم جمعها على سبيل المثال.

ينسق موفر المحتوى للوصول إلى طبقة تخزين البيانات في تطبيقك عدد واجهات برمجة التطبيقات والمكونات المختلفة. كما هو موضح في الشكل 1، تشمل هذه ما يلي:

  • مشاركة الوصول إلى بيانات تطبيقك مع تطبيقات أخرى
  • إرسال البيانات إلى تطبيق مصغّر
  • عرض اقتراحات البحث المخصّص لتطبيقك من خلال البحث إطار عمل باستخدام SearchRecentSuggestionsProvider
  • مزامنة بيانات التطبيق مع خادمك باستخدام تنفيذ AbstractThreadedSyncAdapter
  • جارٍ تحميل البيانات في واجهة المستخدم باستخدام CursorLoader
العلاقة بين موفّر المحتوى والمكونات الأخرى.

الشكل 1. العلاقة بين موفّر المحتوى والمكونات الأخرى.

الوصول إلى مقدّم خدمة

عندما تريد الوصول إلى البيانات في أحد موفّري المحتوى، يمكنك استخدام عنصر واحد (ContentResolver) في تطبيقك Context للتواصل مع مقدّم الخدمة كعميل. تشير رسالة الأشكال البيانية يتصل كائن ContentResolver بعنصر الموفِّر، وهو لفئة تنفذ ContentProvider.

مزوِّد الخدمة ويتلقى طلبات البيانات من العملاء، وينفذ الإجراء المطلوب، ثم يعرض نتائجك. يتضمن هذا الكائن طرقًا تتطلب طرقًا ذات أسماء متطابقة في كائن الموفر، تمثّل هذه السمة إحدى الفئات الفرعية الملموسة للسمة ContentProvider. تشير رسالة الأشكال البيانية توفِّر طرق ContentResolver البيانات الأساسية CRUD (الإنشاء والاسترداد والتحديث والحذف) لوظائف مساحة التخزين الدائمة.

هناك نمط شائع للوصول إلى ContentProvider من واجهة المستخدم الخاصة بك يستخدم CursorLoader لتشغيل طلب بحث غير متزامن في الخلفية. تشير رسالة الأشكال البيانية يستدعي Activity أو Fragment في واجهة المستخدم الخاصة بك CursorLoader إلى الاستعلام، والذي بدوره يحصل على ContentProvider باستخدام ContentResolver.

ويسمح هذا الإجراء بإبقاء واجهة المستخدم متاحة للمستخدم أثناء تنفيذ طلب البحث. هذا النمط يتضمن التفاعل بين عدد من الكائنات المختلفة، بالإضافة إلى العنصر الأساسي لآلية التخزين، كما هو موضح في الشكل 2.

التفاعل بين ContentProvider والفئات الأخرى ومساحة التخزين.

الشكل 2. التفاعل بين ContentProvider والصفوف الأخرى ومساحة التخزين.

ملاحظة: للوصول إلى أحد مقدّمي الخدمة، يجب عادةً أن يطلب تطبيقك الأذونات في ملف البيان. يتم وصف نمط التطوير هذا بمزيد من التفصيل في أذونات موفّر المحتوى

أحد مزودي الخدمة المدمجين في نظام Android الأساسي هو مزود قاموس المستخدم، الذي تخزن الكلمات غير القياسية التي يرغب المستخدم في الاحتفاظ بها. يوضح الجدول 1 ما قد تبدو البيانات في جدول هذا المزود:

الجدول 1: نموذج جدول قاموس المستخدم.

ألعاب الكلمات الرقم التعريفي للتطبيق النشر اللغة _ID
mapreduce المستخدم 1 100 en_US 1
precompiler المستخدم 14 200 fr_FR 2
applet المستخدم 2 225 fr_CA 3
const المستخدم 1 255 pt_BR 4
int المستخدم 5 100 ar_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) مع عبارة 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) للمحتوى تتضمّن الاسم الرمزي لموفّر الخدمة بالكامل، سلطته، بالإضافة إلى اسم يشير إلى جدول - مسار. عند الاتصال العميل للوصول إلى جدول في الموفر، فإن معرف الموارد المنتظم للمحتوى للجدول هو أحد الوسيطات.

في السطور السابقة من التعليمة البرمجية، يكون الثابت يحتوي CONTENT_URI على معرّف الموارد المنتظم (URI) لمحتوى جدول Words لمزود قاموس المستخدم. ContentResolver تحليل مرجع عنوان URI ويستخدمه لحلّ الموفِّر عن طريق يقارن بين السلطة وجدول النظام لمزودي الخدمة المعروفين. تشير رسالة الأشكال البيانية يمكن لـ ContentResolver بعد ذلك نقل وسيطات طلب البحث إلى القيمة الصحيحة المستخدم.

يستخدم ContentProvider جزء المسار من معرّف الموارد المنتظم (URI) للمحتوى لاختيار الوصول إليه. عادةً ما يكون لدى الموفر مسار لكل جدول يعرضه.

في سطور الرمز السابقة، يكون معرّف الموارد المنتظم (URI) الكامل للجدول Words هو:

content://user_dictionary/words
  • سلسلة content:// هي المخطط، وهو موجود دائمًا ويحدد هذا باعتباره معرف موارد منتظم (URI) للمحتوى.
  • تمثّل السلسلة user_dictionary مرجع الموفّر.
  • السلسلة words هي مسار الجدول.

يتيح لك العديد من مقدّمي الخدمة الوصول إلى صف واحد في جدول من خلال إلحاق قيمة معرّف. إلى نهاية عنوان URI. على سبيل المثال، لاسترداد صف يكون _ID فيه 4 من مزود قاموس المستخدم، يمكنك استخدام معرف الموارد المنتظم (URI) هذا للمحتوى:

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 على طرق ملائمة لإلحاق قيم المعرّفات بـ معرف موارد منتظم (URI). يستخدم المقتطف السابق withAppendedId() لإلحاق معرّف بمعرّف الموارد المنتظم (URI) الخاص بمحتوى موفّر قاموس المستخدم.

استرداد البيانات من المزود

يصف هذا القسم كيفية استرداد البيانات من أحد الموفّرين باستخدام "موفّر قاموس المستخدم" كمثال.

وللتوضيح، تقوم مقتطفات الرمز في هذا القسم باستدعاء ContentResolver.query() في سلسلة واجهة المستخدم. ضِمن مع ذلك، يتم إجراء طلبات البحث بشكل غير متزامن في سلسلة محادثات منفصلة. يمكنك استخدام الفئة CursorLoader، والموصوفة بشكلٍ أكثر تفصيلاً في دليل التحميلات. كما أن سطور الرمز هي مقتطفات فقط. ولا تعرض صورة كاملة التطبيق.

لاسترداد البيانات من مقدّم خدمة، اتّبِع الخطوات الأساسية التالية:

  1. اطلب إذن الوصول للقراءة إلى الموفّر.
  2. حدّد الرمز الذي يرسل طلب بحث إلى الموفِّر.

طلب إذن بالقراءة

لاسترداد البيانات من أحد الموفّرين، يحتاج تطبيقك إلى إذن بالقراءة المستخدم. لا يمكنك طلب هذا الإذن في وقت التشغيل. بدلاً من ذلك، يجب عليك تحديد أن يجب الحصول على هذا الإذن في ملف البيان، وذلك باستخدام <uses-permission> واسم الإذن المحدد من قِبل المستخدم.

عندما تحدد هذا العنصر في ملف البيان، فأنت تطلب هذا للحصول على إذن لاستخدام تطبيقك. عندما يثبّت المستخدمون تطبيقك، فإنهم يمنحون ضمنًا لهذا الطلب.

للعثور على الاسم الدقيق لإذن الوصول للقراءة الخاص بالموفِّر الذي تستخدمه، عليك أيضًا بأسماء أذونات الوصول الأخرى التي يستخدمها الموفر، فابحث في ملف التوثيق.

يتم توضيح دور الأذونات في الوصول إلى موفِّري الخدمات بمزيد من التفصيل في أذونات موفّر المحتوى

يحدد موفّر قاموس المستخدم الإذن android.permission.READ_USER_DICTIONARY في ملف البيان، ولذلك فإن على التطبيق الذي يريد القراءة من مقدّم الخدمة طلب هذا الإذن.

إنشاء الاستعلام

الخطوة التالية في استرداد البيانات من الموفر هي إنشاء استعلام. المقتطف التالي بعض المتغيرات للوصول إلى مزود قاموس المستخدم:

Kotlin

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

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

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

Java

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

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

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

يوضح المقتطف التالي كيفية استخدام ContentResolver.query()، باستخدام قاموس المستخدم موفّر الخدمة كمثال. يشبه استعلام عميل الموفر استعلام SQL، ويحتوي على ومجموعة الأعمدة المراد عرضها ومجموعة من معايير الاختيار وترتيب الفرز.

وتُسمى مجموعة الأعمدة التي يعرضها طلب البحث الإسقاط، فيكون المتغير mProjection.

يتم تقسيم التعبير الذي يحدد الصفوف المراد استردادها إلى عبارة تحديد وسيطات التحديد. عبارة التحديد هي مزيج من التعبيرات المنطقية والمنطقية، وأسماء الأعمدة والقيم. المتغيّر هو mSelectionClause. إذا قمت بتحديد المعلمة القابلة للاستبدال ? بدلاً من قيمة، تسترد طريقة طلب البحث القيمة من مصفوفة وسيطات التحديد، وهو المتغير mSelectionArgs.

في المقتطف التالي، إذا لم يُدخل المستخدم أي كلمة، فسيتم تعيين عبارة الاختيار على null ويعرض طلب البحث جميع الكلمات الواردة في الموفِّر. إذا أدخل المستخدم كلمة، يتم تعيين عبارة الاختيار على UserDictionary.Words.WORD + " = ?" يتم تعيين العنصر الأول من مصفوفة وسيطات التحديد على الكلمة التي يدخلها المستخدم.

Kotlin

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

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

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

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

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

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

Java

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

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

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

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

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

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

}

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

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

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

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

}

هذا الاستعلام مماثل لعبارة SQL التالية:

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

في عبارة SQL هذه، يتم استخدام أسماء الأعمدة الفعلية بدلاً من ثوابت فئة العقد.

الحماية من الإدخالات الضارة

إذا كانت البيانات التي يديرها موفّر المحتوى موجودة في قاعدة بيانات 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 الخاصة بك. على سبيل المثال، يمكن للمستخدم إدخال "لا شيء؛ إسقاط الجدول *;" لـ 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 تحتوي على الأعمدة المحددة بواسطة توقع للصفوف التي تتطابق مع معايير تحديد الاستعلام. حاسمة يوفّر الكائن 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" طرق لاسترداد أنواع مختلفة من البيانات من الكائن. على سبيل المثال، قد يستخدم المقتطف السابق تستخدم 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() . تُدرج هذه الطريقة صفًا جديدًا في الموفر وتعرض معرف موارد منتظم (URI) للمحتوى لهذا الصف. يوضح المقتطف التالي كيفية إدراج كلمة جديدة في موفِّر قاموس المستخدم:

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 لكل صف تمت إضافتها. يستخدم موفّرو الخدمات عادةً هذه القيمة كمفتاح أساسي للجدول.

يحدّد معرِّف الموارد المنتظم (URI) للمحتوى المعروض في newUri الصف المضاف حديثًا باستخدام بالتنسيق التالي:

content://user_dictionary/words/<id_value>

تمثّل القيمة <id_value> محتوى _ID في الصف الجديد. ويمكن لمعظم موفّري المحتوى اكتشاف هذا النموذج من معرّف الموارد المنتظم (URI) للمحتوى تلقائيًا، ثم تنفيذ الإجراء المطلوب العملية على هذا الصف بالتحديد.

للحصول على قيمة _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() لمزيد من المعلومات حول هذا، اقرأ قسم الحماية من الإدخالات الضارة.

أنواع بيانات موفّري المحتوى

يمكن لمزودي المحتوى توفير العديد من أنواع البيانات المختلفة. يقدم مزود قاموس المستخدم فقط ولكن يمكن لمزوّدي الخدمات أيضًا تقديم التنسيقات التالية:

  • عدد صحيح
  • عدد صحيح طويل (طويل)
  • النقطة العائمة
  • نقطة عائمة طويلة (مزدوجة)

هناك نوع بيانات آخر يستخدمه الموفرون غالبًا وهو كائن ثنائي كبير (BLOB) يتم تنفيذه باعتباره مصفوفة بايت بحجم 64 كيلوبايت يمكنك الاطّلاع على أنواع البيانات المتاحة من خلال مراجعة صف واحد (Cursor) "get" الطرق.

عادةً ما يتم سرد نوع البيانات لكل عمود في الموفر في وثائقه. تكون أنواع البيانات لمزود قاموس المستخدم مدرجة في الوثائق المرجعية لفئة العقد، UserDictionary.Words. فئات العقود هي كما هو موضح في قسم فئات العقود. يمكنك أيضًا تحديد نوع البيانات من خلال طلب الرقم Cursor.getType().

ويحتفظ الموفّرون أيضًا بمعلومات نوع بيانات MIME لكل معرّف موارد منتظم (URI) للمحتوى يتم تحديده. يمكنك معلومات نوع MIME لمعرفة ما إذا كان تطبيقك يمكنه معالجة البيانات التي أو اختيار نوع المعالجة بناءً على نوع MIME. تحتاج عادةً إلى تستخدم نوع MIME عند العمل مع مقدّم خدمة يحتوي على عناصر معقدة وهياكل أو ملفات البيانات.

على سبيل المثال، ContactsContract.Data في "مقدِّم جهات الاتصال" أنواع MIME لتسمية نوع بيانات جهة الاتصال المخزنة في كل الصف. للحصول على نوع MIME المتوافق مع معرّف الموارد المنتظم (URI) للمحتوى، اتصل ContentResolver.getType()

يصف قسم مرجع نوع MIME لكل من أنواع MIME العادية والمخصَّصة.

الطرق البديلة لوصول مقدّمي الخدمات

هناك ثلاثة أشكال بديلة لوصول مزوِّدي الخدمات اللازمة في تطوير التطبيقات:

يتم توضيح إمكانية الوصول المجمّع والتعديل باستخدام الأهداف في الأقسام التالية.

الوصول المجمّع

يُعد الوصول المجمّع إلى أحد الموفّرين مفيدًا لإدراج عدد كبير من الصفوف لإدراجها صفوف في جداول متعددة بنفس استدعاء الطريقة، وبشكل عام لتنفيذ مجموعة من العمليات التي تتم عبر حدود العملية كمعاملة، تُسمى العملية البسيطة.

وللوصول إلى أحد الموفّرين في الوضع المجمَّع، إنشاء مصفوفة من عناصر ContentProviderOperation ثم وإرسالها إلى موفّر المحتوى ContentResolver.applyBatch() يمكنك اجتياز سلطة موفّر المحتوى لهذه الطريقة، بدلاً من معرّف موارد منتظم (URI) معين للمحتوى.

يتيح ذلك لكل عنصر ContentProviderOperation في المصفوفة العمل. مقابل جدول مختلف. يؤدي استدعاء ContentResolver.applyBatch() إلى عرض مصفوفة من النتائج.

وصف فئة العقد ContactsContract.RawContacts يتضمن مقتطف رمز يوضح الإدراج المجمّع.

الوصول إلى البيانات باستخدام عناصر intent

يمكن أن توفر الأهداف وصولاً غير مباشر لموفّر المحتوى. يمكنك السماح للمستخدم بالوصول إلى البيانات لدى أحد المزودين حتى إذا لم يكن لدى التطبيق أذونات الوصول إلى البيانات من خلال الحصول على نتيجة intent من تطبيق لديه أذونات أو من خلال تفعيل تطبيق لديه أذونات ويسمح للمستخدم بالعمل فيه.

الحصول على أذونات وصول مؤقتة

يمكنك الوصول إلى البيانات لدى أحد موفّري المحتوى، حتى إذا لم يكن لديك صلاحيات الوصول المناسبة الأذونات، عن طريق إرسال هدف إلى تطبيق لديه الأذونات تلقي هدف نتيجة تحتوي على أذونات معرف الموارد المنتظم (URI). هذه أذونات لعنوان URI محدد للمحتوى تستمر حتى النشاط الذي يتلقى الانتهاء منها. التطبيق الذي يحتوي على أذونات دائمة يمنح أذونات مؤقتة الأذونات من خلال وضع علامة في الغرض من النتيجة:

ملاحظة: لا تمنح هذه العلامات إذنًا عامًا لمقدِّم الخدمة بالقراءة أو الكتابة. الذي يتم تضمين سلطته في معرف الموارد المنتظم (URI) للمحتوى. لا يتوفر الوصول إلا لعنوان URI نفسه.

عند إرسال معرّفات الموارد المنتظمة (URI) للمحتوى إلى تطبيق آخر، عليك تضمين واحد على الأقل من هذه المعرّفات. الأعلام. توفِّر العلامات الإمكانات التالية لأي تطبيق يتلقّى هدف وتستهدف الإصدار 11 من نظام التشغيل Android (المستوى 30) أو الإصدارات الأحدث:

  • اقرأ من البيانات التي يمثلها معرف الموارد المنتظم (URI) للمحتوى أو اكتب إليها، اعتمادًا على العلامة المضمَّنة في الهدف.
  • الحصول على حزمة إذن الوصول إلى التطبيق الذي يتضمّن موفِّر المحتوى الذي يتطابق مع مرجع معرّف الموارد المنتظم (URI). التطبيق الذي يرسل الغرض والتطبيق الذي يتضمن موفر المحتوى تطبيقين مختلفين.

يحدد الموفر أذونات عنوان URI لمعرّفات الموارد المنتظمة (URI) للمحتوى في ملف البيان الخاص به، وذلك باستخدام android:grantUriPermissions سمة <provider> العنصر بالإضافة إلى <grant-uri-permission> العنصر الثانوي <provider> العنصر. يتم شرح آلية أذونات عنوان URI بمزيد من التفصيل في دليل الأذونات على Android.

على سبيل المثال، يمكنك استرداد البيانات لجهة اتصال في "مقدم جهات الاتصال"، حتى إذا لم يكون لديك إذن READ_CONTACTS. قد ترغب في القيام هذا في تطبيق يرسل التحية الإلكترونية إلى جهة اتصال في عيد ميلادها. بدلاً من تطلب READ_CONTACTS، التي تتيح لك الوصول إلى جميع جهات اتصال المستخدم وجميع معلوماته، وتتيح للمستخدم التحكم في جهات الاتصال التي يستخدمها تطبيقك. لإجراء ذلك، يُرجى اتّباع العملية التالية:

  1. أرسِل في تطبيقك هدفًا يحتوي على الإجراء. ACTION_PICK و"جهات الاتصال" نوع MIME CONTENT_ITEM_TYPE، باستخدام الطريقة startActivityForResult().
  2. ولأن هذا الهدف يتطابق مع فلتر الأهداف "اختيار" تطبيق الأشخاص النشاط، يظهر النشاط في المقدمة.
  3. في نشاط التحديد، يختار المستخدم جهة اتصال للتحديث. عند حدوث ذلك، يستدعي نشاط التحديد setResult(resultcode, intent) لإعداد نية لرد الجميل إلى تطبيقك. يتضمن الغرض معرّف الموارد المنتظم (URI) للمحتوى جهة الاتصال التي اختارها المستخدم و"الإضافات" العلامة FLAG_GRANT_READ_URI_PERMISSION تمنح هذه العلامات عنوان URI إذنًا لتطبيقك لقراءة بيانات جهة الاتصال التي أشار إليها معرف الموارد المنتظم (URI) للمحتوى. بعد ذلك، يتّصل نشاط الاختيار بـ "finish()" إلى إرجاع التحكم إلى تطبيقك.
  4. يعود نشاطك إلى المقدّمة ويتصل النظام onActivityResult() . تتلقى هذه الطريقة الغرض من النتيجة التي تم إنشاؤها بواسطة نشاط التحديد في تطبيق الأشخاص.
  5. باستخدام معرّف الموارد المنتظم (URI) للمحتوى من الغرض من النتيجة، يمكنك قراءة بيانات جهة الاتصال من "مقدِّم جهات الاتصال"، على الرغم من أنّك لم تطلب إذن الوصول الدائم للقراءة إلى الموفِّر في بيان التطبيق يمكنك بعد ذلك الحصول على معلومات تاريخ ميلاد جهة الاتصال أو عنوان بريد إلكتروني ثم أرسل التحية الإلكترونية.

استخدام تطبيق آخر

يمكنك أيضًا السماح للمستخدم بتعديل البيانات التي لا تملك أذونات وصول إليها، وهي تنشيط تطبيق لديه أذونات والسماح للمستخدم بتنفيذ العمل هناك.

على سبيل المثال، يقبل تطبيق التقويم ACTION_INSERT هدفًا يتيح لك تفعيل واجهة مستخدم الإدراج للتطبيق. يمكنك ضبط "ميزات إضافية" البيانات بهذا الغرض، والتي قد يفكر فيها التطبيق لملء واجهة المستخدم مسبقًا. نظرًا لاحتواء الأحداث المتكررة على بنية معقدة، لإدراج الأحداث في "موفر التقويم" هي تنشيط تطبيق التقويم ACTION_INSERT، ثم السماح للمستخدم بإدراج الحدث هناك.

عرض البيانات باستخدام تطبيق مساعد

إذا كان تطبيقك لديه أذونات وصول، قد تستمر في استخدام نية عرض البيانات في تطبيق آخر. على سبيل المثال، يقبل تطبيق التقويم ACTION_VIEW intent يعرض تاريخًا أو حدثًا معيّنًا. يُتيح لك ذلك عرض معلومات التقويم بدون الحاجة إلى إنشاء واجهة مستخدم خاصة بك. لمزيد من المعلومات عن هذه الميزة، يُرجى مراجعة نظرة عامة على موفّر "تقويم Google".

لا يجب أن يكون التطبيق الذي ترسل إليه intent هو التطبيق. المرتبط بمقدّم الخدمة. على سبيل المثال، يمكنك استرداد جهة اتصال من التواصل مع مقدّم الخدمة، ثم إرسال هدف واحد (ACTION_VIEW) يتضمن معرف الموارد المنتظم (URI) للمحتوى الخاص بصورة جهة الاتصال إلى عارض الصور.

فئات العقود

تحدد فئة العقد الثوابت التي تساعد التطبيقات على العمل مع معرفات الموارد المنتظمة (URI) للمحتوى، عمود والأسماء والإجراءات المقصودة والميزات الأخرى الخاصة بموفّر المحتوى. فئات العقود ليست يتم تضمينه تلقائيًا مع مزود الخدمة. يتعين على مطور الموفر تحديدها ثم وإتاحتها للمطورين الآخرين. والعديد من مزوّدي نظام التشغيل Android لمنصة العرض فئات تعاقد مقابلة في الحزمة android.provider.

على سبيل المثال، لدى موفِّر قاموس المستخدم فئة عقد UserDictionary يتضمّن معرّف الموارد المنتظم (URI) للمحتوى وثابت اسم العمود. تشير رسالة الأشكال البيانية يتم تحديد معرّف الموارد المنتظم (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، ويحتوي على الجداول السطر1 والسطر2 والسطر 3. ردًا على معرف الموارد المنتظم (URI) للمحتوى التالي للجدول 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) للمحتوى.