الأربعاء، 18 أكتوبر 2017

موفري المحتوى Content Providers

موفري المحتوى  Content Providers





في هذه الوثيقة
مزايا مزودي المحتوى
المواضيع
أساسيات موفر المحتوى
إنشاء موفر محتوى
موفر التقويم
مزود جهات الاتصال
عينات ذات الصلة
محول المزامنة الأساسية

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


نظرة عامة على مخطط كيفية إدارة موفري المحتوى للدخول إلى السعة التخزينية.
الشكل 1. نظرة عامة على كيفية إدارة مزودي المحتوى للوصول إلى التخزين.



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

رسم توضيحي لتخزين مزود المحتوى المهاجر.
الشكل 2. توضيح لتخزين مزود المحتوى المهاجر.




يعتمد عدد من الفئات الأخرى على فئة ContentProvider :

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

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

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

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

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

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

تصف الموضوعات التالية مقدمي المحتوى بمزيد من التفصيل:

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



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

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

يصف هذا الموضوع ما يلي:

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

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

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


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

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

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



الشكل 2. التفاعل بين كونتينتبروفيدر، فئات أخرى، والتخزين.

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

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

الجدول 1: نموذج جدول قاموس المستخدم.
كلمة معرف التطبيق تكرر مكان _هوية شخصية
مابريديوس USER1 100 en_US 1
precompiler user14 200 fr_FR 2
بريمج USER2 225 fr_CA 3
CONST USER1 255 PT_BR 4
الباحث user5 100 en_UK 5
في الجدول 1، يمثل كل صف مثيل لكلمة قد لا تكون موجودة في قاموس قياسي. يمثل كل عمود بعض البيانات لهذه الكلمة، مثل اللغة التي تم اكتشافها فيها لأول مرة. رؤوس الأعمدة هي أسماء الأعمدة المخزنة في الموفر. للإشارة إلى لغة صف، يمكنك الرجوع إلى عمود locale . بالنسبة إلى هذا الموفر، يعمل العمود _ID كعمود "المفتاح الأساسي" الذي يحتفظ به الموفر تلقائيا.

للحصول على قائمة من الكلمات ومواقعها من موفر قاموس المستخدم، يمكنك الاتصال ContentResolver.query() . أسلوب query() يستدعي الأسلوب ContentProvider.query() يحدده موفر قاموس المستخدم. تظهر الأسطر التالية من التعليمات البرمجية استدعاء ContentResolver.query() :


// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    mProjection,                        // The columns to return for each row
    mSelectionClause                    // Selection criteria
    mSelectionArgs,                     // Selection criteria
    mSortOrder);                        // The sort order for the returned rows


يوضح الجدول 2 كيفية تطابق الوسيطات query(Uri,projection,selection,selectionArgs,sortOrder) عبارة سيليكت سكل:

جدول 2: استعلام () مقارنة استعلام سكل.
الاستعلام () سيليكت كيوورد / باراميتر ملاحظات
Uri FROM table_name Uri إلى الجدول في موفر اسمه table_name .
projection col,col,col,... projection عبارة عن مجموعة من الأعمدة التي يجب تضمينها لكل صف يتم استرجاعه.
selection WHERE col = value يحدد selection معايير اختيار الصفوف.
selectionArgs (لا يوجد مكافئ دقيق. وسيؤدي وسيط التحديد إلى استبدال العناصر النائبة في شرط التحديد.)
sortOrder ORDER BY col,col,... sortOrder بتحديد الترتيب الذي تظهر فيه الصفوف في Cursor إرجاعه.
عناوين ورل للمحتوى
عنوان أوري للمحتوى هو عنوان أوري يحدد البيانات في موفر الخدمة. تتضمن عناوين ورل للمحتوى الاسم الرمزي للموفر بالكامل ( سلطته ) واسم يشير إلى جدول ( مسار ). عند استدعاء طريقة عميل للوصول إلى جدول في موفر، أوري المحتوى للجدول هو إحدى الوسيطات.

في الأسطر السابقة من التعليمات البرمجية، يحتوي CONTENT_URI المستمر على معرف أوري للمحتوى في جدول "كلمات" قاموس المستخدم. الكائن ContentResolver يوزع سلطة أوري، ويستخدمها "حل" موفر من خلال مقارنة السلطة إلى جدول النظام من مقدمي المعروفة. يمكن ContentResolver ثم إرسال وسيطات الاستعلام إلى موفر الصحيح.

يستخدم ContentProvider مسار جزء من عنوان أوري للمحتوى لاختيار الجدول للوصول إليه. وعادة ما يكون للموفر مسار لكل جدول يعرضه.

في السطور السابقة من التعليمات البرمجية، يكون عنوان أوري الكامل لجدول "الكلمات":

content://user_dictionary/words



حيث سلسلة user_dictionary هي سلطة الموفر، words سترينغ هي مسار الجدول. content:// السلسلة content:// ( المخطط ) موجود دائما، ويحدد هذا كمعرف أوري للمحتوى.

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

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


غالبا ما تستخدم قيم المعرف عند استرجاع مجموعة من الصفوف ثم تريد تحديث أحدها أو حذفها.

ملاحظة: تحتوي صفوف Uri و Uri.Builder على أساليب ملائمة لإنشاء كائنات أوري ذات شكل جيد من السلاسل. تحتوي فئة ContentUris على أساليب ملائمة ContentUris قيم معرف بمعرف أوري. يستخدم المقتطف السابق مع withAppendedId() إلحاق معرف إلى معرف أوري محتوى وسيرديكتيوناري.

استرجاع البيانات من المزود
يصف هذا القسم كيفية استرداد البيانات من موفر، باستخدام موفر قاموس المستخدم كمثال.

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

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

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

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

ويرد وصف دور الأذونات في الوصول إلى مقدمي الخدمات بمزيد من التفصيل في القسم أذونات موفر المحتوى .

يحدد موفر قاموس المستخدم الإذن android.permission.READ_USER_DICTIONARY في ملف البيان الخاص به، لذا يجب أن يطلب أحد التطبيقات التي تريد قراءتها من الموفر هذا الإذن.

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

// A "projection" defines the columns that will be 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 mSelectionClause = null;

// Initializes an array to contain selection arguments
String[] mSelectionArgs = {""};
يعرض المقتطف التالي كيفية استخدام ContentResolver.query() ، وذلك باستخدام موفر قاموس المستخدم كمثال. استعلام عميل مقدم مشابه لاستعلام سكل، ويحتوي على مجموعة من الأعمدة للعودة ومجموعة من معايير التحديد وترتيب الفرز.

مجموعة الأعمدة التي يجب أن يعود الاستعلام يسمى إسقاط (متغير mProjection ).

يتم تقسيم التعبير الذي يحدد الصفوف لاسترداد إلى جملة التحديد وسيطات التحديد. شرط الاختيار عبارة عن مزيج من التعبيرات المنطقية mSelectionClause ، وأسماء الأعمدة، والقيم ( mSelectionClause متغير). إذا قمت بتحديد المعلمة القابلة للاستبدال ? بدلا من قيمة، تقوم طريقة الاستعلام باسترداد القيمة من صفيف وسيطات التحديد ( mSelectionArgs المتغير).

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

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

// Gets a word from the UI
mSearchString = mSearchWord.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(mSearchString)) {
    // Setting the selection clause to null will return all words
    mSelectionClause = null;
    mSelectionArgs[0] = "";

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

    // Moves the user's input string to the selection arguments.
    mSelectionArgs[0] = mSearchString;

}

// 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
    mProjection,                       // The columns to return for each row
    mSelectionClause                   // Either null, or the word the user entered
    mSelectionArgs,                    // Either empty, or the string the user entered
    mSortOrder);                       // 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 may want to
     * 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 was unsuccessful. This isn't necessarily
     * an error. You may 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

}

هذا الاستعلام مماثل لبيان سكل:


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


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

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

فكر في شرط الاختيار هذا:

// Constructs a selection clause by concatenating the user's input to the column name
String mSelectionClause =  "var = " + mUserInput;
إذا قمت بذلك، فإنك تسمح للمستخدم بتسلسل سكل ضار على عبارة سكل الخاص بك. على سبيل المثال، يمكن للمستخدم إدخال "لا شيء؛ دروب تابل *؛" ل mUserInput ، الأمر الذي سيؤدي إلى شرط الاختيار var = nothing; DROP TABLE *; var = nothing; DROP TABLE *; . بما أنه يتم التعامل مع شرط الاختيار كبيان سكل، قد يؤدي هذا إلى محو الموفر كافة الجداول في قاعدة بيانات سكليت الأساسية (ما لم يتم إعداد الموفر للقبض على محاولات حقن سكل ).

لتجنب هذه المشكلة، استخدم شرط التحديد الذي يستخدم ? كمعلمة قابلة للاستبدال ومجموعة منفصلة من حجج الاختيار. عند القيام بذلك، يكون إدخال المستخدم ملزما مباشرة بالاستعلام بدلا من أن يتم تفسيره كجزء من عبارة سكل. لأنه لا يتم التعامل معها ك سكل، لا يمكن إدخال المستخدم إدخال سكل ضار. بدلا من استخدام تسلسل لتشمل إدخال المستخدم، استخدم هذا البند الاختيار:
// Constructs a selection clause with a replaceable parameter
String mSelectionClause =  "var = ?";
إعداد مجموعة من وسيطات التحديد مثل هذا:

// Defines an array to contain the selection arguments
String[] selectionArgs = {""};
ضع قيمة في مجموعة وسيطات التحديد مثل هذا:

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


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

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

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

إذا لم يتطابق أي صف مع معايير التحديد، يقوم الموفر بإرجاع كائن Cursor الذي Cursor.getCount() هو 0 (مؤشر فارغ).

في حالة حدوث خطأ داخلي، تعتمد نتائج الاستعلام على موفر معين. قد تختار العودة null ، أو قد رمي Exception .

منذ Cursor هو "قائمة" الصفوف، طريقة جيدة لعرض محتويات Cursor هو ربطه إلى ListView عبر SimpleCursorAdapter .

يستمر المقتطف التالي في استخدام الشفرة من المقتطف السابق. أنه يخلق كائن SimpleCursorAdapter التي تحتوي على Cursor استرجاعها بواسطة الاستعلام، ويحدد هذا الكائن ليكون المحول ل ListView :





// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] mWordListColumns =
{
    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 will receive the Cursor columns for each row
int[] mWordListItems = { R.id.dictWord, R.id.locale};

// Creates a new SimpleCursorAdapter
mCursorAdapter = 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
    mWordListColumns,                      // A string array of column names in the cursor
    mWordListItems,                        // An integer array of view IDs in the row layout
    0);                                    // Flags (usually none are needed)

// Sets the adapter for the ListView
mWordList.setAdapter(mCursorAdapter);
ملاحظة: لدعم ListView مع Cursor ، يجب أن يحتوي المؤشر على عمود يسمى _ID . وبسبب هذا، الاستعلام الموضح سابقا يسترد عمود _ID لجدول "الكلمات"، على الرغم من أن ListView لا يعرض عليه. يوضح هذا التقييد أيضا السبب في أن معظم مقدمي الخدمات لديهم عمود _ID لكل جدول من جداولهم.

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

// 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 may 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 will 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() ترجع قيمة تشير إلى نوع بيانات العمود.

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

إذا لم يحدد تطبيق موفر الخدمة أية أذونات، فإن التطبيقات الأخرى لا يمكنها الوصول إلى بيانات المزود. ومع ذلك، المكونات في تطبيق موفر دائما الوصول الكامل للقراءة والكتابة، بغض النظر عن الأذونات المحددة.

كما ذكر سابقا، يتطلب موفر قاموس المستخدم إذن android.permission.READ_USER_DICTIONARY لاسترداد البيانات منه. مزود لديه إذن android.permission.WRITE_USER_DICTIONARY منفصلة لإدراج أو تحديث أو حذف البيانات.

للحصول على الأذونات اللازمة للوصول إلى موفر، يطلب تطبيق لهم عنصر <uses-permission> في ملف البيان الخاص به. عندما يقوم مدير حزمة أندرويد بتثبيت التطبيق، يجب أن يوافق المستخدم على كافة الأذونات التي يطلبها التطبيق. إذا وافق المستخدم على كل منهم، يستمر إدارة الحزمة التثبيت؛ إذا كان المستخدم لا يوافق عليها، مدير الحزمة يجهد التثبيت.

تؤدي طلبات عنصر <uses-permission> إلى قراءة حق الوصول إلى موفر قاموس المستخدم:

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


يتم شرح تأثير الأذونات على وصول مزود بمزيد من التفصيل في دليل الأمن و بيرميسيونس .

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

إدراج البيانات
لإدراج البيانات في موفر، يمكنك استدعاء الأسلوب ContentResolver.insert() . تقوم هذه الطريقة بإدراج صف جديد في الموفر وإرجاع معرف أوري للمحتوى لهذا الصف. يعرض هذا المقتطف كيفية إدراج كلمة جديدة في موفر قاموس المستخدم:

// Defines a new Uri object that receives the result of the insertion
Uri mNewUri;

...

// Defines an object to contain the new values to insert
ContentValues mNewValues = new ContentValues();

/*
 * Sets the values of each column and inserts the word. The arguments to the "put"
 * method are "column name" and "value"
 */
mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
mNewValues.put(UserDictionary.Words.WORD, "insert");
mNewValues.put(UserDictionary.Words.FREQUENCY, "100");

mNewUri = getContentResolver().insert(
    UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
    mNewValues                          // the values to insert
);

بيانات الصف الجديد يذهب إلى كائن ContentValues واحد، وهو مشابه في شكل مؤشر صف واحد. الأعمدة في هذا الكائن لا تحتاج إلى نفس نوع البيانات، وإذا كنت لا تريد تحديد قيمة على الإطلاق، يمكنك تعيين عمود إلى null باستخدام ContentValues.putNull() .

لا يضيف المقتطف العمود _ID ، لأنه يتم الاحتفاظ بهذا العمود تلقائيا. يعين موفر قيمة فريدة من _ID إلى كل صف تتم إضافته. عادة ما يستخدم مقدمو الخدمة هذه القيمة كمفتاح أساسي للجدول.

newUri أوري المحتوى في newUri يحدد الصف المضافة حديثا، بالتنسيق التالي:

content://user_dictionary/words/<id_value>



<id_value> هي محتويات _ID للصف الجديد. يمكن لمعظم مقدمي الخدمة الكشف عن هذا النوع من المحتوى أوري تلقائيا ثم تنفيذ العملية المطلوبة على هذا الصف بالذات.

للحصول على قيمة _ID من Uri إرجاعها، استدعاء ContentUris.parseId() .

تحديث البيانات
لتحديث صف، يمكنك استخدام كائن ContentValues مع القيم المحدثة تماما كما تفعل مع إدراج ومعايير التحديد تماما كما تفعل مع استعلام. أسلوب العميل الذي تستخدمه هو ContentResolver.update() . تحتاج فقط إلى إضافة قيم إلى عنصر ContentValues للأعمدة التي تقوم بتحديثها. إذا كنت ترغب في محو محتويات عمود، قم بتعيين القيمة إلى قيمة null .

يتغير المقتطف التالي كل الصفوف التي تحتوي اللغة على اللغة "إن" على اللغة المحلية null . قيمة الإرجاع هي عدد الصفوف التي تم تحديثها:

// Defines an object to contain the updated values
ContentValues mUpdateValues = new ContentValues();

// Defines selection criteria for the rows you want to update
String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
String[] mSelectionArgs = {"en_%"};

// Defines a variable to contain the number of updated rows
int mRowsUpdated = 0;

...

/*
 * Sets the updated value and updates the selected words.
 */
mUpdateValues.putNull(UserDictionary.Words.LOCALE);

mRowsUpdated = getContentResolver().update(
    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    mUpdateValues                       // the columns to update
    mSelectionClause                    // the column to select on
    mSelectionArgs                      // the value to compare to
);

يجب عليك أيضا تعقيم إدخال المستخدم عند استدعاء ContentResolver.update() . لمعرفة المزيد حول هذا، اقرأ القسم حماية ضد المدخلات الخبيثة .

حذف البيانات
حذف الصفوف مشابه لاسترداد بيانات الصف: تقوم بتحديد معايير التحديد للصفوف التي تريد حذفها وأسلوب العميل بإرجاع عدد الصفوف المحذوفة. يحذف المقتطف التالي الصفوف التي يتطابق أبيد معها مع "المستخدم". ترجع الطريقة عدد الصفوف المحذوفة.

// Defines selection criteria for the rows you want to delete
String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
String[] mSelectionArgs = {"user"};

// Defines a variable to contain the number of rows deleted
int mRowsDeleted = 0;

...

// Deletes the words that match the selection criteria
mRowsDeleted = getContentResolver().delete(
    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    mSelectionClause                    // the column to select on
    mSelectionArgs                      // the value to compare to
);
 

يجب عليك أيضا تعقيم إدخال المستخدم عند استدعاء ContentResolver.delete() . لمعرفة المزيد حول هذا، اقرأ القسم حماية ضد المدخلات الخبيثة .

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

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

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

كما يحتفظ موفرو البيانات بمعلومات نوع بيانات مايم لكل معرف أوري للمحتوى يعرفونه. يمكنك استخدام معلومات نوع مايم لمعرفة ما إذا كان التطبيق الخاص بك يمكن التعامل مع البيانات التي يقدمها مزود، أو لاختيار نوع من التعامل استنادا إلى نوع مايم. تحتاج عادة نوع مايم عند العمل مع مزود يحتوي على هياكل البيانات المعقدة أو الملفات. على سبيل المثال، يستخدم جدول contactsContract.Data في موفر جهات الاتصال أنواع مايم لتصنيف نوع بيانات جهات الاتصال المخزنة في كل صف. للحصول على نوع مايم المقابلة لعنوان أوري للمحتوى، اتصل ContentResolver.getType() .

يصف المرجع نوع مايم القسم بناء الجملة لكل من أنواع مايم القياسية والعرف.

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

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

دفعة الوصول
الوصول إلى دفعة إلى موفر مفيد لإدراج عدد كبير من الصفوف، أو لإدراج صفوف في جداول متعددة في نفس استدعاء الأسلوب، أو بشكل عام لأداء مجموعة من العمليات عبر حدود العملية كعملية (عملية الذرية).

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

يتضمن وصف العقد كونتاكتسكونتراكت .RawContacts مقتطف الشفرة الذي يوضح إدراج الدفعة. يحتوي تطبيق نموذج ماناجر كونتاكت على مثال للدخول ContactAdder.java ملف مصدر ContactAdder.java .

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

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

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

إذن القراءة: FLAG_GRANT_READ_URI_PERMISSION
إذن الكتابة: FLAG_GRANT_WRITE_URI_PERMISSION
ملاحظة: لا تمنح هذه الأعلام حق الوصول العام للقراءة أو الكتابة للموفر الذي يتم تضمين سلطته في عنوان أوري للمحتوى. الوصول هو فقط لعنوان أوري نفسه.

يحدد موفر الخدمة أذونات أوري لعناوين ورل للمحتوى في بيانه، وذلك باستخدام السمة android:grantUriPermission <provider> ، بالإضافة إلى العنصر <grant-uri-permission> الطفل في العنصر <provider> . يتم شرح آلية أذونات أوري بمزيد من التفصيل في دليل الأمان والأذونات، في قسم "أذونات أوري".

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

يرسل طلبك نية تحتوي على الإجراء ACTION_PICK و "جهات الاتصال" نوع مايم CONTENT_ITEM_TYPE ، باستخدام أسلوب startActivityForResult() .
لأن هذا القصد يطابق فلتر الأهداف للنشاط التطبيق الشعبية "الاختيار"، والنشاط يأتي إلى المقدمة.
في النشاط الاختيار، يحدد المستخدم جهة اتصال إلى تحديث. وعندما يحدث هذا، يدعو النشاط اختيار setResult(resultcode, intent)لاقامة وجود نية لاعطاء العودة إلى التطبيق الخاص بك. يحتوي على نية محتوى URI للاتصال المستخدم اختيار، و "إضافات" الأعلام FLAG_GRANT_READ_URI_PERMISSION. هذه العلامات تمنح الإذن URI إلى التطبيق الخاص بك لقراءة البيانات من أجل أشار اتصال من قبل محتوى URI. ثم يدعو النشاط اختيار finish()لإعادة التحكم إلى التطبيق الخاص بك.
نشاطك يعود إلى الصدارة، ويدعو النظام نشاطك في onActivityResult()الأسلوب. يتلقى هذا الأسلوب القصد نتيجة الناجمة عن نشاط التحديد في تطبيق الأشخاص.
مع محتوى URI من نية نتيجة لذلك، يمكنك قراءة بيانات جهة الاتصال من مزود اتصالات، على الرغم من أنك لم تطلب إذن الوصول للقراءة دائم لمقدم في البيان الخاص بك. ثم يمكنك الحصول على معلومات عيد ميلاد الاتصال أو له أو لها عنوان البريد الالكتروني ثم قم بإرسال المعايدة الإلكترونية.
باستخدام تطبيق آخر
وهناك طريقة بسيطة للسماح للمستخدم لتعديل البيانات التي لم يكن لديك أذونات الوصول هو تنشيط التطبيق الذي يحتوي على أذونات والسماح للمستخدم للقيام بهذا العمل هناك.

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

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

على سبيل المثال، قاموس العضو المزود لديه فئة العقد UserDictionaryالتي تحتوي على محتوى الثوابت URI واسم العمود. يتم تعريف محتوى URI للجدول "كلمات" في ثابت UserDictionary.Words.CONTENT_URI. و UserDictionary.Wordsيحتوي أيضا الطبقة الثوابت اسم العمود، والتي تستخدم في المثال قصاصات في هذا الدليل. على سبيل المثال، يمكن تعريف الإسقاط الاستعلام على النحو التالي:
String[] mProjection =
{
    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 "ببائعي"، لديها أكثر تعقيدا نوع و سلالة القيم. و نوع القيمة دائما
vnd.android.cursor.dir
 لعدة صفوف، أو

vnd.android.cursor.item


لصف واحد.

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

vnd.android.cursor.item/phone_v2
لاحظ أن القيمة النوع الفرعي هو ببساطة phone_v2.

المطورين مزود آخر قد خلق نمط الخاصة بهم من أنواع فرعية على أساس السلطة وجدول أسماء الموفر. على سبيل المثال، والنظر في موفر يحتوي على جداول زمنية القطار. السلطة الموفر هو com.example.trains، وأنه يحتوي على الجداول LINE1، Line2، وLine3. وردا على محتوى URI

content://com.example.trains/Line1
 لجدول LINE1، الموفر إرجاع نوع MIME

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




وردا على محتوى URI


content://com.example.trains/Line2/5
لالصف 5 في الجدول Line2، الموفر إرجاع نوع MIME

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




معظم موفري المحتوى تحديد الثوابت الدرجة عقد لأنواع MIME التي يستخدمونها. الطبقة عقد اتصالات مزود ContactsContract.RawContacts، على سبيل المثال، يحدد ثابت CONTENT_ITEM_TYPEلنوع MIME صف اتصال واحدة الخام.

ووصف محددات المحتوى للصفوف واحدة في قسم محددات المحتوى .

إنشاء موفر محتوى
في هذه الوثيقة
تصميم تخزين البيانات
تصميم عناوين ورل للمحتوى
تنفيذ فئة كونتنتبروفيدر
الطرق المطلوبة
تنفيذ طريقة الاستعلام ()
تنفيذ طريقة إدراج ()
تنفيذ طريقة الحذف ()
تنفيذ طريقة التحديث ()
تنفيذ طريقة أونكريت ()
تنفيذ أنواع مايم لموفر المحتوى
أنواع مايم للجداول
أنواع مايم للملفات
تنفيذ فئة العقد
تنفيذ أذونات موفر المحتوى
<موفر> العنصر
النوايا والوصول إلى البيانات
الطبقات الرئيسية
ContentProvider
Cursor
Uri
عينات ذات الصلة
ملاحظة تطبيق عينة الوسادة
أنظر أيضا
أساسيات موفر المحتوى
موفر التقويم
يقوم موفر المحتوى بإدارة الوصول إلى مستودع مركزي للبيانات. يمكنك تطبيق موفر كفئة واحدة أو أكثر في تطبيق أندرويد، جنبا إلى جنب مع العناصر الموجودة في ملف البيان. واحدة من الفصول الدراسية تنفذ فئة فرعية ContentProvider ، الذي هو واجهة بين مقدم الخدمة والتطبيقات الأخرى. على الرغم من أن مقدمي المحتوى يهدفون إلى إتاحة البيانات للتطبيقات الأخرى، فقد يكون لديك بالطبع أنشطة في تطبيقك تسمح للمستخدم بالاستعلام عن البيانات التي يديرها مقدم الخدمة وتعديلها.

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

قبل البدء في البناء
قبل البدء في إنشاء مقدم خدمة، ضع في اعتبارك ما يلي:

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

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

هذه بعض تقنيات تخزين البيانات المتوفرة في أندرويد:

إذا كنت تعمل مع بيانات منظمة ثم النظر إما قاعدة بيانات علائقية مثل سكليت، أو غير ذات العلاقة العلائقية قيمة مفتاح البيانات مثل ليفيلدب . إذا كنت تعمل مع بيانات غير منظمة مثل الصوت، صورة، أو وسائط الفيديو ثم النظر في تخزين البيانات كملفات. يمكنك مزج وتطابق عدة أنواع مختلفة من التخزين، وتعريضها باستخدام موفر محتوى واحد إذا لزم الأمر.
ويشمل نظام أندرويد واجهة برمجة التطبيقات قاعدة بيانات سكليت أن مقدمي الروبوت الخاصة تستخدم لتخزين البيانات الموجهة نحو الجدول. فئة SQLiteOpenHelper يساعدك على إنشاء قواعد البيانات، وفئة SQLiteDatabase الفئة الأساسية للوصول إلى قواعد البيانات.
تذكر أنه ليس لديك لاستخدام قاعدة بيانات لتنفيذ مستودع التخزين الخاص بك. يظهر مقدم الخدمة خارجيا كمجموعة من الجداول، على غرار قاعدة البيانات العلائقية، ولكن هذا ليس شرطا للتنفيذ الداخلي للموفر.
لتخزين بيانات الملف، الروبوت لديها مجموعة متنوعة من واجهات برمجة التطبيقات الموجهة ملف. لمعرفة المزيد حول تخزين الملفات، اقرأ موضوع تخزين البيانات . إذا كنت تصمم موفرا يقدم بيانات متعلقة بالوسائط مثل الموسيقى أو مقاطع الفيديو، فيمكنك توفير موفر يجمع بين بيانات الجداول والملفات.
في حالات نادرة، قد تستفيد من تنفيذ أكثر من موفر محتوى واحد لتطبيق واحد. على سبيل المثال، قد ترغب في مشاركة بعض البيانات مع أداة باستخدام موفر محتوى واحد، وتعريض مجموعة مختلفة من البيانات للمشاركة مع التطبيقات الأخرى.
للعمل مع البيانات المستندة إلى الشبكة، واستخدام الطبقات في java.net و android.net . يمكنك أيضا مزامنة البيانات المستندة إلى الشبكة إلى مخزن بيانات محلي مثل قاعدة بيانات، ثم تقديم البيانات كطاولات أو ملفات. يوضح تطبيق نموذج محول المزامنة الأساسية هذا النوع من المزامنة.
ملاحظة : إذا قمت بإجراء تغيير إلى مستودع التخزين الخاص بك غير متوافق مع الوراء، تحتاج إلى وضع علامة على مستودع التخزين مع رقم إصدار جديد. تحتاج أيضا إلى زيادة رقم الإصدار لتطبيقك الذي ينفذ موفر المحتوى الجديد. يؤدي إجراء هذا التغيير إلى تقليل عمليات خفض النظام من التسبب في تعطل النظام عند محاولة إعادة تثبيت تطبيق يحتوي على موفر محتوى غير متوافق.

اعتبارات تصميم البيانات
في ما يلي بعض النصائح لتصميم بنية بيانات مقدم الخدمة:

يجب أن يكون لبيانات الجدول دائما عمود "المفتاح الأساسي" الذي يحتفظ به الموفر كقيمة رقمية فريدة لكل صف. يمكنك استخدام هذه القيمة لربط الصف بالصفوف ذات الصلة في جداول أخرى (باستخدامه ك "مفتاح خارجي"). على الرغم من أنه يمكنك استخدام أي اسم لهذا العمود باستخدام BaseColumns._ID هو الخيار الأفضل لأن ربط نتائج استعلام موفر إلى ListView يتطلب أحد الأعمدة المستردة أن يكون _ID .
إذا كنت ترغب في تقديم صور نقطية أو غيرها من القطع الكبيرة جدا من البيانات المنحى ملف، تخزين البيانات في ملف ومن ثم توفيره بشكل غير مباشر بدلا من تخزينه مباشرة في جدول. إذا قمت بذلك، تحتاج إلى إخبار مستخدمي موفر الخدمة أنهم بحاجة إلى استخدام طريقة ملف ContentResolver للوصول إلى البيانات.
استخدام ثنائي نوع أوبجيكت (بلوب) نوع البيانات لتخزين البيانات التي تختلف في الحجم أو لديها بنية متفاوتة. على سبيل المثال، يمكنك استخدام عمود بلوب لتخزين المخزن مؤقت بروتوكول أو بنية جسون .
يمكنك أيضا استخدام بلوب لتنفيذ جدول مستقل المخطط . في هذا النوع من الجدول، يمكنك تحديد عمود مفتاح أساسي، عمود نوع مايم، واحد أو أكثر من الأعمدة العامة ك بلوب. يشار إلى معنى البيانات في أعمدة بلوب بالقيمة في عمود نوع مايم. يسمح لك هذا بتخزين أنواع صف مختلفة في نفس الجدول. جدول "بيانات" موفر جهات الاتصال كونتاكتسكونتراكت. يعد داتا مثالا لجدول مستقل عن المخطط.
تصميم عناوين ورل للمحتوى
عنوان أوري للمحتوى هو عنوان أوري يحدد البيانات في موفر الخدمة. تتضمن عناوين ورل للمحتوى الاسم الرمزي للموفر بالكامل ( سلطته ) واسم يشير إلى جدول أو ملف ( مسار ). يشير الجزء التعريفي الاختياري إلى صف فردي في جدول. كل طريقة وصول البيانات من ContentProvider يحتوي على أوري المحتوى كوسيطة؛ وهذا يسمح لك لتحديد الجدول أو الصف أو الملف للوصول.

يتم وصف أساسيات عناوين ورل للمحتوى في موضوع أساسيات مقدم المحتوى .

تصميم سلطة
مزود عادة سلطة واحدة، والتي هي بمثابة اسم الروبوت الداخلي. لتجنب النزاعات مع مقدمي الخدمات الآخرين، يجب عليك استخدام ملكية نطاق الإنترنت (في الاتجاه المعاكس) كأساس لسلطة مقدم الخدمة. ولأن هذه التوصية صحيحة أيضا بالنسبة لأسماء حزم أندرويد، فيمكنك تعريف صلاحيات موفر الخدمة كمدخل لاسم الحزمة التي تحتوي على المزود. على سبيل المثال، إذا كان اسم حزمة أندرويد هو com.example.<appname> ، فيجب أن تمنح مقدم الخدمة السلطة com.example.<appname>.provider .

تصميم بنية المسار
عادة ما ينشئ المطورون عناوين ورل للمحتوى من السلطة عن طريق إلحاق المسارات التي تشير إلى الجداول الفردية. على سبيل المثال، إذا كان لديك جدولان com.example.<appname>.provider/table1 2 ، يمكنك الجمع بين السلطة من المثال السابق لإعطاء عنوان ورل للمحتوى com.example.<appname>.provider/table1 و com.example.<appname>.provider/table2 . لا تقتصر المسارات على شريحة واحدة، وليس هناك حاجة إلى جدول لكل مستوى من المسارات.

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

هذه الاتفاقية يسهل نمط تصميم مشترك للتطبيقات الوصول إلى مزود. التطبيق يفعل استعلام ضد مزود ويعرض Cursor الناتجة في ListView باستخدام CursorAdapter . تعريف CursorAdapter يتطلب أحد الأعمدة في Cursor ليكون _ID

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

أنماط عنوان ورل للمحتوى
لمساعدتك في اختيار الإجراء الذي يجب اتخاذه من أجل عنوان ورل للمحتوى الوارد، يتضمن واجهة برمجة التطبيقات UriMatcher فئة UriMatcher ، والتي تحدد "أنماط" معرف أوري للمحتوى لقيم عدد صحيح. يمكنك استخدام القيم الصحيحة في عبارة switch التي تختار الإجراء المطلوب لعنوان أوري للمحتوى أو عناوين ورل التي تتطابق مع نمط معين.

يتطابق نمط عنوان أوري للمحتوى مع عناوين ورل للمحتوى باستخدام أحرف البدل:

* : يطابق سلسلة من أي أحرف صالحة من أي طول.
# : يطابق سلسلة أحرف رقمية من أي طول.
وكمثال على تصميم وتشفير معالجة عنوان ورل للمحتوى، فكر في موفر مزود بالسلطة com.example.app.provider يتعرف على عناوين ورل للمحتوى التالية التي تشير إلى الجداول:

content://com.example.app.provider/table1 : جدول يسمى table1 .
content://com.example.app.provider/table2/dataset1 : جدول يسمى dataset1 .
content://com.example.app.provider/table2/dataset2 : جدول يسمى dataset2 .
content://com.example.app.provider/table3 : جدول يسمى table3 .
كما يتعرف مقدم الخدمة على عناوين ورل للمحتوى هذه إذا كان لديهم معرف صف ملحق به، على سبيل المثال content://com.example.app.provider/table3/1 للصف الذي تم تحديده بواسطة 1 في table3 .

سيكون من الممكن استخدام أنماط أوري للمحتوى التالية:

content://com.example.app.provider/*
تطابق أي معرف أوري للمحتوى في الموفر.
content://com.example.app.provider/table2/* : content://com.example.app.provider/table2/* :
يتطابق مع عنوان أوري للمحتوى في مجموعة بيانات الجداول 1 و dataset2 ، ولكن لا يطابق عناوين ورل للمحتوى table3 أو table1 table3 .
content://com.example.app.provider/table3/# : يطابق عنوان أوري للمحتوى لصفوف واحدة في table3 ، مثل content://com.example.app.provider/table3/6 للصف الذي تم تحديده ب 6 .
يوضح مقتطف الشفرة التالي كيفية عمل الطرق في UriMatcher . يتعامل هذا الرمز مع عناوين ورل لجدول كامل بشكل مختلف عن عناوين ورل لصف واحد، وذلك باستخدام محتوى محتوى أوري content://<authority>/<path> للجداول content://<authority>/<path>/<id> لصفوف واحدة.

الأسلوب addURI() بتعيين سلطة ومسار إلى قيمة عدد صحيح. ترجع match() الطريقة match() القيمة الصحيحة لعنوان أوري. يختار بيان switch بين الاستعلام عن الجدول بأكمله والاستعلام عن سجل واحد:


public class ExampleProvider extends ContentProvider {
...
    // Creates a UriMatcher object.
    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        /*
         * The calls to addURI() go here, for all of the content URI patterns that the provider
         * should recognize. For this snippet, only the calls for table 3 are shown.
         */

        /*
         * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
         * in the path
         */
        sUriMatcher.addURI("com.example.app.provider", "table3", 1);

        /*
         * Sets the code for a single row to 2. In this case, the "#" wildcard is
         * used. "content://com.example.app.provider/table3/3" matches, but
         * "content://com.example.app.provider/table3 doesn't.
         */
        sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
    }
...
    // Implements ContentProvider.query()
    public Cursor query(
        Uri uri,
        String[] projection,
        String selection,
        String[] selectionArgs,
        String sortOrder) {
...
        /*
         * Choose the table to query and a sort order based on the code returned for the incoming
         * URI. Here, too, only the statements for table 3 are shown.
         */
        switch (sUriMatcher.match(uri)) {


            // If the incoming URI was for all of table3
            case 1:

                if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
                break;

            // If the incoming URI was for a single row
            case 2:

                /*
                 * Because this URI was for a single row, the _ID value part is
                 * present. Get the last path segment from the URI; this is the _ID value.
                 * Then, append the value to the WHERE clause for the query
                 */
                selection = selection + "_ID = " + uri.getLastPathSegment();
                break;

            default:
            ...
                // If the URI is not recognized, you should do some error handling here.
        }
        // call the code to actually do the query
    }


فئة أخرى، ContentUris ، توفر طرق الراحة للعمل مع جزء id من عناوين ورل للمحتوى. فئات Uri و Uri.Builder تشمل وسائل الراحة لتحليل كائنات Uri القائمة وبناء جديدة جديدة.

تنفيذ فئة كونتنتبروفيدر
يدير مثيل ContentProvider الوصول إلى مجموعة منظمة من البيانات عن طريق التعامل مع الطلبات من التطبيقات الأخرى. جميع أشكال الوصول في نهاية المطاف استدعاء ContentResolver ، والذي يدعو ثم طريقة ملموسة ContentProvider للوصول.

الطرق المطلوبة
تحدد الفئة المجردة ContentProvider ستة أساليب مجردة يجب تنفيذها كجزء من الفئة الفرعية الخرسانية الخاصة بك. يتم استدعاء كل هذه الطرق باستثناء onCreate() بواسطة تطبيق عميل يحاول الوصول إلى موفر المحتوى:

query()
استرداد البيانات من مزود الخدمة. استخدم الوسيطات لتحديد الجدول للاستعلام والصفوف والأعمدة للعودة وترتيب الفرز للنتيجة. قم بإعادة البيانات ككائن مؤشر.
insert()
إدراج صف جديد في مزود الخاص بك. استخدم الوسيطات لتحديد جدول الوجهة والحصول على قيم العمود المطلوب استخدامها. عرض معرف أوري للمحتوى للصف الذي تم إدراجه حديثا.
update()
تحديث الصفوف الموجودة في موفر الخدمة. استخدم الوسيطات لتحديد الجدول والصفوف للتحديث وللحصول على قيم الأعمدة المحدثة. أعد تحديث الصفوف.
delete()
احذف الصفوف من مزود الخدمة. استخدم الوسيطات لتحديد الجدول والصفوف المراد حذفها. أعد حذف عدد الصفوف.
getType()
أعد نوع مايم المطابق لعنوان أوري للمحتوى. يتم وصف هذه الطريقة بمزيد من التفصيل في القسم تطبيق أنواع مايم موفر المحتوى .
onCreate()
تهيئة موفر الخدمة. نظام أندرويد يدعو هذه الطريقة مباشرة بعد أن يخلق مزود الخاص بك. لاحظ أنه لم يتم إنشاء موفر الخدمة الخاص بك حتى يحاول عنصر ContentResolver الوصول إليه.
لاحظ أن هذه الأساليب لها نفس التوقيع مثل أساليب ContentResolver المسمى نفسه.

يجب أن يكون تطبيقك لهذه الطرق مما يلي:

يمكن استدعاء كل هذه الأساليب باستثناء onCreate() من عدة مؤشرات الترابط في آن واحد، لذلك يجب أن يكون مؤشر ترابط آمنة. لمعرفة المزيد حول سلاسل الترابط متعددة، راجع الموضوع المواضيع و المواضيع .
تجنب القيام بعمليات طويلة في onCreate() . تأجيل المهام التهيئة حتى تكون هناك حاجة فعلا. القسم يناقش طريقة أونكريت () هذا بمزيد من التفصيل.
على الرغم من أنه يجب تنفيذ هذه الأساليب، التعليمات البرمجية لا يجب أن تفعل أي شيء باستثناء إرجاع نوع البيانات المتوقع. على سبيل المثال، قد ترغب في منع التطبيقات الأخرى من إدراج البيانات في بعض الجداول. للقيام بذلك، يمكنك تجاهل المكالمة insert() والعودة 0.
تنفيذ طريقة الاستعلام ()
يجب أن تقوم طريقة ContentProvider.query() بإرجاع كائن Cursor ، أو إذا فشل، رمي Exception . إذا كنت تستخدم قاعدة بيانات سكليت كما تخزين البيانات الخاصة بك، يمكنك ببساطة إرجاع Cursor بإرجاع أحد أساليب query() فئة SQLiteDatabase . إذا لم يطابق الاستعلام أي صفوف، يجب أن ترجع مثيل Cursor الذي getCount() 0. يجب أن ترجع null فقط إذا حدث خطأ داخلي أثناء عملية الاستعلام.

إذا كنت لا تستخدم قاعدة بيانات سكليت كما تخزين البيانات الخاصة بك، استخدم واحدة من فئات فرعية ملموسة من Cursor . على سبيل المثال، فئة MatrixCursor بتنفيذ مؤشر فيه كل صف صفيف Object . باستخدام هذه الفئة، استخدم addRow() لإضافة صف جديد.

تذكر أن نظام أندرويد يجب أن يكون قادرا على التواصل Exception عبر حدود العملية. يمكن لنظام التشغيل أندرويد إجراء ذلك للاستثناءات التالية التي قد تكون مفيدة في معالجة أخطاء طلب البحث:

IllegalArgumentException (يمكنك اختيار رمي هذا إذا كان مزود الخدمة يتلقى عنوان أوري محتوى غير صالح)
NullPointerException
تنفيذ طريقة إدراج ()
تقوم طريقة insert() بإضافة صف جديد إلى الجدول المناسب، باستخدام القيم في وسيطة ContentValues . إذا لم يكن اسم عمود في وسيطة ContentValues ، قد تحتاج إلى توفير قيمة افتراضية له إما في رمز موفر أو في مخطط قاعدة البيانات.

يجب أن تعيد هذه الطريقة عنوان أوري للمحتوى للصف الجديد. لإنشاء هذا، إلحاق قيمة _ID (أو مفتاح أساسي آخر) الصف الجديد إلى أوري محتوى الجدول، باستخدام withAppendedId() .

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

تنفيذ طريقة التحديث ()
تأخذ طريقة update() نفس الوسيطة ContentValues المستخدمة من خلال insert() ، selection نفس وسيطات ContentValues نفسها المستخدمة بواسطة ContentValues delete() و ContentProvider.query() . قد يسمح لك هذا بإعادة استخدام التعليمات البرمجية بين هذه الطرق.

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

على سبيل المثال، إذا كنت تستخدم قاعدة بيانات SQLiteOpenHelper يمكنك إنشاء كائن SQLiteOpenHelper جديد في SQLiteOpenHelper ContentProvider.onCreate() ثم قم بإنشاء جداول سكل في المرة الأولى التي تفتح قاعدة البيانات. لتسهيل هذا، في المرة الأولى التي تتصل getWritableDatabase() ، فإنه يستدعي تلقائيا طريقة SQLiteOpenHelper.onCreate() .

يوضح المقتطفان التاليان التفاعل بين SQLiteOpenHelper.onCreate() و SQLiteOpenHelper.onCreate() . المقتطف الأول هو تنفيذ ContentProvider.onCreate() :


public class ExampleProvider extends ContentProvider

    /*
     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
     * in a following snippet.
     */
    private MainDatabaseHelper mOpenHelper;

    // Defines the database name
    private static final String DBNAME = "mydb";

    // Holds the database object
    private SQLiteDatabase db;

    public boolean onCreate() {

        /*
         * Creates a new helper object. This method always returns quickly.
         * Notice that the database itself isn't created or opened
         * until SQLiteOpenHelper.getWritableDatabase is called
         */
        mOpenHelper = new MainDatabaseHelper(
            getContext(),        // the application context
            DBNAME,              // the name of the database)
            null,                // uses the default SQLite cursor
            1                    // the version number
        );

        return true;
    }

    ...

    // Implements the provider's insert method
    public Cursor insert(Uri uri, ContentValues values) {
        // Insert code here to determine which table to open, handle error-checking, and so forth

        ...

        /*
         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
         *
         */
        db = mOpenHelper.getWritableDatabase();
    }
}


المقطع التالي هو تنفيذ SQLiteOpenHelper.onCreate() ، بما في ذلك فئة المساعد:

...
// A string that defines the SQL statement for creating a table
private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
    "main " +                       // Table's name
    "(" +                           // The columns in the table
    " _ID INTEGER PRIMARY KEY, " +
    " WORD TEXT"
    " FREQUENCY INTEGER " +
    " LOCALE TEXT )";
...
/**
 * Helper class that actually creates and manages the provider's underlying data repository.
 */
protected static final class MainDatabaseHelper extends SQLiteOpenHelper {

    /*
     * Instantiates an open helper for the provider's SQLite data repository
     * Do not do database creation and upgrade here.
     */
    MainDatabaseHelper(Context context) {
        super(context, DBNAME, null, 1);
    }

    /*
     * Creates the data repository. This is called when the provider attempts to open the
     * repository and SQLite reports that it doesn't exist.
     */
    public void onCreate(SQLiteDatabase db) {

        // Creates the main table
        db.execSQL(SQL_CREATE_MAIN);
    }
}


تنفيذ أنواع مايم كونتنتبروفيدر
تحتوي فئة ContentProvider على طريقتين لعودة أنواع مايم:

getType()
واحدة من الطرق المطلوبة التي يجب تنفيذها لأي مزود.
getStreamTypes()
هي الطريقة التي من المتوقع أن تنفذها إذا كان مزود الخدمة يقدم الملفات.
أنواع مايم للجداول
ترجع طريقة getType() String في تنسيق مايم يصف نوع البيانات التي يتم إرجاعها بواسطة وسيطة أوري للمحتوى. يمكن أن تكون وسيطة Uri نمطا بدلا من معرف أوري محدد؛ في هذه الحالة، يجب عليك إعادة نوع البيانات المرتبطة بعناوين ورل للمحتوى التي تتطابق مع النمط.

بالنسبة لأنواع البيانات الشائعة مثل النصوص أو هتمل أو جبيغ، يجب أن يعيد getType() نوع مايم القياسي لتلك البيانات. تتوفر قائمة كاملة بهذه األنواع القياسية على موقع ويب أنواع وسائط إيانا مايم .

بالنسبة إلى عناوين ورل للمحتوى التي تشير إلى صف أو صف من بيانات الجدول، يجب أن يعرض getType() نوع مايم بتنسيق مايم الخاص ببائع أندرويد:

اكتب الجزء: vnd
جزء النوع الفرعي:
إذا كان نمط أوري صف واحد: android.cursor. item / android.cursor. item /
إذا كان نمط أوري لأكثر من صف واحد: android.cursor. dir / android.cursor. dir /
جزء خاص vnd.<name> : vnd.<name> . <type>
يمكنك تقديم <name> و <type> . يجب أن تكون قيمة <name> فريدة من نوعها على مستوى العالم، ويجب أن تكون قيمة <type> فريدة لنمط أوري المناظر. الخيار الجيد ل <name> هو <name> شركتك أو جزء من اسم حزمة أندرويد للتطبيق. هناك خيار جيد <type> هو عبارة عن سلسلة تعرف الجدول المرتبط بعنوان أوري.
على سبيل المثال، إذا كانت سلطة موفر الخدمة com.example.app.provider ، وتعرض جدولا يسمى table1 ، نوع مايم لعدة صفوف في table1 هو:

vnd.android.cursor.dir/vnd.com.example.provider.table1
لصف واحد من table1 ، نوع مايم هو:

vnd.android.cursor.item/vnd.com.example.provider.table1
 
أنواع مايم للملفات
إذا كان مزود الخاص بك يقدم الملفات، تنفيذ getStreamTypes() . تقوم الطريقة بإرجاع صفيف String من أنواع مايم للملفات التي يمكن للموفر إرجاعها لعنوان أوري محتوى معين. يجب تصفية أنواع مايم التي تقدمها الوسيطة عامل تصفية نوع مايم، بحيث تقوم بإرجاع أنواع مايم التي يريد العميل التعامل معها فقط.

على سبيل المثال، فكر في موفر يقدم صور الصور كملفات بتنسيق .gif . و .gif و. .gif . إذا كان تطبيق استدعاء ContentResolver.getStreamTypes() مع image/* سلسلة مرشح image/* (شيء هو "صورة")، ثم طريقة ContentProvider.getStreamTypes() يجب إعادة الصفيف:

{ "image/jpeg", "image/png", "image/gif"}

إذا كان التطبيق مهتما فقط في ملفات .jpg ، ثم يمكن استدعاء ContentResolver.getStreamTypes() مع سلسلة التصفية *\/jpeg ، و ContentProvider.getStreamTypes() يجب أن يعود:

{"image/jpeg"}

إذا لم يقدم موفر الخدمة أي من أنواع مايم المطلوبة في سلسلة عامل التصفية، يجب أن ترجع getStreamTypes() null .

تنفيذ فئة العقد
فئة العقد عبارة عن فئة public final تحتوي على تعريفات ثابتة لعناوين ورل وأسماء الأعمدة وأنواع مايم والبيانات الوصفية الأخرى المتعلقة بالموفر. وتنشئ الفئة عقدا بين مقدم الخدمة والتطبيقات الأخرى من خلال ضمان إمكانية الوصول إلى مقدم الخدمة بشكل صحيح حتى إذا كانت هناك تغييرات على القيم الفعلية لعناوين ورل وأسماء الأعمدة وما إلى ذلك.

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

لا يمكن للمطورين الوصول إلى ملف فئة فئة العقد من التطبيق الخاص بك، ولكن يمكن تجميعها بشكل ثابت في تطبيقها من ملف .jar التي تقدمها.

فئة كونتاكتسكونتراكت والفئات المتداخلة هي أمثلة على فئات العقد.

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

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

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

يمكنك تحديد أذونات لمزودك باستخدام عنصر واحد أو أكثر من <permission> في ملف البيان. لجعل الإذن فريد لمزودك، استخدم جافا على نطاق android:name لسمة android:name . على سبيل المثال، قم بتسمية إذن القراءة com.example.app.provider.permission.READ_PROVIDER .

توضح القائمة التالية نطاق أذونات المزود، بدءا من الأذونات التي تنطبق على موفر الخدمة بالكامل ثم تصبح أكثر دقة. المزيد من أذونات غرامة الحبيبات تأخذ الأسبقية على تلك مع نطاق أكبر:

واحد القراءة والكتابة إذن على مستوى مزود
إذن واحد يتحكم في كل من القراءة والكتابة الوصول إلى مزود كامل، المحدد مع السمة android:permission العنصر <provider> .
فصل القراءة والكتابة على مستوى مزود إذن
إذن القراءة وإذن الكتابة للمزود بأكمله. يمكنك تحديد لهم مع android:readPermission android:writePermission سمات android:writePermission من العنصر <provider> . أنها تأخذ الأسبقية على إذن المطلوبة من قبل android:permission .
إذن على مستوى المسار
قراءة أو كتابة أو قراءة / كتابة إذن لعنوان أوري للمحتوى في موفر الخدمة. يمكنك تحديد كل عنوان أوري تريد التحكم فيه باستخدام عنصر الطفل <path-permission> العنصر <provider> . بالنسبة لكل عنوان أوري للمحتوى الذي تحدده، يمكنك تحديد إذن للقراءة / الكتابة أو إذن للقراءة أو إذن كتابي أو كل ثلاثة. أذونات القراءة والكتابة لها الأسبقية على إذن القراءة / الكتابة. أيضا، إذن مستوى المسار له الأسبقية على أذونات مستوى المزود.
إذن مؤقت
مستوى الإذن الذي يمنح الوصول المؤقت إلى أحد التطبيقات، حتى إذا لم يكن للتطبيق الأذونات المطلوبة عادة. تعمل ميزة الوصول المؤقت على تقليل عدد الأذونات التي يطلبها التطبيق في بيانه. عند تشغيل أذونات مؤقتة، التطبيقات الوحيدة التي تحتاج إلى أذونات "دائمة" لمزودك هي تلك التي الوصول إلى جميع البيانات الخاصة بك باستمرار.
ضع في اعتبارك الأذونات التي تحتاجها لتنفيذ موفر البريد الإلكتروني والتطبيق، عندما تريد السماح لتطبيق عارض صور خارجي بعرض مرفقات الصور من مزود الخدمة. لمنح عارض الصور إمكانية الدخول الضرورية بدون الحاجة إلى أذونات، يمكنك إعداد أذونات مؤقتة لعناوين ورل للمحتوى للصور. تصميم التطبيق البريد الإلكتروني الخاص بك بحيث عندما يريد المستخدم لعرض صورة، التطبيق يرسل نية تحتوي على أوري محتوى الصورة وأذونات إذن إلى عارض الصور. يمكن لمشاهد الصور بعد ذلك الاستعلام عن موفر البريد الإلكتروني الخاص بك لاسترداد الصورة، على الرغم من أن المشاهد لا يملك الإذن العادي للقراءة لمزودك.

لتشغيل الأذونات المؤقتة، يمكنك تعيين سمة android:grantUriPermissions <provider> أو إضافة عنصر أو أكثر <grant-uri-permission> العناصر <grant-uri-permission> إلى عنصر <provider> . إذا كنت تستخدم أذونات مؤقتة، لديك للاتصال Context.revokeUriPermission() كلما قمت بإزالة الدعم لعنوان أوري المحتوى من موفر الخاص بك، ويرتبط أوري المحتوى بإذن مؤقت.

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

إذا تم تعيين هذه العلامة على false ، فيجب إضافة العناصر الفرعية <grant-uri-permission> إلى عنصر <provider> . يحدد كل عنصر من عناصر الطفل عنوان أوري للمحتوى أو عناوين ورل التي يتم منح حق الوصول المؤقت إليها.

لتفويض الدخول المؤقت إلى أحد التطبيقات، يجب أن يحتوي النية على علامة FLAG_GRANT_READ_URI_PERMISSION أو FLAG_GRANT_WRITE_URI_PERMISSION أو كليهما. يتم تعيين هذه مع طريقة setFlags() .

إذا كانت android:grantUriPermissionsالسمة غير موجودة، انها يفترض أن يكون false.

و<مزود> العنصر
مثل Activityو Serviceالمكونات، فئة فرعية من ContentProviderيجب أن تكون محددة في ملف البيان لتطبيقه، وذلك باستخدام <provider>عنصر. نظام أندرويد يحصل على المعلومات التالية من العناصر:

سلطة ( android:authorities)
أسماء رمزية تحديد كامل مزود داخل النظام. يتم وصف هذه السمة بمزيد من التفصيل في القسم محددات المحتوى تصميم .
اسم الفئة مزود ( android:name)
الطبقة التي تطبق ContentProvider. يتم وصف هذه الفئة بمزيد من التفصيل في قسم تنفيذ الدرجة ContentProvider .
أذونات
الصفات التي تحدد الأذونات التي يجب أن يكون التطبيقات الأخرى من أجل الوصول إلى البيانات الموفر:
android:grantUriPermssions : العلم إذن مؤقت.
android:permission : واحد على نطاق مزود القراءة / الكتابة إذن.
android:readPermission : على نطاق مزود إذن القراءة.
android:writePermission : على نطاق مزود الكتابة إذن.
وصفت ضوابط وسمات يناظرها بمزيد من التفصيل في قسم تنفيذ ضوابط مزود المحتوى .

بدء التشغيل والتحكم سمات
هذه الصفات تحدد كيف ومتى يبدأ نظام الروبوت مزود، وخصائص عملية مزود، وغيرها من إعدادات وقت التشغيل:
android:enabled : العلم يسمح النظام لبدء الموفر.
android:exported : العلم السماح التطبيقات الأخرى لاستخدام هذا الموفر.
android:initOrder : الترتيب الذي يجب أن تبدأ هذا المزود، نسبة إلى غيرها من مقدمي الخدمات في نفس العملية.
android:multiProcess : العلم يسمح النظام لبدء الموفر في نفس العملية كعميل الدعوة.
android:process : اسم العملية التي يجب تشغيل الموفر.
android:syncable : العلم مشيرة إلى أن البيانات الموفر هو أن sync'ed مع البيانات على الخادم.
يتم توثيق سمات تماما في موضوع دليل ديف ل <provider>عنصر.

سمات إعلامية
رمز اختياري والتسمية لموفر:
android:icon: مورد drawable تحتوي على رمز لموفر. يظهر رمز بجانب التسمية الموفر في قائمة التطبيقات في إعدادات > تطبيقات > جميع .
android:label: تسمية إعلامية تصف مزود أو البيانات الخاصة به، أو كليهما. تظهر التسمية في قائمة التطبيقات في إعدادات > تطبيقات > جميع .
يتم توثيق سمات تماما في موضوع دليل ديف ل <provider>عنصر.

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

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

التعامل مع وجود نية الواردة التي ترغب في تعديل البيانات مزود الخاص بك هو لا يختلف عن التعامل مع المقاصد الأخرى. يمكنك معرفة المزيد حول استخدام النوايا من خلال قراءة الموضوع المقاصد ومرشحات النوايا .


موفر التقويم



في هذه الوثيقة
مبادئ
أذونات المستخدم
جدول التقاويم
الاستعلام عن تقويم
تعديل تقويم
إدراج تقويم
جدول الأحداث
إضافة أحداث
تحديث الأحداث
حذف الأحداث
جدول الحضور
إضافة الحضور
جدول التذكيرات
إضافة تذكيرات
جدول المثيلات
الاستعلام عن جدول المثيلات
نوايا التقويم
استخدام نية لإدراج حدث
استخدام نية لتعديل حدث
استخدام النوايا لعرض بيانات التقويم
مزامنة محولات
الطبقات الرئيسية
CalendarContract.Calendars
CalendarContract.Events
CalendarContract.Attendees
CalendarContract.Reminders
موفر التقويم هو مستودع لأحداث التقويم للمستخدم. تتيح لك واجهة برمجة تطبيقات مزود التقويم إجراء استعلام، وإدراج، وتحديث، وحذف عمليات في التقاويم والأحداث والحاضرين والتذكيرات وما إلى ذلك.

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

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

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

يعرض كل موفر محتوى عنوان أوري العام (ملفوفة ككائن Uri ) الذي يحدد مجموعة بياناته بشكل فريد. يعرض موفر المحتوى الذي يتحكم في مجموعات بيانات متعددة (جداول متعددة) عنوان ورل منفصل لكل واحد. تبدأ جميع عناوين ورل للمزودين بالحرف "كونتنت: //". ويحدد ذلك البيانات التي يتحكم فيها مقدم المحتوى. يحدد موفر التقويم الثوابت لعناوين ورل لكل فئة من فئاته (الجداول). تحتوي عناوين ورل هذه على التنسيق <class> .CONTENT_URI . على سبيل المثال، Events.CONTENT_URI .

ويبين الشكل 1 تمثيل رسومي لنموذج بيانات موفر التقويم. ويظهر الجداول الرئيسية والحقول التي تربط بعضها ببعض.

نموذج بيانات موفر التقويم
الشكل 1. نموذج بيانات مقدم الخدمة.

يمكن أن يكون لدى المستخدم تقويمات متعددة، ويمكن ربط تقاويم مختلفة بأنواع مختلفة من الحسابات (تقويم غوغل و إكسهانج وما إلى ذلك).

يحدد كالنداركونتراكت نموذج بيانات التقويم والحدث المعلومات ذات الصلة. يتم تخزين هذه البيانات في عدد من الجداول، المدرجة أدناه.

الجدول (الفئة) وصف
CalendarContract.Calendars
يحتفظ هذا الجدول بالمعلومات الخاصة بالتقويم. يحتوي كل صف في هذا الجدول على تفاصيل تقويم واحد، مثل الاسم واللون ومعلومات المزامنة وما إلى ذلك.
CalendarContract.Events يحتفظ هذا الجدول بالمعلومات الخاصة بالحدث. يحتوي كل صف في هذا الجدول على معلومات عن حدث واحد - على سبيل المثال، عنوان الحدث والموقع ووقت البدء ووقت الانتهاء وما إلى ذلك. الحدث يمكن أن يحدث لمرة واحدة أو يمكن أن تتكرر عدة مرات. يتم تخزين الحضور، والتذكير، وخصائص الموسعة في جداول منفصلة. لديهم كل EVENT_ID يشير إلى _ID في جدول الأحداث.
CalendarContract.Instances يحتفظ هذا الجدول بوقت البدء والانتهاء لكل حدث من الأحداث. يمثل كل صف في هذا الجدول حدث حدثا واحدا. للأحداث لمرة واحدة هناك 1: 1 تعيين الحالات إلى الأحداث. بالنسبة للأحداث المتكررة، يتم إنشاء عدة صفوف تلقائيا تتطابق مع حالات تكرار متعددة لهذا الحدث.
CalendarContract.Attendees يحتوي هذا الجدول على معلومات الحضور (الضيف). يمثل كل صف ضيفا واحدا لحدث ما. وهو يحدد نوع الضيف واستجابة الحضور للحدث.
CalendarContract.Reminders يحمل هذا الجدول بيانات التنبيه / الإشعار. يمثل كل صف تنبيها واحدا لحدث ما. يمكن أن يكون للحدث تذكيرات متعددة. يتم تحديد الحد الأقصى لعدد التذكيرات لكل حدث في MAX_REMINDERS ، والذي يتم تعيينه بواسطة محول المزامنة الذي يملك التقويم المعين. يتم تحديد التذكيرات قبل دقائق من الحدث ويكون لها طريقة تحدد كيفية تنبيه المستخدم.
تم تصميم واجهة برمجة تطبيقات مزود التقويم لتكون مرنة وقوية. وفي الوقت نفسه، من المهم توفير تجربة جيدة للمستخدم النهائي وحماية سلامة التقويم وبياناته. ولهذه الغاية، إليك بعض الأمور التي يجب وضعها في الاعتبار عند استخدام واجهة برمجة التطبيقات:

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



محولات المزامنة. يقوم مزامنة المزامنة بمزامنة بيانات التقويم على جهاز المستخدم مع وحدة خدمة أو مصدر بيانات آخر. في الجداول CalendarContract.Events كالندارس و CalendarContract.Events ، هناك أعمدة محجوزة لمحولات المزامنة لاستخدامها. يجب على مقدم الخدمة والتطبيقات عدم تعديلها. في الواقع، فهي غير مرئية ما لم يتم الوصول إليها كمحول المزامنة. لمزيد من المعلومات حول محولات المزامنة، راجع محولات المزامنة .


أذونات المستخدم
لقراءة بيانات التقويم، يجب أن يتضمن READ_CALENDAR إذن READ_CALENDAR في ملف البيان. يجب أن تتضمن إذن WRITE_CALENDAR لحذف بيانات التقويم أو إدراجها أو تحديثها:





<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
    <uses-sdk android:minSdkVersion="14" />
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
    ...
</manifest>

جدول التقاويم
يحتوي جدول CalendarContract.Calendars على تفاصيل للتقاويم الفردية. يمكن كتابة أعمدة التقاويم التالية بواسطة تطبيق ومحول المزامنة . للحصول على قائمة كاملة من الحقول المعتمدة، راجع مرجع كالنداركونتراكت.

ثابت وصف
NAME اسم التقويم.
CALENDAR_DISPLAY_NAME اسم هذا التقويم الذي يتم عرضه للمستخدم.
VISIBLE منطقية تشير إلى ما إذا كان التقويم محددا ليتم عرضه. تشير القيمة 0 إلى أنه لا يجب عرض الأحداث المرتبطة بهذا التقويم. تشير القيمة 1 إلى أنه يجب عرض الأحداث المرتبطة بهذا التقويم. تؤثر هذه القيمة على إنشاء صفوف في جدول CalendarContract.Instances .
SYNC_EVENTS منطقية تشير إلى ما إذا كان يجب مزامنة التقويم وتخزين أحداثه على الجهاز. قيمة 0 يقول لا مزامنة هذا التقويم أو تخزين الأحداث على الجهاز. قيمة 1 يقول أحداث المزامنة لهذا التقويم وتخزين أحداثه على الجهاز.
الاستعلام عن تقويم
في ما يلي مثال يوضح كيفية الحصول على التقاويم التي يملكها مستخدم معين. للحصول على بساطة، في هذا المثال يتم عرض عملية الاستعلام في مؤشر ترابط واجهة المستخدم ("مؤشر الترابط الرئيسي"). في الممارسة العملية، يجب أن يتم ذلك في مؤشر ترابط غير متزامن بدلا من مؤشر الترابط الرئيسي. لمزيد من المناقشة، راجع لوادر . إذا كنت لا مجرد قراءة البيانات ولكن تعديله، انظر AsyncQueryHandler .

// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
public static final String[] EVENT_PROJECTION = new String[] {
    Calendars._ID,                           // 0
    Calendars.ACCOUNT_NAME,                  // 1
    Calendars.CALENDAR_DISPLAY_NAME,         // 2
    Calendars.OWNER_ACCOUNT                  // 3
};

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;


لماذا يجب تضمين ACCOUNT_TYPE؟
إذا قمت بالاستعلام عن Calendars.ACCOUNT_NAME . Calendars.ACCOUNT_NAME ، يجب عليك أيضا تضمين Calendars.ACCOUNT_TYPE في التحديد. وذلك لأن حساب معين يعتبر فريدا فقط نظرا لكل من ACCOUNT_NAME و ACCOUNT_TYPE . ACCOUNT_TYPE هي السلسلة المطابقة لمصدق الحساب الذي تم استخدامه عندما تم تسجيل الحساب لدى AccountManager . هناك أيضا نوع خاص من الحساب يسمى ACCOUNT_TYPE_LOCAL للتقاويم غير المقترنة بحساب الجهاز. لا تتم مزامنة الحسابات ACCOUNT_TYPE_LOCAL .
في الجزء التالي من المثال، يمكنك إنشاء طلب البحث. يحدد الاختيار معايير الاستعلام. في هذا المثال يبحث طلب البحث عن تقاويم تحتوي على ACCOUNT_NAME "hera@example.com" و ACCOUNT_TYPE "com.example" و OWNER_ACCOUNT "hera@example.com". إذا كنت تريد مشاهدة جميع التقاويم التي شاهدها المستخدم، وليس فقط التقاويم التي يملكها المستخدم، OWNER_ACCOUNT . يقوم الاستعلام بإرجاع كائن Cursor الذي يمكنك استخدامه لاجتياز مجموعة النتائج التي تم إرجاعها بواسطة استعلام قاعدة البيانات. لمزيد من المناقشة حول استخدام الاستعلامات في موفري المحتوى، راجع موفري المحتوى .



// Run query
Cursor cur = null;
ContentResolver cr = getContentResolver();
Uri uri = Calendars.CONTENT_URI;
String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND ("
                        + Calendars.ACCOUNT_TYPE + " = ?) AND ("
                        + Calendars.OWNER_ACCOUNT + " = ?))";
String[] selectionArgs = new String[] {"hera@example.com", "com.example",
        "hera@example.com"};
// Submit the query and get a Cursor object back.
cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);

يستخدم هذا القسم التالي المؤشر للانتقال من خلال مجموعة النتائج. يستخدم الثوابت التي تم إعدادها في بداية المثال لإرجاع القيم لكل حقل.


// Use the cursor to step through the returned records
while (cur.moveToNext()) {
    long calID = 0;
    String displayName = null;
    String accountName = null;
    String ownerName = null;

    // Get the field values
    calID = cur.getLong(PROJECTION_ID_INDEX);
    displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
    accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
    ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);

    // Do something with the values...

   ...
}


تعديل تقويم
لإجراء تحديث للتقويم، يمكنك تقديم _ID من التقويم إما withAppendedId() ملحق إلى أوري ( withAppendedId() ) أو كعنصر الاختيار الأول. يجب أن يبدأ الاختيار ب "_id=?" ، ويجب أن يكون selectionArg الأول _ID للتقويم. يمكنك أيضا إجراء تحديثات من خلال ترميز المعرف في عنوان أوري. يقوم هذا المثال بتغيير اسم عرض التقويم باستخدام النهج ( withAppendedId() ):

private static final String DEBUG_TAG = "MyActivity";
...
long calID = 2;
ContentValues values = new ContentValues();
// The new display name for the calendar
values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);
Inserting a calendar

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

جدول الأحداث
يحتوي جدول CalendarContract.Events على تفاصيل الأحداث الفردية. لإضافة أحداث أو تحديثها أو حذفها، يجب أن يتضمن WRITE_CALENDAR الإذن WRITE_CALENDAR في ملف البيان .

يمكن كتابة أعمدة الأحداث التالية بواسطة تطبيق ومحول المزامنة. للحصول على قائمة كاملة من الحقول المعتمدة، راجع مرجع CalendarContract.Events .

ثابت وصف
CALENDAR_ID _ID للتقويم الذي ينتمي إليه الحدث.
ORGANIZER البريد الإلكتروني لمنظم (مالك) الحدث.
TITLE عنوان الحدث.
EVENT_LOCATION حيث يحدث الحدث.
DESCRIPTION وصف الحدث.
DTSTART وقت بدء الحدث في أوتك ميلي ثانية منذ العصر.
DTEND وقت انتهاء الحدث في أوتك ميلي ثانية منذ العصر.
EVENT_TIMEZONE المنطقة الزمنية للحدث.
EVENT_END_TIMEZONE المنطقة الزمنية لوقت انتهاء الحدث.
DURATION مدة الحدث بتنسيق RFC5545 . على سبيل المثال، قيمة "PT1H" تنص على أن الحدث يجب أن تستمر ساعة واحدة، وقيمة "P2W" يشير إلى مدة 2 أسابيع.
ALL_DAY تشير القيمة 1 إلى أن هذا الحدث يشغل اليوم بأكمله، كما هو محدد في المنطقة الزمنية المحلية. تشير القيمة 0 إلى أنه حدث عادي قد يبدأ وينتهي في أي وقت خلال يوم.
RRULE قاعدة تكرار تنسيق الحدث. على سبيل المثال، "FREQ=WEEKLY;COUNT=10;WKST=SU" . يمكنك العثور على مزيد من الأمثلة هنا .
RDATE تواريخ تكرار الحدث. عادة ما تستخدم RDATE بالاقتران مع RDATE لتعريف مجموعة مجمعة من تكرار الأحداث. لمزيد من المناقشة، راجع المواصفات RFC5545 .
AVAILABILITY إذا كان هذا الحدث يعد وقت مشغول أو هو وقت الفراغ التي يمكن جدولة أكثر.
GUESTS_CAN_MODIFY ما إذا كان يمكن للضيوف تعديل الحدث.
GUESTS_CAN_INVITE_OTHERS يمكن للنزلاء دعوة الضيوف الآخرين.
GUESTS_CAN_SEE_GUESTS ما إذا كان يمكن للضيوف رؤية قائمة الحضور.
إضافة أحداث
عندما يقوم التطبيق الخاص بك بإدراج حدث جديد، نوصي باستخدام INSERT إنتنت، كما هو موضح في استخدام نية لإدراج حدث . ومع ذلك، إذا كنت بحاجة إلى، يمكنك إدراج الأحداث مباشرة. يصف هذا القسم كيفية القيام بذلك.

في ما يلي قواعد إدراج حدث جديد:

يجب تضمين CALENDAR_ID و DTSTART .
يجب تضمين EVENT_TIMEZONE . للحصول على قائمة بمعرفات المنطقة الزمنية المثبتة للنظام، استخدم getAvailableIDs() . لاحظ أن هذه القاعدة لا تنطبق إذا قمت بإدراج حدث من خلال INSERT إنتنت الموضح في استخدام نية لإدراج حدث في هذا السيناريو يتم توفير منطقة زمنية افتراضية.
بالنسبة للأحداث غير المتكررة، يجب تضمين DTEND .
بالنسبة للأحداث المتكررة، يجب تضمين RDATE بالإضافة إلى RDATE أو RDATE . لاحظ أن هذه القاعدة لا تنطبق إذا قمت بإدراج حدث من خلال DTSTART DTEND الموضح في استخدام نية لإدراج حدث في هذا السيناريو، يمكنك استخدام DTSTART بالاقتران مع DTSTART و DTEND ، وتحويل تطبيق التقويم فإنه إلى مدة تلقائيا.
في ما يلي مثال لإدراج حدث. يتم تنفيذ هذا في مؤشر الترابط واجهة المستخدم لبساطة. في الممارسة العملية، يجب أن يتم إدراج وإدخال التحديثات في مؤشر ترابط غير متزامن لنقل الإجراء إلى مؤشر ترابط الخلفية. لمزيد من المعلومات، راجع AsyncQueryHandler .


long calID = 3;
long startMillis = 0;
long endMillis = 0;
Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 9, 14, 7, 30);
startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 9, 14, 8, 45);
endMillis = endTime.getTimeInMillis();
...

ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Events.DTSTART, startMillis);
values.put(Events.DTEND, endMillis);
values.put(Events.TITLE, "Jazzercise");
values.put(Events.DESCRIPTION, "Group workout");
values.put(Events.CALENDAR_ID, calID);
values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
Uri uri = cr.insert(Events.CONTENT_URI, values);

// get the event ID that is the last element in the Uri
long eventID = Long.parseLong(uri.getLastPathSegment());
//
// ... do something with event ID
//
//

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

تحديث الأحداث
عندما يريد تطبيقك السماح للمستخدم بتعديل حدث ما، نوصي باستخدام نية EDIT ، كما هو موضح في استخدام نية لتعديل حدث ما . ومع ذلك، إذا كنت بحاجة إلى ذلك، يمكنك تحرير الأحداث مباشرة. لإجراء تحديث حدث ما، يمكنك تقديم _ID للحدث إما withAppendedId() ملحق إلى أوري ( withAppendedId() ) أو كعنصر تحديد أول. يجب أن يبدأ الاختيار ب "_id=?" ، وينبغي أن يكون selectionArg الأول _ID للحدث. يمكنك أيضا القيام بتحديثات باستخدام تحديد بدون معرف. في ما يلي مثال لتحديث حدث. يقوم بتغيير عنوان الحدث باستخدام النهج withAppendedId() :

private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 188;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri updateUri = null;
// The new title for the event
values.put(Events.TITLE, "Kickboxing");
updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows)

حذف الأحداث
يمكنك حذف حدث إما بواسطة _ID باعتباره _ID على عنوان أوري، أو باستخدام التحديد القياسي. إذا كنت تستخدم معرفا مرفقا، فلا يمكنك إجراء تحديد. هناك نسختان من الحذف: كتطبيق وكمحول المزامنة. يقوم تطبيق حذف بتحديد العمود المحذوف إلى 1. هذه العلامة التي تخبر محول المزامنة أنه تم حذف الصف وأنه يجب نشر هذا الحذف إلى الملقم. حذف محول المزامنة إزالة الحدث من قاعدة البيانات جنبا إلى جنب مع كافة البيانات المرتبطة بها. في ما يلي مثال على تطبيق حذف حدث من خلال _ID :
private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 201;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri deleteUri = null;
deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().delete(deleteUri, null, null);
Log.i(DEBUG_TAG, "Rows deleted: " + rows);
Attendees Table

جدول الحضور
يمثل كل صف من جدول CalendarContract.Attendees حاضرا واحدا أو ضيفا لحدث ما. يقوم استدعاء query() بإرجاع قائمة الحضور لهذا الحدث مع EVENT_ID . يجب أن يتطابق EVENT_ID مع _ID لحدث معين.

يسرد الجدول التالي الحقول القابلة للكتابة. عند إدراج جهة حضور جديدة، يجب تضمين كل منهم باستثناء ATTENDEE_NAME .

ثابت وصف
EVENT_ID معرف الحدث.
ATTENDEE_NAME اسم الحضور.
ATTENDEE_EMAIL عنوان البريد الإلكتروني للحضور.
ATTENDEE_RELATIONSHIP
علاقة الحضور بالحدث. واحد من:

RELATIONSHIP_ATTENDEE
RELATIONSHIP_NONE
RELATIONSHIP_ORGANIZER
RELATIONSHIP_PERFORMER
RELATIONSHIP_SPEAKER
ATTENDEE_TYPE
نوع من الحضور. واحد من:

TYPE_REQUIRED
TYPE_OPTIONAL
ATTENDEE_STATUS
حالة حضور الحضور. واحد من:

ATTENDEE_STATUS_ACCEPTED
ATTENDEE_STATUS_DECLINED
ATTENDEE_STATUS_INVITED
ATTENDEE_STATUS_NONE
ATTENDEE_STATUS_TENTATIVE
إضافة الحضور
في ما يلي مثال يضيف أحد الحضور إلى الحدث. لاحظ أن EVENT_ID مطلوب:



long eventID = 202;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Attendees.ATTENDEE_NAME, "Trevor");
values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
values.put(Attendees.EVENT_ID, eventID);
Uri uri = cr.insert(Attendees.CONTENT_URI, values);
 
جدول التذكيرات
يمثل كل صف من جدول CalendarContract.Reminders تذكيرا واحدا لحدث ما. يعرض query() قائمة بالتذكيرات للحدث مع EVENT_ID .

يسرد الجدول التالي الحقول القابلة للكتابة للتذكيرات. كل منهم يجب أن تدرج عند إدراج تذكير جديد. لاحظ أن محولات المزامنة تحدد أنواع التذكيرات التي تدعمها في جدول CalendarContract.Calendars . راجع ALLOWED_REMINDERS لمزيد من التفاصيل.

ثابت وصف
EVENT_ID معرف الحدث.
MINUTES المحاضر قبل الحدث أن تذكير يجب اطلاق النار.
METHOD
طريقة الإنذار، كما تم تعيينها على الملقم. واحد من:

METHOD_ALERT
METHOD_DEFAULT
METHOD_EMAIL
METHOD_SMS
إضافة تذكيرات
يضيف هذا المثال تذكير إلى حدث. ينطلق التذكير قبل 15 دقيقة من الحدث.


long eventID = 221;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Reminders.MINUTES, 15);
values.put(Reminders.EVENT_ID, eventID);
values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
Uri uri = cr.insert(Reminders.CONTENT_URI, values);

جدول المثيلات
يحتفظ جدول CalendarContract.Instances بوقت البدء والانتهاء لحدوث حدث. يمثل كل صف في هذا الجدول حدث حدثا واحدا. جدول المثيلات غير قابل للكتابة ولا يوفر سوى طريقة للاستعلام عن أحداث الحدث.

يسرد الجدول التالي بعض الحقول التي يمكن الاستعلام عنها لمثيل. لاحظ أن المنطقة الزمنية محددة بواسطة KEY_TIMEZONE_TYPE و KEY_TIMEZONE_INSTANCES .

ثابت وصف
BEGIN وقت بداية المثيل، بالتوقيت أوتك ميلي ثانية.
END وقت انتهاء المثيل، بالتوقيت أوتك ميلي ثانية.
END_DAY يوم نهاية جوليان من المثال، بالنسبة إلى المنطقة الزمنية للتقويم.
END_MINUTE يتم قياس دقيقة النهاية للمثيل من منتصف الليل في المنطقة الزمنية للتقويم.
EVENT_ID _ID الحدث لهذا المثال.
START_DAY يوم بداية جوليان للمثيل، بالنسبة إلى المنطقة الزمنية للتقويم.
START_MINUTE يتم قياس دقيقة البداية للمثيل من منتصف الليل، بالنسبة إلى المنطقة الزمنية للتقويم.
الاستعلام عن جدول المثيلات
للاستعلام عن جدول مثيلات، تحتاج إلى تحديد نطاق وقت الاستعلام في أوري. في هذا المثال، يحصل calendarContract.Instances على حق الوصول إلى حقل TITLE خلال تطبيق واجهة CalendarContract.EventsColumns Contract.EventsColumns. وبعبارة أخرى، يتم إرجاع TITLE خلال عرض قاعدة بيانات، وليس من خلال الاستعلام عن جدول CalendarContract.Instances الخام.


private static final String DEBUG_TAG = "MyActivity";
public static final String[] INSTANCE_PROJECTION = new String[] {
    Instances.EVENT_ID,      // 0
    Instances.BEGIN,         // 1
    Instances.TITLE          // 2
  };

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_BEGIN_INDEX = 1;
private static final int PROJECTION_TITLE_INDEX = 2;
...

// Specify the date range you want to search for recurring
// event instances
Calendar beginTime = Calendar.getInstance();
beginTime.set(2011, 9, 23, 8, 0);
long startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2011, 10, 24, 8, 0);
long endMillis = endTime.getTimeInMillis();

Cursor cur = null;
ContentResolver cr = getContentResolver();

// The ID of the recurring event whose instances you are searching
// for in the Instances table
String selection = Instances.EVENT_ID + " = ?";
String[] selectionArgs = new String[] {"207"};

// Construct the query with the desired date range.
Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);

// Submit the query
cur =  cr.query(builder.build(),
    INSTANCE_PROJECTION,
    selection,
    selectionArgs,
    null);

while (cur.moveToNext()) {
    String title = null;
    long eventID = 0;
    long beginVal = 0;

    // Get the field values
    eventID = cur.getLong(PROJECTION_ID_INDEX);
    beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
    title = cur.getString(PROJECTION_TITLE_INDEX);

    // Do something with the values.
    Log.i(DEBUG_TAG, "Event:  " + title);
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(beginVal);
    DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
    Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));
    }
 }

نوايا التقويم
لا يحتاج التطبيق إلى أذونات لقراءة وكتابة بيانات التقويم. ويمكن بدلا من ذلك استخدام النوايا المدعومة من قبل تطبيق التقويم الروبوت لتسليم عمليات القراءة والكتابة لهذا التطبيق. يسرد الجدول التالي النوايا المدعومة من قبل موفر التقويم:

عمل URI وصف إضافات

VIEW 
content://com.android.calendar/time/<ms_since_epoch>

يمكنك أيضا الرجوع إلى عنوان أوري باستخدام calendarContract.CONTENT_URI. للحصول على مثال لاستخدام هذه النية، راجع استخدام النوايا لعرض بيانات التقويم . افتح التقويم إلى الوقت المحدد بواسطة <ms_since_epoch> . لا شيء.
VIEW
content://com.android.calendar/events/<event_id>

يمكنك أيضا الرجوع إلى عنوان أوري مع Events.CONTENT_URI . Events.CONTENT_URI . للحصول على مثال لاستخدام هذه النية، راجع استخدام النوايا لعرض بيانات التقويم . عرض الحدث المحدد بواسطة <event_id> . CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT
content://com.android.calendar/events/<event_id>

يمكنك أيضا الرجوع إلى عنوان أوري مع Events.CONTENT_URI . Events.CONTENT_URI . للحصول على مثال لاستخدام هذه النية، راجع استخدام نية لتعديل حدث . عدل الحدث المحدد بواسطة <event_id> . CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT 

INSERT
content://com.android.calendar/events

يمكنك أيضا الرجوع إلى عنوان أوري مع Events.CONTENT_URI . Events.CONTENT_URI . للحصول على مثال لاستخدام هذه النية، راجع استخدام نية لإدراج حدث . إنشاء حدث. أي من الإضافات المدرجة في الجدول أدناه.
يسرد الجدول التالي إضافات النوايا التي يدعمها موفر التقويم:

نية اضافية وصف
Events.TITLE اسم الحدث.
CalendarContract.EXTRA_EVENT_BEGIN_TIME يبدأ الحدث الوقت بالمللي ثانية من الحقبة.
CalendarContract.EXTRA_EVENT_END_TIME وقت انتهاء الحدث بالمللي ثانية من العصر.
CalendarContract.EXTRA_EVENT_ALL_DAY منطقية تشير إلى أن الحدث كل يوم. يمكن أن تكون القيمة true أو false .
Events.EVENT_LOCATION موقع الحدث.
Events.DESCRIPTION وصف الحدث.
Intent.EXTRA_EMAIL عناوين البريد الإلكتروني لتلك للدعوة كقائمة مفصولة بفواصل.
Events.RRULE قاعدة تكرار الحدث.
Events.ACCESS_LEVEL ما إذا كان الحدث الخاص أو العام.
Events.AVAILABILITY إذا كان هذا الحدث يعد وقت مشغول أو هو وقت الفراغ التي يمكن جدولة أكثر.
تصف الأقسام التالية كيفية استخدام هذه النوايا.

استخدام نية لإدراج حدث
باستخدام INSERT إنتنت يتيح يد التطبيق الخاص بك قبالة مهمة إدراج الحدث إلى التقويم نفسه. مع هذا النهج، التطبيق الخاص بك حتى لا تحتاج إلى الحصول على إذن WRITE_CALENDAR المدرجة في ملف البيان .

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

في ما يلي مقتطف الشفرة الذي يحدد موعدا لحدث ما في 19 كانون الثاني (يناير) 2012، والذي يبدأ من الساعة 7:30 صباحا وحتى 8:30 صباحا. لاحظ ما يلي حول مقتطف الشفرة هذا:

وهو يحدد Events.CONTENT_URI مثل أوري.
ويستخدم حقل CalendarContract.EXTRA_EVENT_END_TIME CONTContract.EXTRA_EVENT_BEGIN_TIME و CalendarContract.EXTRA_EVENT_END_TIME الإضافيين لتعبئة النموذج مسبقا مع وقت الحدث. يجب أن تكون القيم لهذه الأوقات في أوتك ميلي ثانية من العصر.
ويستخدم الحقل Intent.EXTRA_EMAIL إضافي لتوفير قائمة مفصولة بفواصل من المدعوين، محددة بواسطة عنوان البريد الإلكتروني.

Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 0, 19, 7, 30);
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 0, 19, 8, 30);
Intent intent = new Intent(Intent.ACTION_INSERT)
        .setData(Events.CONTENT_URI)
        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
        .putExtra(Events.TITLE, "Yoga")
        .putExtra(Events.DESCRIPTION, "Group class")
        .putExtra(Events.EVENT_LOCATION, "The gym")
        .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
startActivity(intent);


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

في ما يلي مثال على نية تحدد عنوانا جديدا لحدث محدد وتتيح للمستخدمين تعديل الحدث في التقويم.

long eventID = 208;
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_EDIT)
    .setData(uri)
    .putExtra(Events.TITLE, "My New Title");
startActivity(intent)

استخدام النوايا لعرض بيانات التقويم
يقدم موفر الرزنق طريقتين مختلفتين لاستخدام طريقة الرؤية:

لفتح التقويم إلى تاريخ معين.
لعرض حدث.
في ما يلي مثال يوضح كيفية فتح التقويم إلى تاريخ معين:


// A date-time specified in milliseconds since the epoch.
long startMillis;
...
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder.appendPath("time");
ContentUris.appendId(builder, startMillis);
Intent intent = new Intent(Intent.ACTION_VIEW)
    .setData(builder.build());
startActivity(intent);
 
في ما يلي مثال يوضح كيفية فتح حدث للعرض:



long eventID = 208;
...
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_VIEW)
   .setData(uri);
startActivity(intent);


مزامنة محولات
هناك اختلافات طفيفة فقط في كيفية وصول تطبيق ومحول المزامنة إلى موفر التقويم:

يجب أن يحدد محول المزامنة أنه محول مزامنة من خلال إعداد CALLER_IS_SYNCADAPTER إلى CALLER_IS_SYNCADAPTER .
يحتاج محول المزامنة إلى توفير ACCOUNT_NAME و ACCOUNT_TYPE كمعلمات طلب بحث في معرف الموارد المنتظم (أوري).
يحتوي محول المزامنة على حق الوصول للكتابة إلى أعمدة أكثر من تطبيق أو عنصر واجهة مستخدم. على سبيل المثال، يمكن للتطبيق فقط تعديل بعض خصائص التقويم، مثل اسمه واسم العرض وإعداد الرؤية وما إذا كان التقويم متزامنا أم لا. على سبيل المقارنة، يمكن لمزامنة المزامنة الوصول ليس فقط تلك الأعمدة، ولكن العديد من الآخرين، مثل لون التقويم، والمنطقة الزمنية، ومستوى الوصول، والموقع، وهلم جرا. ومع ذلك، يقتصر محول المزامنة على ACCOUNT_NAME و ACCOUNT_TYPE المحدد.
في ما يلي طريقة مساعدة يمكنك استخدامها لإرجاع معرف موارد منتظم (أوري) للاستخدام مع محول المزامنة:

static Uri asSyncAdapter(Uri uri, String account, String accountType) {
    return uri.buildUpon()
        .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
        .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
        .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
 }

لتنفيذ نموذج محول مزامنة (لا تتعلق على وجه التحديد بالتقويم )، راجع سامبلسينكادابتر .

مزود جهات الاتصال
نظرة سريعة
مستودع الروبوت من المعلومات حول الناس.
مزامنة مع شبكة الإنترنت.
يدمج بيانات ساحة المشاركات الاجتماعية.
في هذه الوثيقة
مؤسسة مزود الاتصالات
جهات اتصال أولية
البيانات
جهات الاتصال
البيانات من محولات المزامنة
الأذونات المطلوبة
الملف الشخصي للمستخدم
البيانات الوصفية لموفر جهات الاتصال
وصول مزود الاتصالات
كونتاكتس بروفيدر سينك أدابترس
بيانات ساحة المشاركات الاجتماعية
ميزات إضافية لموفر جهات الاتصال
الطبقات الرئيسية
ContactsContract.Contacts
ContactsContract.RawContacts
ContactsContract.Data
عينات ذات الصلة
جهات الاتصال الأساسية
أنظر أيضا
أساسيات موفر المحتوى
موفر اتصالات هو عنصر الروبوت قوية ومرنة التي تدير مستودع الجهاز المركزي للبيانات حول الناس. موفر جهات الاتصال هو مصدر البيانات التي تراها في تطبيق جهات اتصال الجهاز، ويمكنك أيضا الوصول إلى بياناته في التطبيق الخاص بك ونقل البيانات بين الجهاز والخدمات عبر الإنترنت. ويستوعب مقدم الخدمة مجموعة واسعة من مصادر البيانات ويحاول إدارة أكبر قدر ممكن من البيانات لكل شخص، مما يؤدي إلى تعقيد تنظيمه. وبسبب هذا، يتضمن أبي مزود مجموعة واسعة من فئات العقد والواجهات التي تسهل كل من استرجاع البيانات وتعديلها.

يصف هذا الدليل ما يلي:

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

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


الشكل 1. بنية الجدول مزود جهات الاتصال.


وعادة ما يشار إلى الجداول الثلاثة بأسماء فئات العقود الخاصة بهم. تحدد الفئات الثوابت لعناوين ورل للمحتوى وأسماء الأعمدة وقيم الأعمدة التي تستخدمها الجداول:

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

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

لا يتم تخزين معظم البيانات للاتصال الخام في جدول ContactsContract.RawContacts . بدلا من ذلك، يتم تخزينها في صف واحد أو أكثر في جدول contactsContract.Data. يحتوي كل صف بيانات على عمود Data.RAW_CONTACT_ID يحتوي على قيمة RawContacts._ID الأصل.

أعمدة الاتصال الأولية الهامة
يتم سرد الأعمدة الهامة في الجدول ContactsContract.RawContacts في الجدول 1. يرجى قراءة الملاحظات التي تتبع بعد الجدول:

الجدول 1. أعمدة الاتصال الأولية الهامة.
اسم العمود استعمال ملاحظات
ACCOUNT_NAME اسم الحساب لنوع الحساب الذي هو مصدر جهة الاتصال هذه. على سبيل المثال، اسم حساب أحد حسابات غوغل هو أحد عناوين غميل لمالك الجهاز. راجع الإدخال التالي ل ACCOUNT_TYPE للحصول على مزيد من المعلومات. تنسيق هذا الاسم محدد لنوع الحساب. ليس بالضرورة عنوان بريد إلكتروني.
ACCOUNT_TYPE نوع الحساب الذي هو مصدر جهة الاتصال هذه. على سبيل المثال، نوع الحساب لحساب غوغل هو com.google . تأهل دائما نوع حسابك مع معرف نطاق لنطاق تملكه أو تتحكم فيه. سيضمن ذلك أن نوع حسابك فريد. يحتوي نوع الحساب الذي يقدم بيانات جهات الاتصال عادة على محول المزامنة المقترن الذي تتم مزامنته مع موفر جهات الاتصال.
DELETED العلامة "المحذوفة" لجهة اتصال أولية. تسمح هذه العلامة لموفر جهات الاتصال بالاحتفاظ بالصف داخليا حتى تصبح محولات المزامنة قادرة على حذف الصف من وحدات الخدمة الخاصة بهم ومن ثم حذف الصف من مستودع التخزين.
ملاحظات
فيما يلي ملاحظات هامة حول جدول ContactContract.RawContacts:

لا يتم تخزين اسم جهة الاتصال الأولية في صفه في ContactsContract.RawContacts . بدلا من ذلك، يتم تخزينها في جدول ContactsContract.Data ، في صف ContactsContract.CommonDataKinds.StructuredName ContonDataKinds.StructuredName. تحتوي جهة الاتصال الأولية على صف واحد فقط من هذا النوع في جدول contactsContract.Data.
تحذير: لاستخدام بيانات الحساب الخاص بك في صف الاتصال الخام، يجب أولا أن تكون مسجلة مع AccountManager . لإجراء ذلك، اطلب من المستخدمين إضافة نوع الحساب واسم الحساب إلى قائمة الحسابات. إذا لم تقم بذلك، فسيحذف موفر جهات الاتصال صف الاتصال الخام تلقائيا.
على سبيل المثال، إذا كنت تريد أن يحتفظ تطبيقك ببيانات جهات الاتصال للخدمة المستندة إلى الويب من خلال النطاق com.example.dataservice ، وأن حساب المستخدم becky.sharp@dataservice.example.com هو becky.sharp@dataservice.example.com ، يجب على المستخدم أولا إضافة الحساب " com.example.dataservice " ( com.example.dataservice ) والحساب " becky.smart@dataservice.example.com " ( becky.smart@dataservice.example.com ) قبل أن يتمكن تطبيقك من إضافة صفوف جهات اتصال أولية. يمكنك توضيح هذا الشرط للمستخدم في الوثائق، أو يمكنك مطالبة المستخدم بإضافة النوع والاسم، أو كليهما. يتم وصف أنواع الحسابات وأسماء الحسابات بمزيد من التفصيل في القسم التالي.
مصادر بيانات جهات الاتصال الأولية
لفهم كيفية عمل جهات الاتصال الخام، والنظر في المستخدم "إميلي ديكنسون" الذي لديه حسابات المستخدمين الثلاثة التالية المحددة على جهازها:

emily.dickinson@gmail.com
emilyd@gmail.com
حساب تويتر "belle_of_amherst"
لقد مكن هذا المستخدم مزامنة جهات الاتصال لجميع هذه الحسابات الثلاثة في إعدادات الحسابات .

لنفترض إميلي ديكنسون يفتح نافذة المتصفح، يسجل في غميل كما emily.dickinson@gmail.com ، يفتح اتصالات، ويضيف "توماس هيجينسون". في وقت لاحق، وقالت انها يسجل في غميل كما emilyd@gmail.com ويرسل بريدا إلكترونيا إلى "توماس هيجينسون"، والذي يضيف تلقائيا له كجهة اتصال. كما أنها تتبع "colonel_tom" (هوية تويتر توماس هيغنسون) على تويتر.

ينشئ موفر جهات الاتصال ثلاث جهات اتصال أولية نتيجة لهذا العمل:

اتصال الخام ل "توماس هيجينسون" المرتبطة emily.dickinson@gmail.com . نوع حساب المستخدم هو غوغل.
اتصال الخام الثاني ل "توماس هيجينسون" المرتبطة emilyd@gmail.com . نوع حساب المستخدم هو أيضا غوغل. هناك جهة اتصال أولية ثانية على الرغم من أن الاسم مطابق لاسم سابق، لأن الشخص تمت إضافته لحساب مستخدم مختلف.
اتصال ثالث ثالث ل "توماس هيجينسون" المرتبطة "belle_of_amherst". نوع حساب المستخدم هو تويتر.
البيانات
كما ذكر سابقا، يتم تخزين البيانات الخاصة بجهة اتصال أولية في صف ContactContract.Data مرتبط بقيمة _ID لجهة الاتصال الأولية. وهذا يسمح لجهة اتصال واحدة واحدة أن يكون مثيلات متعددة من نفس النوع من البيانات مثل عناوين البريد الإلكتروني أو أرقام الهواتف. على سبيل المثال، إذا كان "توماس هيجينسون" ل emilyd@gmail.com (صف الاتصال الخام لتوماس هيجينسون المرتبطة حساب جوجل emilyd@gmail.com ) لديه عنوان البريد الإلكتروني للمنزل thigg@gmail.com وعنوان البريد الإلكتروني العمل من thomas.higginson@gmail.com ، يقوم مزود جهات الاتصال بتخزين صفين عنوان البريد الإلكتروني thomas.higginson@gmail.com بكل من جهة الاتصال الأولية.

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

أسماء الأعمدة الوصفية
بعض الأمثلة لأسماء الأعمدة الوصفية هي:

RAW_CONTACT_ID
قيمة العمود _ID جهة الاتصال الأولية لهذه البيانات.
MIMETYPE
نوع البيانات المخزنة في هذا الصف، والتي تم التعبير عنها كنوع مايم مخصص. يستخدم موفر جهات الاتصال أنواع مايم المعرفة في الفئات الفرعية من ContactsContract.CommonDataKinds . هذه الأنواع مايم مفتوحة المصدر، ويمكن استخدامها من قبل أي تطبيق أو المزامنة محول يعمل مع مزود جهات الاتصال.
IS_PRIMARY
إذا كان هذا النوع من صف البيانات يمكن أن يحدث أكثر من مرة للاتصال الخام، العمود IS_PRIMARY أعلام صف البيانات الذي يحتوي على البيانات الأساسية للنوع. على سبيل المثال، إذا كان المستخدم يضغط طويلا على رقم هاتف لجهة اتصال ثم حدد سيت IS_PRIMARY ، فإن صف IS_PRIMARY الذي يحتوي على هذا الرقم له مجموعة IS_PRIMARY تعيين قيمة غير صفرية.
أسماء الأعمدة العامة
هناك 15 أعمدة عامة تدعى DATA1 خلال DATA15 المتوفرة بشكل عام وأربعة أعمدة عامة إضافية SYNC1 خلال SYNC4 يجب أن تستخدم فقط بواسطة محولات المزامنة. تعمل ثوابت اسم العمود العام دائما، بغض النظر عن نوع البيانات التي يحتوي عليها الصف.

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

وفقا للاتفاقية، DATA15 العمود DATA15 لتخزين بيانات كائن ثنائي كبير (بلوب) مثل الصور المصغرة.

أسماء الأعمدة الخاصة بالنوع
لتسهيل العمل مع الأعمدة لنوع معين من الصف، يوفر موفر جهات الاتصال أيضا الثوابت اسم العمود نوع محددة، المعرفة في الفئات الفرعية من ContactContract.CommonDataKinds. الثوابت ببساطة إعطاء اسم ثابت مختلف إلى نفس اسم العمود، والتي تساعدك على الوصول إلى البيانات في صف من نوع معين.

على سبيل المثال، تعرف فئة Email.CONTENT_ITEM_TYPE الثوابت اسم العمود النوع محددة لصف Email.CONTENT_ITEM_TYPE الذي يحتوي على نوع مايم Email.CONTENT_ITEM_TYPE . تحتوي الفئة على ADDRESS ثابت لعنوان عنوان البريد الإلكتروني. القيمة الفعلية ADDRESS هي "data1"، وهو نفس الاسم العام للعمود.

تحذير: لا تقم بإضافة البيانات المخصصة الخاصة بك إلى جدول contactsContract.Data باستخدام صف يحتوي على أحد أنواع مايم المحددة مسبقا للموفر. إذا قمت بذلك، فقد تفقد البيانات أو تتسبب في عطل الموفر. على سبيل المثال، يجب عدم إضافة صف مع نوع مايم Email.CONTENT_ITEM_TYPE الذي يحتوي على اسم مستخدم بدلا من عنوان بريد إلكتروني في العمود DATA1 . إذا كنت تستخدم نوع مايم المخصص الخاص بك للصف، فأنت حر في تحديد أسماء الأعمدة الخاصة بنوع خاص بك واستخدام الأعمدة مع ذلك.

يوضح الشكل 2 كيف تظهر الأعمدة الوصفية وأعمدة البيانات في صف contactsContract.Data وكيف يتم تسمية أسماء الأعمدة الخاصة بالنوع "تراكب" أسماء الأعمدة العامة
الشكل 2. أسماء الأعمدة الخاصة بالنوع وأسماء الأعمدة العامة.

فئات اسم الأعمدة الخاصة بالنوع
يسرد الجدول 2 فئات أسماء الأعمدة الأكثر استخداما من النوع النوعي:

جدول 2. فئات اسم الأعمدة الخاصة بالنوع
فئة التعيين نوع البيانات ملاحظات
ContactsContract.CommonDataKinds.StructuredName بيانات اسم جهة الاتصال الأولية المرتبطة بصف البيانات هذا. تحتوي جهة الاتصال الأولية على واحد فقط من هذه الصفوف.
ContactsContract.CommonDataKinds.Photo الصورة الرئيسية للاتصال الخام المرتبطة مع صف البيانات هذا. تحتوي جهة الاتصال الأولية على واحد فقط من هذه الصفوف.
ContactsContract.CommonDataKinds.Email عنوان بريد إلكتروني لجهة الاتصال الأولية المرتبطة بصف البيانات هذا. يمكن أن تحتوي جهة الاتصال الأولية على عناوين بريد إلكتروني متعددة.
ContactsContract.CommonDataKinds.StructuredPostal عنوان بريدي لجهة الاتصال الأولية المرتبطة بصف البيانات هذا. يمكن أن تحتوي جهة الاتصال الأولية على عناوين بريدية متعددة.
ContactsContract.CommonDataKinds.GroupMembership معرف يربط جهة الاتصال الأولية بأحد المجموعات في موفر جهات الاتصال. المجموعات هي ميزة اختيارية لنوع الحساب واسم الحساب. يتم وصفها بمزيد من التفصيل في قسم جهات الاتصال .
جهات الاتصال
يجمع موفر جهات الاتصال صفوف الاتصال الأولية عبر جميع أنواع الحسابات وأسماء الحسابات لتشكيل جهة اتصال . وهذا يسهل عرض وتعديل جميع البيانات التي جمعها المستخدم لشخص ما. يدير موفر جهات الاتصال إنشاء صفوف اتصال جديدة، وتجميع جهات الاتصال الأولية مع صف اتصال موجود. لا يسمح للتطبيقات ولا محولات المزامنة بإضافة جهات اتصال، وبعض الأعمدة في صف الاتصال للقراءة فقط.

ملاحظة: إذا حاولت إضافة جهة اتصال إلى موفر جهات الاتصال مع insert() ، فستحصل على استثناء UnsupportedOperationException . إذا حاولت تحديث عمود مسرد ك "للقراءة فقط"، فسيتم تجاهل التحديث.

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

يرسل موفر جهات الاتصال صف جهة اتصال إلى صفوف جهات الاتصال الأولية مع عمود _ID لصف الاتصال في جدول Contacts . العمود CONTACT_ID من جدول جهات الاتصال الأولية _ID يحتوي _ID على قيم _ID لصف جهات الاتصال المقترنة بكل صف من جهات الاتصال الأولية.

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

ويبين الشكل 3 كيفية ارتباط الجداول الرئيسية الثلاثة ببعضها البعض.



الشكل 3. اتصالات، اتصالات أولية، وتفاصيل العلاقات الجدول.

البيانات من محولات المزامنة
يدخل المستخدمون بيانات جهات الاتصال مباشرة في الجهاز، ولكن البيانات تتدفق أيضا إلى مزود جهات الاتصال من خدمات الويب عبر محولات المزامنة ، والتي تقوم تلقائيا بنقل البيانات بين الجهاز والخدمات. تشغيل محولات المزامنة في الخلفية تحت سيطرة النظام، ويطلقون على أساليب ContentResolver لإدارة البيانات.

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

نوع الحساب
يحدد الخدمة التي قام المستخدم بتخزين البيانات فيها. معظم الوقت، المستخدم لديه لمصادقة مع الخدمة. على سبيل المثال، جهات اتصال غوغل هي نوع حساب، يتم تحديده بواسطة الشفرة google.com . تتوافق هذه القيمة مع نوع الحساب المستخدم بواسطة AccountManager .
أسم الحساب
يحدد حسابا أو تسجيل دخول معين لنوع الحساب. حسابات جهات اتصال غوغل هي نفسها حسابات غوغل، التي لها عنوان بريد إلكتروني كاسم حساب. قد تستخدم خدمات أخرى اسم مستخدم واحد كلمة أو معرف رقمي.
لا يجب أن تكون أنواع الحسابات فريدة. يمكن للمستخدم تهيئة حسابات جهات اتصال غوغل متعددة وتنزيل بياناته إلى موفر جهات الاتصال؛ قد يحدث هذا إذا كان المستخدم لديه مجموعة واحدة من جهات الاتصال الشخصية لاسم حساب شخصي، ومجموعة أخرى للعمل. تكون أسماء الحسابات فريدة من نوعها عادة. معا، فإنها تحدد تدفق بيانات محددة بين مزود جهات الاتصال وخدمة خارجية.

إذا كنت ترغب في نقل بيانات الخدمة إلى موفر جهات الاتصال، فسيلزمك كتابة محول المزامنة الخاص بك. يتم وصف ذلك بمزيد من التفصيل في القسم كونتاكتس بروفيدر سينك أدابترس .

ويبين الشكل 4 كيف يتناسب مزود خدمة الاتصالات مع تدفق البيانات حول الأشخاص. في المربع المسمى "محولات المزامنة"، يتم تسمية كل محول بواسطة نوع الحساب الخاص به.



الشكل 4. تدفق مزود جهات الاتصال للبيانات.

الأذونات المطلوبة
يجب أن تطلب التطبيقات التي تريد الدخول إلى موفر جهات الاتصال الأذونات التالية:

قراءة حق الوصول إلى جدول واحد أو أكثر
READ_CONTACTS ، المحدد في AndroidManifest.xml باستخدام العنصر <uses-permission android:name="android.permission.READ_CONTACTS"> READ_CONTACTS <uses-permission> ك <uses-permission android:name="android.permission.READ_CONTACTS"> READ_CONTACTS <uses-permission android:name="android.permission.READ_CONTACTS"> .
كتابة الوصول إلى واحد أو أكثر من الجداول
WRITE_CONTACTS ، المحدد في AndroidManifest.xml باستخدام العنصر <uses-permission android:name="android.permission.WRITE_CONTACTS"> WRITE_CONTACTS <uses-permission> ك <uses-permission android:name="android.permission.WRITE_CONTACTS"> WRITE_CONTACTS <uses-permission android:name="android.permission.WRITE_CONTACTS"> .
لا تمتد هذه الأذونات إلى بيانات ملف تعريف المستخدم. يتم مناقشة ملف تعريف المستخدم والأذونات المطلوبة في القسم التالي، الملف الشخصي للمستخدم .

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

الملف الشخصي للمستخدم
يحتوي جدول contactsContract.Contacts على صف واحد يحتوي على بيانات الملف الشخصي لمستخدم الجهاز. توضح هذه البيانات user الجهاز بدلا من أحد جهات اتصال المستخدم. يتم ربط صف جهات الاتصال الشخصية بصف جهات اتصال أولية لكل نظام يستخدم ملفا شخصيا. يمكن أن يحتوي صف كل صف شخصي لصفوف بيانات متعددة على صفوف بيانات متعددة. الثوابت للوصول إلى ملف تعريف المستخدم متوفرة في فئة ContactsContract.Profile .

يتطلب الوصول إلى ملف تعريف المستخدم أذونات خاصة. بالإضافة إلى READ_CONTACTS و WRITE_CONTACTS الأذونات اللازمة للقراءة والكتابة، والوصول إلى ملف تعريف المستخدم يتطلب android.Manifest.permission # READ_PROFILE و android.Manifest.permission # WRITE_PROFILE الأذونات للقراءة والكتابة الوصول، على التوالي.

تذكر أنه يجب مراعاة الملف الشخصي للمستخدم ليكون حساسا. إذن android.Manifest.permission # READ_PROFILE يسمح لك بالوصول إلى بيانات المستخدم شخصيا تحديد الهوية. تأكد من إخبار المستخدم لماذا تحتاج إلى أذونات الوصول إلى ملف تعريف المستخدم في وصف التطبيق الخاص بك.

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


// Sets the columns to retrieve for the user profile
mProjection = new String[]
    {
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMARY,
        Profile.LOOKUP_KEY,
        Profile.PHOTO_THUMBNAIL_URI
    };

// Retrieves the profile from the Contacts Provider
mProfileCursor =
        getContentResolver().query(
                Profile.CONTENT_URI,
                mProjection ,
                null,
                null,
                null);


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

البيانات الوصفية لموفر جهات الاتصال
يدير موفر جهات الاتصال البيانات التي تتعقب حالة بيانات جهات الاتصال في مستودع التخزين. يتم تخزين هذه البيانات التعريفية للمستودع في أماكن مختلفة، بما في ذلك صفوف جدول جهات الاتصال والبيانات والبيانات جهات الاتصال، وطاولة ContactsContract.Settings ، والجدول contactsContract.SyncState. ويبين الجدول التالي أثر كل قطعة من هذه البيانات الوصفية:

الجدول 3. البيانات الوصفية في موفر جهات الاتصال
الطاولة عمود القيم المعنى
ContactsContract.RawContacts DIRTY "0" - لم يتغير منذ آخر مزامنة. علامات جهات الاتصال الأولية التي تم تغييرها على الجهاز ويجب أن تتم مزامنتها مرة أخرى إلى الملقم. يتم تعيين القيمة تلقائيا بواسطة موفر جهات الاتصال عند تحديث تطبيقات أندرويد صفا.
يجب أن CALLER_IS_SYNCADAPTER محولات المزامنة التي تعدل جهات الاتصال الأولية أو جداول البيانات السلسلة CALLER_IS_SYNCADAPTER دائما CALLER_IS_SYNCADAPTER معرف أوري للمحتوى الذي تستخدمه. يمنع هذا الموفر من وضع علامات على الصفوف على أنها متسخة. خلاف ذلك، يبدو أن تعديلات محول المزامنة تعديلات محلية ويتم إرسالها إلى الملقم، على الرغم من أن الملقم هو مصدر التعديل.
"1" - تغير منذ المزامنة الأخيرة، يحتاج إلى مزامن مرة أخرى إلى الملقم.
ContactsContract.RawContacts VERSION رقم إصدار هذا الصف. يقوم موفر جهات الاتصال تلقائيا بزيادة هذه القيمة كلما تغير الصف أو البيانات ذات الصلة.
ContactsContract.Data DATA_VERSION رقم إصدار هذا الصف. يقوم موفر جهات الاتصال تلقائيا بزيادة هذه القيمة كلما تم تغيير صف البيانات.
ContactsContract.RawContacts SOURCE_ID قيمة سلسلة تحدد هذا الاتصال الخام بشكل فريد للحساب الذي تم إنشاؤه. عندما يقوم محول المزامنة بتكوين جهة اتصال أولية جديدة، يجب أن يتم تعيين هذا العمود إلى معرف وحدة الخدمة الفريد الخاص بجهة الاتصال الأولية. عندما ينشئ تطبيق أندرويد جهة اتصال جديدة، يجب أن يترك التطبيق هذا العمود فارغا. هذا يشير محول المزامنة أنه يجب إنشاء جهة اتصال جديدة على الملقم، والحصول على قيمة SOURCE_ID .
على وجه الخصوص، يجب أن يكون معرف المصدر فريدا لكل نوع حساب ويجب أن يكون ثابتا عبر المزامنة:

فريد: يجب أن يكون لكل جهة اتصال أولية لحساب ما معرف المصدر الخاص بها. إذا لم تنفذ هذا، فستسبب مشكلات في تطبيق جهات الاتصال. لاحظ أن اثنين من جهات الاتصال الأولية لنوع الحساب نفسه قد يكون لها نفس معرف المصدر. على سبيل المثال، يسمح الاتصال الخام "توماس هيجينسون" للحساب emily.dickinson@gmail.com أن يكون نفس معرف المصدر كما الاتصال الخام "توماس هيجينسون" لحساب emilyd@gmail.com .
مستقر: إدس المصدر هي جزء دائم من بيانات الخدمة عبر الإنترنت للاتصال الخام. على سبيل المثال، إذا قام المستخدم بمسح جهات تخزين جهات الاتصال من إعدادات أبس وإعادة المزامنة، يجب أن يكون لدى جهات الاتصال الأولية المستعادة نفس معرفات المصدر كما كان من قبل. إذا لم تنفذ هذا، فستتوقف الاختصارات عن العمل.
ContactsContract.Groups GROUP_VISIBLE "0" - يجب ألا تكون جهات الاتصال في هذه المجموعة مرئية في واجهة مستخدم تطبيق أندرويد. هذا العمود هو التوافق مع الخوادم التي تسمح للمستخدم بإخفاء جهات الاتصال في مجموعات معينة.
"1" - يسمح للاتصالات في هذه المجموعة لتكون مرئية في أويس التطبيق.
ContactsContract.Settings UNGROUPED_VISIBLE "0" - لهذا الحساب ونوع الحساب، جهات الاتصال التي لا تنتمي إلى مجموعة غير مرئية لتطبيقات نظام التشغيل أندرويد. افتراضيا، جهات الاتصال غير مرئية إذا كان أي من جهات الاتصال الخاصة بهم ينتمي إلى مجموعة (عضوية مجموعة لاتصال الخام يشار إليها من قبل واحد أو أكثر من صفوف ContactContract.CommonDataKinds.GroupMembership في جدول contactsContract.Data). من خلال تعيين هذه العلامة في صف ContactsContract.Settings CContract.Settings لنوع الحساب والحساب، يمكنك فرض جهات الاتصال بدون مجموعات لتكون مرئية. أحد استخدام هذه العلامة هو إظهار جهات الاتصال من الخوادم التي لا تستخدم المجموعات.
"1" - لهذا الحساب ونوع الحساب، تكون جهات الاتصال التي لا تنتمي إلى مجموعة مرئية لواجهة مستخدم التطبيق.
ContactsContract.SyncState (الكل) استخدم هذا الجدول لتخزين البيانات الوصفية لمحول المزامنة. مع هذا الجدول يمكنك تخزين حالة المزامنة وغيرها من البيانات ذات الصلة المزامنة باستمرار على الجهاز.
وصول مزود الاتصالات
يصف هذا القسم إرشادات الوصول إلى البيانات من موفر جهات الاتصال، مع التركيز على ما يلي:

استفسارات الكيان.
دفعة التعديل.
استرجاع وتعديل مع النوايا.
تكامل البيانات.
يتم أيضا إجراء تعديلات من محول المزامنة بمزيد من التفصيل في القسم كونتاكتس بروفيدر سينك أدابترس .

للحصول على مثال حول كيفية استخدام موفر جهات الاتصال عند البحث عن جهات الاتصال، راجع نموذج "جهات الاتصال الأساسية" .

كيريينغ كياناتس
نظرا لأن جداول مزود جهات الاتصال يتم تنظيمها في تسلسل هرمي، فمن المفيد في كثير من الأحيان استرداد صف وكل الصفوف "الفرعية" المرتبطة به. على سبيل المثال، لعرض كافة المعلومات لشخص ما، قد تحتاج إلى استرداد كافة صفوف contactsContract.RawContacts صف واحد ContactContract.Contacts أو كافة صفوف ContactContract.CommonDataKinds.Email لصف ContactsContract.RawContacts Contract.RawContacts واحد. لتسهيل هذا، يوفر مزود جهات الاتصال الكيان يبني، والتي تعمل مثل قاعدة البيانات ينضم بين الجداول.

الكيان يشبه جدول يتألف من أعمدة مختارة من جدول الأصل وجدوله الفرعي. عند الاستعلام عن كيان، يمكنك توفير معايير إسقاط والبحث استنادا إلى الأعمدة المتوفرة من الكيان. والنتيجة هي Cursor الذي يحتوي على صف واحد لكل صف جدول طفل تم استرجاعه. على سبيل المثال، إذا قمت باستعلام contactsContract.Contacts.Entity للحصول على اسم جهة الاتصال وجميع صفوف ContactContract.CommonDataKinds.Email لجميع جهات الاتصال الأولية لهذا الاسم، يمكنك الحصول على مؤشر يحتوي على صف واحد لكل ContactsContract.CommonDataKinds.Email الصف.

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

ملاحظة: لا يحتوي الكيان عادة على كافة أعمدة الجدول الأصل و الطفل. إذا حاولت العمل مع اسم عمود غير موجود في قائمة ثوابت اسم العمود للكيان، فستحصل على Exception .

يعرض المقتطف التالي كيفية استرداد جميع صفوف جهات الاتصال الأولية لجهة اتصال. المقتطف هو جزء من تطبيق أكبر يحتوي على نشاطين "رئيسي" و "تفاصيل". يظهر النشاط الرئيسي قائمة الصفوف الاتصال. عندما يقوم المستخدم بتحديد واحد، والنشاط يرسل معرفها إلى النشاط التفاصيل. يستخدم النشاط التفصيلي ContactContract.Contacts.Entity لعرض جميع صفوف البيانات من جميع جهات الاتصال الأولية المقترنة جهة الاتصال المحددة.

يتم أخذ هذا المقتطف من النشاط "التفاصيل":


...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    mContactUri = Uri.withAppendedPath(
            mContactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);

    // Initializes the loader identified by LOADER_ID.
    getLoaderManager().initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this);      // The context of the activity

    // Creates a new cursor adapter to attach to the list view
    mCursorAdapter = new SimpleCursorAdapter(
            this,                        // the context of the activity
            R.layout.detail_list_item,   // the view item containing the detail widgets
            mCursor,                     // the backing cursor
            mFromColumns,                // the columns in the cursor that provide the data
            mToViews,                    // the views in the view item that display the data
            0);                          // flags

    // Sets the ListView's backing adapter.
    mRawContactList.setAdapter(mCursorAdapter);
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    String[] projection =
        {
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
        };

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    String sortOrder =
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
            " ASC";

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return new CursorLoader(
            getApplicationContext(),  // The activity's context
            mContactUri,              // The entity content URI for a single contact
            projection,               // The columns to retrieve
            null,                     // Retrieve all the raw contacts and their data rows.
            null,                     //
            sortOrder);               // Sort by the raw contact ID.
}

عند الانتهاء من تحميل، LoaderManager استدعاء رد على onLoadFinished() . واحدة من الحجج الواردة إلى هذا الأسلوب هو مؤشر مع نتائج الاستعلام. في التطبيق الخاص بك، يمكنك الحصول على البيانات من هذا Cursor لعرضه أو العمل معها أكثر من ذلك.

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

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

نقاط العائد
يمكن تعديل دفعة تحتوي على عدد كبير من العمليات منع عمليات أخرى، مما أدى إلى تجربة المستخدم بشكل عام سيئة. لتنظيم جميع التعديلات التي تريد تنفيذها في عدد قليل من قوائم منفصلة قدر الإمكان، وفي الوقت نفسه منعهم من حظر النظام، يجب تعيين نقاط العائد لعملية واحدة أو أكثر. نقطة العائد هو كائن ContentProviderOperation له قيمة isYieldAllowed() تعيين إلى isYieldAllowed() . عندما يواجه موفر جهات الاتصال نقطة عائد، فإنه يوقف عمله للسماح لعمليات أخرى بتشغيل وإغلاق الصفقة الحالية. عندما يبدأ الموفر مرة أخرى، فإنه يستمر مع العملية التالية في ArrayList ويبدأ معاملة جديدة.

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

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

تعديل مراجع العودة
عند إدراج صف اتصال خام جديد وصفوف البيانات المرتبطة به كمجموعة من عناصر ContentProviderOperation ، يجب ربط صفوف البيانات بصف صف جهات الاتصال الأولية عن طريق إدراج قيمة _ID لجهة الاتصال الأولية كقيمة RAW_CONTACT_ID . ومع ذلك، لا تتوفر هذه القيمة عند إنشاء ContentProviderOperation لصف البيانات، لأنك لم تقم بتطبيق تطبيق ContentProviderOperation حتى الآن على صف جهات الاتصال الأولية. كمحاولة للتغلب على هذه الفئة withValueBackReference() . withValueBackReference() لديه الأسلوب withValueBackReference() . تسمح لك هذه الطريقة بإدراج عمود أو تعديله مع نتيجة عملية سابقة.

يحتوي أسلوب withValueBackReference() :

key
مفتاح زوج مفتاح القيمة. يجب أن تكون قيمة هذه الوسيطة اسم عمود في الجدول الذي تقوم بتعديله.
previousResult
الفهرس الذي يستند إلى 0 لقيمة في مجموعة الكائنات ContentProviderResult من applyBatch() . كما يتم تطبيق عمليات دفعة، يتم تخزين نتيجة كل عملية في مجموعة وسيطة من النتائج. قيمة previousResult هي فهرس واحد من هذه النتائج التي يتم استرجاعها وتخزينها مع قيمة key . هذا يسمح لك بإدراج سجل اتصال خام جديد والعودة قيمة _ID ثم قم بعمل "مرجع الرجوع" إلى القيمة عند إضافة صف ContactContract.Data.
يتم إنشاء مجموعة النتائج بأكملها عند استدعاء أول applyBatch() ، مع حجم يساوي حجم applyBatch() من الكائنات ContentProviderOperation التي تقدمها.ومع ذلك، يتم تعيين جميع العناصر في المصفوفة نتيجة ل null، وإذا حاولت القيام إشارة إلى نتيجة لذلك لاجراء عملية جراحية التي لم يتم تطبيقها، withValueBackReference()يطرح Exception.

تظهر قصاصات التالية كيفية إدراج الاتصال والبيانات الخام جديدة دفعة واحدة. وتشمل هذه التعليمات البرمجية التي تحدد نقطة المحصول ويستخدم مرجعا في الظهر.

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

// Creates a contact entry from the current UI values, using the currently-selected account.
protected void createContactEntry() {
    /*
     * Gets values from the UI
     */
    String name = mContactNameEditText.getText().toString();
    String phone = mContactPhoneEditText.getText().toString();
    String email = mContactEmailEditText.getText().toString();

    int phoneType = mContactPhoneTypes.get(
            mContactPhoneTypeSpinner.getSelectedItemPosition());

    int emailType = mContactEmailTypes.get(
            mContactEmailTypeSpinner.getSelectedItemPosition());


المقتطف المقبل يخلق عملية لادخال الصف اتصال الخام في ContactsContract.RawContactsالجدول:

 /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

     // Creates a new array of ContentProviderOperation objects.
    ArrayList<ContentProviderOperation> ops =
            new ArrayList<ContentProviderOperation>();

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    ContentProviderOperation.Builder op =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());


بعد ذلك، رمز يخلق صفوف البيانات للصفوف اسم العرض، والهاتف، والبريد الإلكتروني.

يستخدم كل كائن عملية البناء withValueBackReference()للحصول على RAW_CONTACT_ID. وتشير إشارة إلى ContentProviderResultكائن من العملية الأولى، وهو ما يضيف الصف اتصال الخام وإرجاع الجديدة _IDالقيمة. ونتيجة لذلك، كل صف البيانات المرتبطة تلقائيا لها RAW_CONTACT_IDإلى جديد ContactsContract.RawContactsالصف الذي ينتمي إليه.

و ContentProviderOperation.Builderالكائن الذي يضيف الصف البريد الإلكتروني هو علامة مع withYieldAllowed()الذي يحدد نقطة العائد:


// Creates the display name for the new raw contact, as a StructuredName data row.
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified phone number and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified email and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());


يظهر المقتطف الماضي الدعوة إلى applyBatch()أن يدرج الاتصال الخام والبيانات صفوف جديدة.

// Ask the Contacts Provider to create a new contact
    Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
            mSelectedAccount.getType() + ")");
    Log.d(TAG,"Creating contact: " + name);

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {

            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    } catch (Exception e) {

            // Display a warning
            Context ctx = getApplicationContext();

            CharSequence txt = getString(R.string.contactCreationFailure);
            int duration = Toast.LENGTH_SHORT;
            Toast toast = Toast.makeText(ctx, txt, duration);
            toast.show();

            // Log exception
            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    }
}

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

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

لاستخدام عنصر تحكم التوافق الأمثل أثناء تحديث واحد ContactsContract.RawContactsصف واحد، اتبع الخطوات التالية:

استرداد اتصال الخام في VERSIONعمود جنبا إلى جنب مع غيرها من البيانات التي استرداد.
إنشاء ContentProviderOperation.Builderكائن مناسبة لفرض القيد، وذلك باستخدام طريقة newAssertQuery(Uri). عن محتوى URI، استخدم RawContacts.CONTENT_URIمع جهة الاتصال الخام _IDإلحاق إليه.
لل ContentProviderOperation.Builderكائن، والدعوة withValue()لمقارنة VERSIONالعمود إلى رقم الإصدار الذي تم استردادها فقط.
لنفسه ContentProviderOperation.Builder، والدعوة withExpectedCount()لضمان صف واحد فقط يتم اختبار هذا التأكيد.
دعوة build()لإنشاء ContentProviderOperationكائن، ثم يضاف هذا الكائن ككائن الأول في ArrayListأن تمرر ل applyBatch().
تطبيق الصفقة دفعة واحدة.
إذا تم تحديث الصف اتصال الخام من قبل عملية أخرى بين الوقت الذي تقرأ الصف والوقت الذي تحاول تعديله، و "تأكيد" ContentProviderOperationستفشل، ودفعة كاملة من العمليات سوف تكون مدعومة بها. يمكنك بعد ذلك اختيار لإعادة محاولة دفعي أو اتخاذ بعض الإجراءات الأخرى.

يوضح المقتطف التالي كيفية إنشاء "تأكيد" ContentProviderOperationبعد الاستعلام عن اسم واحد الخام باستخدام CursorLoader:

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    // Gets the raw contact's _ID and VERSION values
    mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}

...

// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);

// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);

// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);

// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList<ContentProviderOperationg>;

ops.add(assertOp.build());

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try
    {
        ContentProviderResult[] results =
                getContentResolver().applyBatch(AUTHORITY, ops);

    } catch (OperationApplicationException e) {

        // Actions you want to take if the assert operation fails go here
    }


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

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

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

ووصف العملية العامة لإرسال بقصد الوصول إلى موفر بالتفصيل في أساسيات مزود المحتوى دليل في قسم "الوصول إلى البيانات عبر النوايا." وتتلخص العمل، نوع MIME، والقيم البيانات التي تستخدمها للمهام المتاحة في الجدول رقم 4، في حين تقدر اشياء يمكنك استخدامها مع putExtra()المسرودة في الوثائق المرجعية ل ContactsContract.Intents.Insert:

الجدول 4. النوايا اتصالات موفر.
مهمة عمل البيانات نوع التمثيل الصامت ملاحظات
اختيار أحد الأسماء من قائمة ACTION_PICK واحد من:
Contacts.CONTENT_URI ، الذي يعرض قائمة الأسماء.
Phone.CONTENT_URI ، الذي يعرض قائمة من أرقام الهواتف للاتصال الخام.
StructuredPostal.CONTENT_URI ، الذي يعرض قائمة من العناوين البريدية لجهة اتصال الخام.
Email.CONTENT_URI ، الذي يعرض قائمة من عناوين البريد الإلكتروني لجهة اتصال الخام.
غير مستعمل يعرض قائمة من الاتصالات الخام أو قائمة من البيانات من الاتصال الخام، اعتمادا على محتوى نوع URI قمت بتوفير.
استدعاء startActivityForResult()، والتي ترجع URI المحتوى من الصف المحدد. شكل URI هو محتوى الجدول URI مع الصف و LOOKUP_IDإلحاق إليه. المندوبين الاتصالات التطبيق للجهاز القراءة والكتابة أذونات لهذا المحتوى URI لحياة نشاطك. رؤية المحتوى أساسيات مزود الدليل لمزيد من التفاصيل.
اضافة الى وجود اتصال الخام الجديد Insert.ACTION N / A RawContacts.CONTENT_TYPE ، نوع MIME لمجموعة من الاتصالات الخام. يعرض جهاز الاتصالات التطبيق إضافة جهة اتصال الشاشة. يتم عرض القيم اشياء تضيفها إلى القصد. إذا أرسلت مع startActivityForResult()، يتم تمرير محتوى URI للاتصال الخام المضافة حديثا مرة أخرى إلى النشاط الخاص بك onActivityResult()طريقة الاستدعاء في Intentحجة، في حقل "البيانات". للحصول على القيمة، والدعوة getData().
تعديل اسم ACTION_EDIT CONTENT_LOOKUP_URIللإتصال به. فإن النشاط محرر تسمح للمستخدم لتحرير أي من البيانات المرتبطة بهذا الاتصال. Contacts.CONTENT_ITEM_TYPE ، جهة اتصال واحدة. يعرض على الشاشة تحرير الاتصال في تطبيق جهات الاتصال. يتم عرض القيم اشياء تضيفها إلى القصد. عندما ينقر المستخدم تم لحفظ التعديلات، نشاطك يعود إلى المقدمة.
عرض منتقي التي يمكن أيضا إضافة البيانات. ACTION_INSERT_OR_EDIT N / A CONTENT_ITEM_TYPE هذه النية يعرض دائما شاشة منتقي التطبيق الاتصالات و. يمكن للمستخدم إما اختيار جهة اتصال لتعديل أو إضافة جهة اتصال جديدة. يبدو إما تحرير أو شاشة إضافية، اعتمادا على اختيار المستخدم، ويتم عرض البيانات اشياء كنت تمر في القصد. إذا يعرض التطبيق بيانات الاتصال مثل البريد الإلكتروني أو رقم الهاتف، واستخدام هذه النية للسماح للمستخدم لإضافة البيانات إلى جهة اتصال موجودة. اتصل،
ملاحظة: ليس هناك حاجة لإرسال قيمة اسم في اشياء هذه النية، لأن المستخدم يختار دائما اسم موجود أو يضيف واحدة جديدة. وعلاوة على ذلك، إذا قمت بإرسال الاسم، ويختار المستخدم للقيام تحرير، فإن التطبيق الاتصالات عرض اسم ترسلها، الكتابة فوق القيمة السابقة. إذا كان المستخدم لا تلاحظ هذا ويحفظ تحرير، يتم فقدان القيمة القديمة.

الاتصالات التطبيق للجهاز لا يسمح لك لحذف اتصال الخام أو أي من البيانات الخاصة به مع وجود نية. بدلا من ذلك، لحذف اتصال الخام، واستخدام ContentResolver.delete()أو ContentProviderOperation.newDelete().

يظهر المقتطف التالي كيفية بناء وترسل نية أن إدراج اتصال الخام الجديد والبيانات:


// Gets values from the UI
String name = mContactNameEditText.getText().toString();
String phone = mContactPhoneEditText.getText().toString();
String email = mContactEmailEditText.getText().toString();

String company = mCompanyName.getText().toString();
String jobtitle = mJobTitle.getText().toString();

// Creates a new intent for sending to the device's contacts application
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);

// Sets the MIME type to the one expected by the insertion activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);

// Sets the new contact name
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);

// Sets the new company and job title
insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();


/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
ContentValues rawContactRow = new ContentValues();

// Adds the account type and name to the row
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());

// Adds the row to the array
contactData.add(rawContactRow);

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
ContentValues phoneRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
phoneRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
);

// Adds the phone number and its type to the row
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);

// Adds the row to the array
contactData.add(phoneRow);

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
ContentValues emailRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
emailRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
);

// Adds the email address and its type to the row
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);

// Adds the row to the array
contactData.add(emailRow);

/*
 * Adds the array to the intent's extras. It must be a parcelable object in order to
 * travel between processes. The device's contacts app expects its key to be
 * Intents.Insert.DATA
 */
insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent);




تكامل البيانات
لأن مستودع يحتوي على بيانات الاتصالات الهامة والحساسة التي يتوقع المستخدمون أن تكون صحيحة وتصل حتى الآن، واتصالات مزود قواعد سلامة البيانات واضحة المعالم. انها مسؤوليتكم لتتوافق مع هذه القواعد عند تعديل بيانات الأسماء. يتم سرد القواعد الهامة هنا:

دائما إضافة ContactsContract.CommonDataKinds.StructuredNameصف واحد لكل ContactsContract.RawContactsصف وإضافة.
A ContactsContract.RawContactsالتوالي دون ContactsContract.CommonDataKinds.StructuredNameصف في ContactsContract.Dataالجدول قد يسبب مشاكل أثناء التجميع.
ربط دائما جديدة ContactsContract.Dataالصفوف والدهم ContactsContract.RawContactsالتوالي.
A ContactsContract.Dataالتوالي التي لم يتم ربطها ContactsContract.RawContactsلن تكون مرئية في تطبيق جهات الاتصال للجهاز، وأنه قد يسبب مشاكل مع محولات متزامنة.
تغيير البيانات فقط لهذه الاتصالات الأولية التي تمتلكها.
تذكر أن اتصالات مزود عادة إدارة البيانات من عدة أنواع الحسابات المختلفة / الخدمات عبر الإنترنت. تحتاج إلى التأكد من أن التطبيق الخاص بك يعدل فقط أو حذف البيانات عن الصفوف التي تنتمي إلى لك، وأنه فقط إدراج البيانات مع نوع الحساب والاسم الذي يمكنك التحكم.
دائما استخدام الثوابت المحددة في ContactsContractوالفئات الفرعية للسلطات، محددات المحتوى، ومسارات URI، أسماء الأعمدة، وأنواع MIME، و TYPEالقيم.
باستخدام هذه الثوابت يساعدك على تجنب الأخطاء. وسيتم إشعارك أيضا مع تحذيرات مترجم إذا كان أي من الثوابت هو مستنكر.
صفوف البيانات المخصصة
من خلال خلق وباستخدام أنواع MIME المخصصة الخاصة بك، يمكنك إدراج، تحرير، حذف، واسترداد صفوف البيانات الخاصة بك في ContactsContract.Dataالجدول. تقتصر الصفوف لاستخدام عمود تعريف في ContactsContract.DataColumns، على الرغم من أنك يمكن تعيين نوع معين الخاصة أسماء الأعمدة لأسماء الأعمدة الافتراضية. في تطبيق جهات الاتصال في الجهاز، يتم عرض البيانات للصفوف الخاصة بك ولكن لا يمكن تحريرها أو حذفها، ولا يمكن للمستخدمين إضافة بيانات إضافية. السماح للمستخدمين بتعديل صفوف البيانات المخصصة الخاصة بك، يجب توفير النشاط محرر في التطبيق الخاص بك.

لعرض البيانات المخصصة، وتوفير contacts.xmlملف يحتوي على <ContactsAccountType>عنصر واحد أو أكثر من لها <ContactsDataKind>العناصر التابعة. يوصف هذا بمزيد من التفصيل في القسم <ContactsDataKind> element.

لمعرفة المزيد حول أنواع MIME مخصصة، وقراءة إنشاء موفر المحتوى دليل.

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

على الرغم من أنك يمكن تنفيذ تزامن في مجموعة متنوعة من الطرق، يوفر نظام أندرويد المكونات في إطار التزامن بأتمتة المهام التالية:

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

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

ملاحظة: باستخدام نوع الحساب كجزء من هوية محول المزامنة ويسمح هذا النظام لكشف ومجموعة مزامنة معا المحولات التي الوصول إلى الخدمات المختلفة من نفس المؤسسة. على سبيل المثال، محولات متزامنة لخدمات جوجل على الانترنت في جميع لديها نفس نوع الحساب com.google. عند قيام المستخدمين بإضافة حساب Google لأجهزتهم، يتم سرد كافة محولات متزامنة تثبيت لخدمات Google معا. كل محول المزامنة المصاحبة مدرجة مع موفر محتوى مختلف على الجهاز.

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

يجمع اسم المستخدم أو كلمة المرور أو معلومات مماثلة (المستخدم أوراق اعتماد ).
يرسل أوراق الاعتماد إلى الخدمة
يدرس الرد الخدمة.
إذا كانت الخدمة تقبل أوراق الاعتماد، يمكن المصادق تخزين بيانات الاعتماد لاستخدامها لاحقا. بسبب إطار الموثق في المكونات، و AccountManagerيمكن أن توفر الوصول إلى أي authtokens على الدعم الموثق ويختار لفضح مثل authtokens في OAuth2.

على الرغم من عدم المصادقة مطلوبة، ومعظم الخدمات الاتصالات استخدامها. ومع ذلك، كنت لا حاجة لاستخدام إطار المصادقة الروبوت للقيام المصادقة.

تنفيذ محول المزامنة
لتنفيذ محول مزامنة لمزود اتصالات، عليك أن تبدأ من خلال خلق الروبوت يحتوي على ما يلي:

A Serviceالمكون الذي يستجيب لطلبات من نظام لربط محول المزامنة.
عندما يريد نظام تشغيل التزامن، يقوم باستدعاء الخدمة onBind()طريقة للحصول على IBinderلمحول المزامنة. وهذا يسمح للنظام للقيام عبر عملية يدعو إلى أساليب للمحول.
محول متزامنة الفعلي، كما نفذت فئة فرعية محددة من AbstractThreadedSyncAdapter.
هذه الفئة لا عمل من تحميل البيانات من الخادم، وتحميل البيانات من الجهاز، وتسوية النزاعات. يتم العمل الرئيسي للمحول في الأسلوب onPerformSync(). يجب مثيل هذه الفئة مثل المفرد.
A فئة فرعية من Application.
تعمل هذه الفئة كمصنع للالمفرد محول المزامنة. استخدام onCreate()طريقة إنشاء مثيل محول المزامنة، وتوفير ثابت طريقة "حاصل" للعودة إلى المفرد ل onBind()طريقة خدمة محول مزامنة ل.
اختياري: A Serviceالمكون الذي يستجيب لطلبات من نظام للمصادقة المستخدم.
AccountManagerتبدأ هذه الخدمة لبدء عملية المصادقة. الخدمة في onCreate()طريقة instantiates كائن المصدق. عندما يريد النظام لمصادقة حساب المستخدم لمحول مزامنة التطبيق، تستدعي الخدمة onBind()طريقة للحصول على IBinderلالمصدق. وهذا يسمح للنظام للقيام عبر عملية يدعو إلى أساليب المصدق ل..
اختياري: A فرعية محددة من AbstractAccountAuthenticatorأن يعالج طلبات المصادقة.
توفر هذه الفئة أساليب أن AccountManagerيتضرع إلى مصادقة أوراق اعتماد المستخدم مع الخادم. تختلف تفاصيل عملية المصادقة على نطاق واسع، على أساس تكنولوجيا الخادم في الاستخدام. يجب عليك الرجوع إلى وثائق برنامج الخادم الخاص بك لمعرفة المزيد حول مصادقة.
ملفات XML التي تحدد محول متزامنة والموثق لهذا النظام.
يتم تعريف المكونات محول وخدمة الموثق متزامنة الموصوفة سابقا في عناصر في بيان التطبيق. هذه العناصر تحتوي على العناصر التابعة التي توفر بيانات محددة للنظام:< service >< meta-data >
و العنصر لنقاط الخدمة محول المزامنة لملف XML . بدوره، يحدد هذا الملف URI لخدمة الويب التي من شأنها أن تكون متزامنة مع موفر اتصالات، ونوع الحساب لخدمة الويب.< meta-data >res/xml/syncadapter.xml
اختياري: إن عنصر لنقاط المصدق إلى ملف XML . بدوره، يحدد هذا الملف نوع الحساب الذي يدعم هذا الموثق، فضلا عن الموارد UI التي تظهر أثناء عملية المصادقة. نوع الحساب المحدد في هذا العنصر يجب أن يكون نفس نوع الحساب المحدد لمحول المزامنة.< meta-data >res/xml/authenticator.xml
الاجتماعية تدفق البيانات
وandroid.provider.ContactsContract.StreamItems والجداول android.provider.ContactsContract.StreamItemPhotos إدارة البيانات الواردة من الشبكات الاجتماعية. يمكنك كتابة محول المزامنة أن يضيف تدفق البيانات من الشبكة الخاصة بك لهذه الجداول، أو يمكنك قراءة البيانات تيار من هذه الجداول وعرضها في التطبيق الخاص بك، أو كليهما. مع هذه الميزات، لديك خدمات الشبكات الاجتماعية والتطبيقات التي يمكن دمجها في تجربة الروبوت الشبكات الاجتماعية.

نص تيار الاجتماعي
ترتبط العناصر تيار دائما مع جهة اتصال الخام. وandroid.provider.ContactsContract.StreamItemsColumns # RAW_CONTACT_ID يربط إلى _IDقيمة للاتصال الخام. يتم تخزين نوع الحساب وحساب اسم جهة الاتصال الخام أيضا في الصف البند تيار.

تخزين البيانات من تيار الخاصة بك في الأعمدة التالية:

android.provider.ContactsContract.StreamItemsColumns # ACCOUNT_TYPE
مطلوب. نوع حساب المستخدم للاتصال الخام لها صلة بهذا العنصر تيار. تذكر أن تعيين هذه القيمة عند إدراج عنصر تيار.
android.provider.ContactsContract.StreamItemsColumns # ACCOUNT_NAME
مطلوب. اسم حساب المستخدم للاتصال الخام لها صلة بهذا العنصر تيار. تذكر أن تعيين هذه القيمة عند إدراج عنصر تيار.
أعمدة معرف
مطلوب. يجب إدراج الأعمدة معرف التالية عند إدراج البند الدفق:
android.provider.ContactsContract.StreamItemsColumns # CONTACT_ID: قيمة # _ID android.provider.BaseColumns من جهة الاتصال التي يرتبط هذا البند مع تيار.
android.provider.ContactsContract.StreamItemsColumns # CONTACT_LOOKUP_KEY: قيمة LOOKUP_KEY android.provider.ContactsContract.ContactsColumns # للاتصال ويرتبط هذا البند مع تيار.
android.provider.ContactsContract.StreamItemsColumns # RAW_CONTACT_ID: قيمة # _ID android.provider.BaseColumns للاتصال الخام التي يرتبط هذا البند مع تيار.
android.provider.ContactsContract.StreamItemsColumns # تعليقات
اختياري. مخازن المعلومات الموجزة التي يمكنك عرض في بداية عنصر تيار.
android.provider.ContactsContract.StreamItemsColumns # TEXT
نص البند تيار، إما المحتوى الذي تم نشره من قبل مصدر هذا البند، أو وصفا لبعض الإجراءات التي ولدت هذا البند تيار. يمكن أن يحتوي هذا العمود أي تنسيق والصور الموارد المضمنة التي يمكن تقديمها من قبل fromHtml(). قد باقتطاع مزود أو ellipsize المحتوى طويلة، لكنها ستحاول تجنب علامات الانهيار.
android.provider.ContactsContract.StreamItemsColumns # الطابع الزمني
سلسلة نصية تحتوي على وقت تم إدراج هذا البند تيار أو تحديثها، في شكل من ميلي ثانية منذ عصر. التطبيقات التي تضاف أو عناصر تيار التحديث هي المسؤولة عن الحفاظ على هذا العمود. لم يتم الحفاظ عليه تلقائيا من قبل مزود اتصالات.
لعرض معلومات تعريفية للعناصر تيار، استخدم android.provider.ContactsContract.StreamItemsColumns # RES_ICON، android.provider.ContactsContract.StreamItemsColumns # RES_LABEL، وandroid.provider.ContactsContract.StreamItemsColumns # RES_PACKAGE لربط الموارد في التطبيق الخاص بك.

يحتوي الجدول android.provider.ContactsContract.StreamItems أيضا الأعمدة android.provider.ContactsContract.StreamItemsColumns # SYNC1 من خلال android.provider.ContactsContract.StreamItemsColumns # SYNC4 للاستخدام الحصري من محولات متزامنة.

صور تيار الاجتماعي
الصور android.provider.ContactsContract.StreamItemPhotos الجدول مخازن المرتبطة عنصر تيار. android.provider.ContactsContract.StreamItemPhotosColumns # STREAM_ITEM_ID الروابط عمود الجدول على القيم في _IDعمود من الجدول android.provider.ContactsContract.StreamItems. يتم تخزين الصورة الإشارات في الجدول في هذه الأعمدة:

android.provider.ContactsContract.StreamItemPhotos العمود # صورة (أ BLOB).
تمثيل ثنائي من الصورة، تغيير حجمها من قبل مزود للتخزين والعرض. يتوفر التوافق مع الإصدارات السابقة من مزود اتصالات التي تستخدم لتخزين الصور هذا العمود. ومع ذلك، في الإصدار الحالي يجب أن لا تستخدم هذا العمود لتخزين الصور. بدلا من ذلك، استخدم إما android.provider.ContactsContract.StreamItemPhotosColumns # PHOTO_FILE_ID أو android.provider.ContactsContract.StreamItemPhotosColumns # PHOTO_URI (وكلاهما موضح في النقاط التالية) لتخزين الصور في ملف. يتضمن هذا العمود الآن صورة مصغرة من الصورة، وهو متاح للقراءة.
android.provider.ContactsContract.StreamItemPhotosColumns # PHOTO_FILE_ID
معرف رقمي من صورة لجهة اتصال الخام. إلحاق هذه القيمة إلى ثابت DisplayPhoto.CONTENT_URIللحصول على محتوى URI لافتا إلى ملف صورة واحدة، ومن ثم استدعاء openAssetFileDescriptor()للحصول على مؤشر إلى ملف الصورة.
android.provider.ContactsContract.StreamItemPhotosColumns # PHOTO_URI
ومحتوى URI تشير مباشرة إلى ملف صورة للممثلة هذا الصف الصورة. اتصل openAssetFileDescriptor()مع هذا URI للحصول على مؤشر إلى ملف الصورة.
باستخدام الجداول تيار الاجتماعي
هذه الجداول تعمل نفس الجداول الرئيسية الأخرى في موفر اتصالات، باستثناء ما يلي:

هذه الجداول تتطلب أذونات الوصول إضافية. لقراءة منها، يجب أن يكون التطبيق الخاص بك إذن android.Manifest.permission # READ_SOCIAL_STREAM. لتعديلها، يجب أن يكون التطبيق الخاص بك إذن android.Manifest.permission # WRITE_SOCIAL_STREAM.
للجدول android.provider.ContactsContract.StreamItems، عدد الصفوف المخزنة لكل جهة اتصال الخام محدودة. وبمجرد الوصول إلى هذا الحد، واتصالات مزود يجعل مساحة للصفوف الجديدة البند تيار عن طريق حذف الصفوف وجود أقدم android.provider.ContactsContract.StreamItemsColumns # الطابع الزمني تلقائيا. للحصول على الحد، إصدار استعلام إلى محتوى URI android.provider.ContactsContract.StreamItems # CONTENT_LIMIT_URI. يمكنك ترك كل الحجج الأخرى من محتوى المقرر URI ل null. إرجاع الاستعلام المؤشر تحتوي على صف واحد، مع عمود واحد android.provider.ContactsContract.StreamItems # MAX_ITEMS.
الطبقة android.provider.ContactsContract.StreamItems.StreamItemPhotos يحدد جدول فرعي من android.provider.ContactsContract.StreamItemPhotos تحتوي على الصفوف الصورة لعنصر تيار واحد.

التفاعلات تيار الاجتماعي
البيانات تيار الاجتماعية التي تديرها مزود اتصالات، بالتزامن مع تطبيق جهات الاتصال للجهاز، ويوفر وسيلة قوية لربط نظام الشبكات الاجتماعية الخاصة بك مع جهات الاتصال الموجودة. تتوفر الميزات التالية:

بواسطة المزامنة الخاص بك خدمة الشبكات الاجتماعية إلى مزود اتصالات مع محول المزامنة، يمكنك استرداد النشاط الأخير لجهات اتصال المستخدم وتخزينها في android.provider.ContactsContract.StreamItems وandroid.provider.ContactsContract.StreamItemPhotos الجداول لاستخدامها لاحقا.
وبالاضافة الى التزامن منتظم، يمكنك تحريك محول المزامنة لاسترداد بيانات إضافية عندما يقوم المستخدم بتحديد اتصال لعرضها. وهذا يسمح محول المزامنة لاسترداد صور عالية الدقة وعناصر تيار أحدث لجهة الاتصال.
من خلال تسجيل إشعار مع تطبيق جهات الاتصال للجهاز ومزود اتصالات، يمكنك الحصول على وجود نية عند عرض جهة اتصال، وعند هذه النقطة تحديث مركز الاتصال من الخدمة الخاص بك. قد يكون هذا النهج بشكل أسرع واستخدام عرض النطاق الترددي أقل من القيام مزامنة كاملة مع محول المزامنة.
يمكن للمستخدمين إضافة جهة اتصال إلى خدمة الشبكات الاجتماعية الخاصة بك في حين يبحث في اتصال في تطبيق جهات الاتصال للجهاز. تمكين هذا مع "دعوة الاتصال" ميزة، الذي تمكن مع مزيج من النشاط الذي يضيف جهة اتصال موجودة إلى الشبكة الخاصة بك، وملف XML الذي يوفر تطبيق جهات الاتصال للجهاز واتصالات مزود بتفاصيل طلبك.
تزامن المنتظم للبنود تيار مع موفر اتصالات هو نفس تزامن الأخرى. لمعرفة المزيد حول المزامنة، راجع قسم اتصالات مزود مزامنة محولات . وتغطي تسجيل الإخطارات ودعوة الاتصالات في القسمين المقبلة.

تسجيل للتعامل مع وجهات النظر الشبكات الاجتماعية
لتسجيل محول المزامنة لتلقي الإخطارات عندما يشاهد المستخدم الاتصال التي تدار من قبل محول المزامنة الخاص بك:

قم بإنشاء ملف يسمى contacts.xmlفي مشروعك res/xml/الدليل. إذا كان لديك بالفعل هذا الملف، يمكنك تخطي هذه الخطوة.
في هذا الملف، إضافة العنصر <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. إذا كان هذا العنصر موجود بالفعل، يمكنك تخطي هذه الخطوة.
تسجيل الخدمة التي يتم إخطار عندما يفتح المستخدم صفحة تفاصيل جهة الاتصال في تطبيق جهات الاتصال للجهاز، إضافة السمة إلى العنصر، حيث هو اسم_الفئة مؤهل بشكل كامل من الخدمة التي يجب أن تتلقى القصد من تطبيق جهات الاتصال للجهاز. لخدمة المخطر، استخدم فئة التي تمتد لإتاحة الخدمة لتلقي النوايا. يحتوي على البيانات في النية واردة محتوى URI للاتصال الخام قام المستخدم بالنقر فوق. من خدمة المخطر، يمكنك ربط وثم استدعاء محول المزامنة لتحديث البيانات للاتصال الخام.viewContactNotifyService=" serviceclass "serviceclassIntentService
لتسجيل النشاط ليتم استدعاؤها عندما ينقر المستخدم على عنصر تيار أو صورة أو كليهما:

قم بإنشاء ملف يسمى contacts.xmlفي مشروعك res/xml/الدليل. إذا كان لديك بالفعل هذا الملف، يمكنك تخطي هذه الخطوة.
في هذا الملف، إضافة العنصر <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. إذا كان هذا العنصر موجود بالفعل، يمكنك تخطي هذه الخطوة.
لتسجيل واحدة من الأنشطة الخاصة بك على التعامل مع المستخدم النقر على عنصر تيار في تطبيق جهات الاتصال للجهاز، إضافة السمة إلى العنصر، حيث هو اسم_الفئة مؤهل بشكل كامل من النشاط الذي ينبغي أن تحصل على نوايا من تطبيق جهات الاتصال للجهاز.viewStreamItemActivity=" activityclass "activityclass
لتسجيل واحدة من الأنشطة الخاصة بك على التعامل مع المستخدم النقر على الصورة تيار في تطبيق جهات الاتصال للجهاز، إضافة السمة إلى العنصر، حيث هو اسم_الفئة مؤهل بشكل كامل من النشاط الذي ينبغي أن تحصل على نوايا من تطبيق جهات الاتصال للجهاز.viewStreamItemPhotoActivity=" activityclass "activityclass
و <ContactsAccountType>يتم وصف العنصر بمزيد من التفصيل في القسم العنصر <ContactsAccountType> .

يحتوي على نية الواردة محتوى URI في هذا البند أو الصورة التي قام المستخدم بالنقر فوق. لديك أنشطة منفصلة عن العناصر النصية والصور واستخدام كل الخصائص في نفس الملف.

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

قم بإنشاء ملف يسمى contacts.xmlفي مشروعك res/xml/الدليل. إذا كان لديك بالفعل هذا الملف، يمكنك تخطي هذه الخطوة.
في هذا الملف، إضافة العنصر <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. إذا كان هذا العنصر موجود بالفعل، يمكنك تخطي هذه الخطوة.
إضافة السمات التالية:
inviteContactActivity=" activityclass "
inviteContactActionLabel="@string/ invite_action_label "
و activityclassالقيمة هي اسم_الفئة مؤهل بشكل كامل من النشاط الذي ينبغي أن تحصل على القصد. و invite_action_labelالقيمة هي سلسلة نصية الذي يتم عرضه في اتصال إضافة القائمة في تطبيق جهات الاتصال للجهاز.
ملاحظة: ContactsSource هو إهمال اسم شعارا ل ContactsAccountType.

إشارة contacts.xml
ملف contacts.xmlيحتوي على عناصر XML التي تتحكم في تفاعل محول المزامنة وتطبيقها مع تطبيق جهات الاتصال ومزود اتصالات. يتم وصف هذه العناصر في الأقسام التالية.

<ContactsAccountType> العنصر
و <ContactsAccountType>يتحكم عنصر تفاعل التطبيق الخاص بك مع تطبيق جهات الاتصال. لديها بناء الجملة التالي:


<ContactsAccountType
        xmlns:android="http://schemas.android.com/apk/res/android"
        inviteContactActivity="activity_name"
        inviteContactActionLabel="invite_command_text"
        viewContactNotifyService="view_notify_service"
        viewGroupActivity="group_view_activity"
        viewGroupActionLabel="group_action_text"
        viewStreamItemActivity="viewstream_activity_name"
        viewStreamItemPhotoActivity="viewphotostream_activity_name">


ترد في:

res/xml/contacts.xml

يمكن أن تحتوي على:

<ContactsDataKind>

وصف:

تعلن مكونات الروبوت والعلامات UI التي تسمح للمستخدمين لدعوة إحدى جهات الاتصال الخاصة بهم إلى الشبكة الاجتماعية، وإخطار المستخدمين عند واحد من تيارات الشبكات الاجتماعية يتم تحديثها، وهكذا دواليك.

لاحظ أن البادئة السمة android:ليست ضرورية لصفات<ContactsAccountType> .

الصفات:

inviteContactActivity
اسم الفئة مؤهل بشكل كامل من النشاط في التطبيق الخاص بك الذي تريد تنشط عندما يحدد المستخدم إضافة اتصال من تطبيق جهات الاتصال للجهاز.
inviteContactActionLabel
سلسلة النص الذي يتم عرضه للنشاط المحددة في inviteContactActivity، في إضافة اتصال القائمة. على سبيل المثال، يمكنك استخدام السلسلة "اتبع في شبكة الاتصال". يمكنك استخدام معرف مورد سلسلة لهذه التسمية.
viewContactNotifyService
اسم الفئة مؤهل بشكل كامل من خدمة في التطبيق الخاص بك التي يجب أن تتلقى إخطارات عندما يشاهد المستخدم للإتصال به. تم إرسال هذا الإشعار تطبيق جهات الاتصال للجهاز. فإنه يسمح التطبيق الخاص بك لتأجيل عمليات كثيفة البيانات حتى تكون الحاجة إليها. على سبيل المثال، يمكن أن طلبك لرد على هذا الإعلام من خلال قراءة في وعرض عالية الدقة صورة جهة الاتصال وعناصر تيار اجتماعي الأخيرة. يتم وصف هذه الميزة في المزيد من التفاصيل في القسم التفاعلات تيار الاجتماعي . يمكنك ان ترى مثالا على خدمة الإعلام في NotifierService.javaالملف في SampleSyncAdapter عينة التطبيق.
viewGroupActivity
اسم الفئة مؤهل بشكل كامل من النشاط في التطبيق الذي يمكن عرض معلومات عن المجموعة. عندما يقوم المستخدم بالنقر فوق تسمية مجموعة في تطبيق جهات الاتصال في الجهاز، يتم عرض واجهة المستخدم لهذا النشاط.
viewGroupActionLabel
التسمية التي يعرض تطبيق جهات الاتصال لعنصر تحكم UI التي تسمح للمستخدم لإلقاء نظرة على مجموعة في التطبيق الخاص بك.
على سبيل المثال، إذا قمت بتثبيت تطبيق + Google على جهازك ومزامنة في + Google مع تطبيق جهات الاتصال، سترى دوائر + Google الموضحة كمجموعات في الاتصالات التطبيق الخاص بك المجموعات التبويب. إذا قمت بالنقر على دائرة على + Google، سترى الناس في هذه الدائرة على النحو الوارد "مجموعة". في الجزء العلوي من الشاشة، سترى رمز في + Google. إذا قمت بالنقر فوق ذلك، ومراقبة مفاتيح لتطبيق + Google. تطبيق جهات الاتصال يفعل ذلك مع viewGroupActivity، وذلك باستخدام رمز Google+ حيث بلغت قيمةviewGroupActionLabel .

ويسمح للمعرف مورد سلسلة لهذه السمة.

viewStreamItemActivity
اسم الفئة مؤهل بشكل كامل من النشاط في التطبيق الخاص بك أن تطلق تطبيق جهات الاتصال في الجهاز عندما يقوم المستخدم بالنقر فوق عنصر تيار لجهة اتصال الخام.
viewStreamItemPhotoActivity
اسم الفئة مؤهل بشكل كامل من النشاط في التطبيق الخاص بك أن تطلق تطبيق جهات الاتصال في الجهاز عندما يقوم المستخدم بالنقر على الصورة في البند تيار لجهة اتصال الخام.
<ContactsDataKind> العنصر
و <ContactsDataKind>يتحكم عنصر عرض صفوف البيانات المخصصة التطبيق الخاص بك في واجهة المستخدم للتطبيق جهات الاتصال ل. لديها بناء الجملة التالي:
<ContactsDataKind
        android:mimeType="MIMEtype"
        android:icon="icon_resources"
        android:summaryColumn="column_name"
        android:detailColumn="column_name">


ترد في:

<ContactsAccountType>
وصف:

استخدام هذا العنصر أن يكون تطبيق جهات الاتصال عرض محتويات صف بيانات مخصصة كجزء من تفاصيل الاسم الخام. كل <ContactsDataKind>عنصر تابع ل <ContactsAccountType>يمثل نوع من صف البيانات المخصصة التي محول المزامنة الخاصة بك يضيف إلى ContactsContract.Dataالجدول. إضافة واحد <ContactsDataKind>عنصر لكل نوع MIME المخصصة التي تستخدمها. لم يكن لديك لإضافة العنصر إذا كان لديك صف البيانات المخصصة التي كنت لا ترغب في عرض البيانات.

الصفات:

android:mimeType
نوع MIME المخصصة التي حددتها لأحد أنواع صف البيانات المخصصة في ContactsContract.Dataالجدول. على سبيل المثال، قيمة vnd.android.cursor.item/vnd.example.locationstatusيمكن أن يكون نوع MIME مخصص لصف البيانات التي تسجل آخر موقع معروف لجهة الاتصال.
android:icon
والروبوت الموارد drawable أن يعرض تطبيق جهات الاتصال بجانب البيانات الخاصة بك. استخدام هذا للإشارة إلى المستخدم الذي يأتي البيانات من الخدمة الخاص بك.
android:summaryColumn
اسم العمود الأول من قيمتين استردادها من الصف البيانات. يتم عرض القيمة كما في السطر الأول من دخول هذا الصف البيانات. والمقصود السطر الأول لاستخدامها ملخصا للبيانات، ولكن هذا هو اختياري. انظر أيضا الروبوت: detailColumn .
android:detailColumn
اسم العمود الثاني من قيمتين استردادها من الصف البيانات. يتم عرض القيمة كما في السطر الثاني من دخول هذا الصف البيانات. انظر أيضا android:summaryColumn.
إضافية اتصالات مزود الميزات
بالإضافة إلى الميزات الرئيسية وصفها في الأجزاء السابقة، واتصالات مزود يقدم هذه الخصائص المفيدة للعمل مع بيانات الأسماء:

مجموعة الاتصال
ملامح الصورة
مجموعة الاتصال
جهات الاتصال مزود يمكن اختياريا مجموعات التسمية من الاتصالات ذات الصلة مع مجموعة البيانات. إذا كان الملقم المرتبطة حساب المستخدم يريد الحفاظ على مجموعات، يجب محول المزامنة لنوع الحساب الحساب نقل البيانات جماعات بين مزود اتصالات والخادم. عندما يقوم المستخدم بإضافة اسم جديد إلى الملقم ثم وضع هذا الاتصال في مجموعة جديدة، يجب على محول المزامنة إضافة مجموعة جديدة إلى ContactsContract.Groupsالجدول. المجموعة أو المجموعات ينتمي إليها الاسم الخام ليتم تخزينها في ContactsContract.Dataالجدول، وذلك باستخدامContactsContract.CommonDataKinds.GroupMembership نوع MIME.

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

الصور الاتصال
و ContactsContract.Dataالجدول بتخزين الصور والصفوف مع نوع MIME Photo.CONTENT_ITEM_TYPE. الصف و CONTACT_IDيرتبط عمود إلى _IDعمود من الاتصال الخام التي ينتمي إليها. الطبقة ContactsContract.Contacts.Photoتحدد جدولا فرعي من ContactsContract.Contactsتحتوي على معلومات الصورة للصور الأولية لجهة الاتصال، والتي هي الصورة الأولية للاتصال الخام الأولية والاتصال. وبالمثل، فإن الطبقة ContactsContract.RawContacts.DisplayPhotoتحدد جدولا فرعي من ContactsContract.RawContactsتحتوي على معلومات الصورة للصور الأولية اتصال الخام في.

وثائق مرجعية ل ContactsContract.Contacts.Photoو ContactsContract.RawContacts.DisplayPhotoتتضمن الأمثلة على استرجاع المعلومات الصورة. لا توجد أية فئة من الراحة لاسترجاع الصورة المصغرة الأولية لجهة اتصال الخام، ولكن يمكنك إرسال الاستعلام إلى ContactsContract.Dataالجدول، واختيار على جهة الاتصال الخام _ID، و Photo.CONTENT_ITEM_TYPE، و IS_PRIMARYعمود للعثور على التوالي صورة الأساسي للاتصال الخام في.

لشخص قد تتضمن بيانات تيار الاجتماعية أيضا الصور. يتم تخزين هذه في الجدول android.provider.ContactsContract.StreamItemPhotos، الذي يوصف في مزيد من التفاصيل في قسم الصور تيار الاجتماعي .

Open Files using Storage Access Framework

افتح الملفات باستخدام فريميورك أسيس فريميورك
في هذه الوثيقة تظهر أكثر
نظرة عامة
تدفق التحكم
كتابة تطبيق عميل
الطبقات الرئيسية
DocumentsProvider
DocumentsContract
أشرطة فيديو
ديفبيتس: أندرويد 4.4 إطار الوصول إلى التخزين: المزود
ديفبيتس: الروبوت 4.4 إطار الوصول التخزين: العميل
الملفات الظاهرية في إطار الوصول التخزين
عينات التعليمات البرمجية
StorageProvider
StorageClient
أنظر أيضا
إنشاء موفر مستندات مخصص
أساسيات موفر المحتوى
يقدم أندرويد 4.4 (مستوى أبي 19) إطار الوصول إلى التخزين (ساف). وتسهل القوات المسلحة السودانية على المستخدمين تصفح المستندات والصور والملفات الأخرى وفتحها عبر جميع مزودي خدمات تخزين المستندات المفضلين لديهم. تتيح واجهة مستخدم قياسية وسهلة الاستخدام للمستخدمين تصفح الملفات والوصول إلى الرسائل الحديثة بطريقة متسقة عبر التطبيقات ومقدمي الخدمات.

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

وتشمل القوات المسلحة السودانية ما يلي:

موفر المستندات -A مزود المحتوى الذي يسمح لخدمة التخزين (مثل غوغل دريف) للكشف عن الملفات التي تديرها. يتم تنفيذ موفر الوثيقة كفئة فرعية من فئة دوكومنتسبروفيدر. يستند مخطط موفر المستند إلى التسلسل الهرمي للملف التقليدي، على الرغم من أن موفر المستند يقوم بتخزين البيانات فعليا متروك لك. تتضمن منصة أندرويد العديد من مزودي المستندات المضمنة، مثل التنزيلات والصور ومقاطع الفيديو.
تطبيق العميل -A التطبيق المخصص الذي يستدعي ACTION_OPEN_DOCUMENT و / أو ACTION_CREATE_DOCUMENT ويتلقى الملفات التي تم إرجاعها من قبل موفري الوثائق.
المنتقى -A واجهة مستخدم النظام التي تمكن المستخدمين من الوصول إلى المستندات من جميع موفري المستندات التي تلبي معايير بحث تطبيق العميل.
وفيما يلي بعض السمات التي تقدمها القوات المسلحة السودانية:

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

نموذج البيانات

الشكل 1. نموذج بيانات مزود الوثائق. نقطة الجذر إلى وثيقة واحدة، والتي تبدأ بعد ذلك مروحة من الشجرة بأكملها.

لاحظ ما يلي:



لاحظ ما يلي:

یقدم کل مقدم وثائق تقریر "جذور" أو أکثر، وھو ما یعد نقطة انطلاق في استکشاف شجرة الوثائق. يحتوي كل جذر على COLUMN_ROOT_ID فريدة، ويشير إلى مستند (دليل) يمثل محتويات تحت هذا الجذر. جذور ديناميكية من قبل تصميم لدعم حالات الاستخدام مثل حسابات متعددة، عابرة أجهزة تخزين أوسب، أو تسجيل دخول المستخدم / الخروج.
تحت كل جذر هو وثيقة واحدة. وتشير هذه الوثيقة إلى وثائق من 1 إلى N ، يمكن لكل منها أن تشير بدورها إلى وثائق من 1 إلى N.
تقوم كل COLUMN_DOCUMENT_ID الملفات والأدلة الفردية من خلال الرجوع إليها باستخدام COLUMN_DOCUMENT_ID فريدة. يجب أن تكون معرفات المستند فريدة ولا يتم تغييرها مرة واحدة، نظرا لأنها تستخدم لمنح أوري المستمرة عبر إعادة تشغيل الجهاز.
يمكن أن تكون المستندات إما ملف قابل للفتح (مع نوع مايم معين)، أو دليل يحتوي على مستندات إضافية (مع نوع مايم MIME_TYPE_DIR ).
يمكن أن يكون لكل مستند إمكانات مختلفة، كما هو موضح في COLUMN_FLAGS . على سبيل المثال، FLAG_SUPPORTS_WRITE و FLAG_SUPPORTS_DELETE و FLAG_SUPPORTS_THUMBNAIL . يمكن تضمين نفس COLUMN_DOCUMENT_ID في أدلة متعددة.
تدفق التحكم
وكما ذكر أعلاه، يستند نموذج بيانات موفر الوثائق إلى التسلسل الهرمي للملفات التقليدية. ومع ذلك، يمكنك تخزين البيانات الخاصة بك جسديا ولكن تريد، طالما يمكنك الوصول إليه باستخدام DocumentsProvider أبي. على سبيل المثال، يمكنك استخدام السعة التخزينية المستندة إلى العلامات لبياناتك.

ويبين الشكل 2 كيف يمكن لتطبيق صورة استخدام القوات المسلحة السودانية للوصول إلى البيانات المخزنة:



الشكل 2. تدفق إطار الوصول إلى التخزين

لاحظ ما يلي:

في القوات المسلحة السودانية، ومقدمي والعملاء لا تتفاعل مباشرة. يطلب العميل إذنا بالتفاعل مع الملفات (أي لقراءة أو تعديل أو إنشاء أو حذف الملفات).
يبدأ التفاعل عندما يؤدي تطبيق (في هذا المثال، تطبيق صور) إلى ACTION_OPEN_DOCUMENT نية ACTION_OPEN_DOCUMENT أو ACTION_CREATE_DOCUMENT . يمكن أن تتضمن النية فلاتر لتحسين المعايير - على سبيل المثال، "أعطني جميع الملفات القابلة للفتح التي تحتوي على" صورة "نوع مايم."
مرة واحدة الحرائق نية، منتقي النظام يذهب إلى كل مزود مسجل ويظهر للمستخدم جذور المحتوى مطابقة.
يوفر منتقي المستخدمين واجهة قياسية للوصول إلى الوثائق، على الرغم من أن مقدمي الوثائق الأساسية قد تكون مختلفة جدا. على سبيل المثال، يعرض الشكل 2 مزود غوغل دريف، ومزود خدمة أوسب، ومزود خدمة السحاب.
ويبين الشكل 3 منتقي قام المستخدم الذي يبحث عن الصور بتحديد حساب غوغل دريف. كما يظهر كل من الجذور المتاحة لتطبيق العميل.


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


الشكل 4. الصور

كتابة التطبيق العميل
في الإصدار 4.3 من نظام التشغيل أندرويد والإصدارات الأقدم، إذا كنت تريد أن يسترد تطبيقك ملفا من تطبيق آخر، فيجب عليه استدعاء نية مثل ACTION_PICK أو ACTION_GET_CONTENT . يجب على المستخدم ثم تحديد التطبيق واحد الذي لاختيار ملف والتطبيق المحدد يجب أن توفر واجهة المستخدم للمستخدم لتصفح واختيار من الملفات المتوفرة.

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

لا يقصد من ACTION_OPEN_DOCUMENT أن تكون بديلا عن ACTION_GET_CONTENT . تعتمد الطريقة التي يجب عليك استخدامها على احتياجات تطبيقك:

استخدم ACTION_GET_CONTENT إذا كنت تريد أن يقرأ تطبيقك ببساطة بيانات / استيرادها. مع هذا النهج، التطبيق يستورد نسخة من البيانات، مثل ملف صورة.
استخدم ACTION_OPEN_DOCUMENT إذا كنت تريد أن يكون لتطبيقك حق وصول طويل الأمد ومستمر إلى المستندات التي يملكها موفر المستندات. ومن الأمثلة على ذلك تطبيق لتحرير الصور يتيح للمستخدمين تعديل الصور المخزنة في موفر المستندات.
يصف هذا القسم كيفية كتابة تطبيقات العميل استنادا إلى ACTION_OPEN_DOCUMENT و ACTION_CREATE_DOCUMENT .

ابحث عن المستندات
يستخدم المقتطف التالي ACTION_OPEN_DOCUMENT للبحث عن موفري المستندات الذين ACTION_OPEN_DOCUMENT على ملفات الصور:
private static final int READ_REQUEST_CODE = 42;
...
/**
 * Fires an intent to spin up the "file chooser" UI and select an image.
 */
public void performFileSearch() {

    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
    // browser.
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

    // Filter to only show results that can be "opened", such as a
    // file (as opposed to a list of contacts or timezones)
    intent.addCategory(Intent.CATEGORY_OPENABLE);

    // Filter to show only images, using the image MIME data type.
    // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
    // To search for all documents available via installed storage providers,
    // it would be "*/*".
    intent.setType("image/*");

    startActivityForResult(intent, READ_REQUEST_CODE);
}

لاحظ ما يلي:

عندما يطلق التطبيق نية ACTION_OPEN_DOCUMENT ، فإنه يقوم بتشغيل منتقي يعرض جميع مزودي المستندات المطابقة.
يؤدي إضافة الفئة CATEGORY_OPENABLE إلى الهدف إلى تصفية النتائج لعرض المستندات التي يمكن فتحها فقط، مثل ملفات الصور.
يتم عرض intent.setType("image/*") لعرض المستندات التي تحتوي على نوع بيانات مايم فقط.
نتائج العملية
بعد أن يقوم المستخدم بتحديد مستند في منتقي، يتم onActivityResult() . تحتوي معلمة resultData على عنوان أوري الذي يشير إلى المستند المحدد. استخراج أوري باستخدام getData() . عندما يكون لديك، يمكنك استخدامه لاسترداد الوثيقة يريد المستخدم. فمثلا:
@Override
public void onActivityResult(int requestCode, int resultCode,
        Intent resultData) {

    // The ACTION_OPEN_DOCUMENT intent was sent with the request code
    // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
    // response to some other intent, and the code below shouldn't run at all.

    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        // The document selected by the user won't be returned in the intent.
        // Instead, a URI to that document will be contained in the return intent
        // provided to this method as a parameter.
        // Pull that URI using resultData.getData().
        Uri uri = null;
        if (resultData != null) {
            uri = resultData.getData();
            Log.i(TAG, "Uri: " + uri.toString());
            showImage(uri);
        }
    }
}

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

public void dumpImageMetaData(Uri uri) {

    // The query, since it only applies to a single document, will only return
    // one row. There's no need to filter, sort, or select fields, since we want
    // all fields for one document.
    Cursor cursor = getActivity().getContentResolver()
            .query(uri, null, null, null, null, null);

    try {
    // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
    // "if there's anything to look at, look at it" conditionals.
        if (cursor != null && cursor.moveToFirst()) {

            // Note it's called "Display Name".  This is
            // provider-specific, and might not necessarily be the file name.
            String displayName = cursor.getString(
                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            Log.i(TAG, "Display Name: " + displayName);

            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
            // If the size is unknown, the value stored is null.  But since an
            // int can't be null in Java, the behavior is implementation-specific,
            // which is just a fancy term for "unpredictable".  So as
            // a rule, check if it's null before assigning to an int.  This will
            // happen often:  The storage API allows for remote files, whose
            // size might not be locally known.
            String size = null;
            if (!cursor.isNull(sizeIndex)) {
                // Technically the column stores an int, but cursor.getString()
                // will do the conversion automatically.
                size = cursor.getString(sizeIndex);
            } else {
                size = "Unknown";
            }
            Log.i(TAG, "Size: " + size);
        }
    } finally {
        cursor.close();
    }
}

افتح مستندا
بمجرد أن يكون لديك عنوان أوري للمستند، يمكنك فتحه أو القيام بأي شيء آخر تريد القيام به.

نقطية
في ما يلي مثال لكيفية فتح Bitmap :

private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    ParcelFileDescriptor parcelFileDescriptor =
            getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    parcelFileDescriptor.close();
    return image;
}

لاحظ أنه يجب عدم القيام بهذه العملية على مؤشر ترابط واجهة المستخدم. تفعل ذلك في الخلفية، وذلك باستخدام AsyncTask . بمجرد فتح الصورة النقطية، يمكنك عرضها في ImageView .

الحصول على إنبوتستريم
فيما يلي مثال لكيفية الحصول على InputStream من عنوان أوري. في هذا المقتطف، تتم قراءة أسطر الملف في سلسلة:

private String readTextFromUri(Uri uri) throws IOException {
    InputStream inputStream = getContentResolver().openInputStream(uri);
    BufferedReader reader = new BufferedReader(new InputStreamReader(
            inputStream));
    StringBuilder stringBuilder = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
        stringBuilder.append(line);
    }
    fileInputStream.close();
    parcelFileDescriptor.close();
    return stringBuilder.toString();
}
 
إنشاء مستند
يمكن لتطبيقك استخدام القصد ACTION_CREATE_DOCUMENT لإنشاء مستند جديد في موفر المستندات. لإنشاء ملف، اعط نية نوع مايم واسم ملف ثم قم بتشغيله برمز طلب فريد. يتم أخذ بقية الرعاية لك:

// Here are some examples of how you might call this method.
// The first parameter is the MIME type, and the second parameter is the name
// of the file you are creating:
//
// createFile("text/plain", "foobar.txt");
// createFile("image/png", "mypicture.png");

// Unique request code.
private static final int WRITE_REQUEST_CODE = 43;
...
private void createFile(String mimeType, String fileName) {
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);

    // Filter to only show results that can be "opened", such as
    // a file (as opposed to a list of contacts or timezones).
    intent.addCategory(Intent.CATEGORY_OPENABLE);

    // Create a file with the requested MIME type.
    intent.setType(mimeType);
    intent.putExtra(Intent.EXTRA_TITLE, fileName);
    startActivityForResult(intent, WRITE_REQUEST_CODE);
}

بعد إنشاء مستند جديد يمكنك الحصول على عنوان أوري الخاص به في onActivityResult() بحيث يمكنك الاستمرار في الكتابة إليه.

حذف مستند
إذا كان لديك عنوان أوري لمستند و document.COLUMN_FLAGS Document.COLUMN_FLAGS يحتوي على SUPPORTS_DELETE ، فيمكنك حذف المستند. فمثلا:

DocumentsContract.deleteDocument(getContentResolver(), uri);
تعديل مستند
يمكنك استخدام القوات المسلحة السودانية لتحرير وثيقة نصية في مكانها. ACTION_OPEN_DOCUMENT هذا المقتطف النية ACTION_OPEN_DOCUMENT ويستخدم الفئة CATEGORY_OPENABLE لعرض المستندات القابلة للفتح فقط. كما يعمل على تصفية الفلاتر لعرض الملفات النصية فقط:

private static final int EDIT_REQUEST_CODE = 44;
/**
 * Open a file for writing and append some text to it.
 */
 private void editDocument() {
    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
    // file browser.
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

    // Filter to only show results that can be "opened", such as a
    // file (as opposed to a list of contacts or timezones).
    intent.addCategory(Intent.CATEGORY_OPENABLE);

    // Filter to show only text files.
    intent.setType("text/plain");

    startActivityForResult(intent, EDIT_REQUEST_CODE);
}

بعد ذلك، من onActivityResult() (انظر نتائج العملية ) يمكنك استدعاء الكود لتنفيذ التعديل. يحصل المقتطف التالي على FileOutputStream من ContentResolver . افتراضيا يستخدم وضع الكتابة. من أفضل الممارسات أن تطلب أقل قدر من الوصول التي تحتاج إليها، لذلك لا تطلب القراءة / الكتابة إذا كان كل ما تحتاجه هو الكتابة:

private void alterDocument(Uri uri) {
    try {
        ParcelFileDescriptor pfd = getActivity().getContentResolver().
                openFileDescriptor(uri, "w");
        FileOutputStream fileOutputStream =
                new FileOutputStream(pfd.getFileDescriptor());
        fileOutputStream.write(("Overwritten by MyCloud at " +
                System.currentTimeMillis() + "\n").getBytes());
        // Let the document provider know you're done by closing the stream.
        fileOutputStream.close();
        pfd.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

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

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


final int takeFlags = intent.getFlags()
            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(uri, takeFlags);


هناك خطوة أخيرة. قد لا تكون أحدث عناوين ورل التي تم الوصول إليها لتطبيقك صالحة - فقد يكون تطبيق آخر قد حذف أو عدل مستندا. وبالتالي، يجب عليك دائما استدعاء getContentResolver().takePersistableUriPermission() للتحقق من البيانات الطازجة.

افتح الملفات الظاهرية
الروبوت 7.0 يضيف مفهوم الملفات الظاهرية إلى إطار الوصول التخزين. على الرغم من أن الملفات الظاهرية ليس لديها تمثيل ثنائي، يمكن تطبيق العميل الخاص بك فتح محتوياتها عن طريق إجبارهم على نوع ملف مختلف أو عن طريق عرض تلك الملفات باستخدام ACTION_VIEW القصد.

لفتح الملفات الظاهرية، يحتاج تطبيق العميل الخاص بك إلى تضمين منطق خاص للتعامل معها. إذا كنت ترغب في الحصول على تمثيل بايت من الملف لمعاينة الملف، على سبيل المثال، تحتاج إلى طلب نوع مايم بديل من موفر الوثائق.

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

هام : نظرا لأن التطبيق لا يمكنه فتح ملف ظاهري مباشرة باستخدام طريقة openInputStream() ، فإن تطبيقك لا يتلقى أية ملفات ظاهرية في حالة تضمين CATEGORY_OPENABLE في القصد ACTION_OPEN_DOCUMENT .
بعد قيام المستخدم onActivityResult() ، يقوم النظام باستدعاء الأسلوب onActivityResult() ، كما هو موضح سابقا في نتائج العملية . يمكن لتطبيقك استرداد معرف أوري للملف ثم تحديد ما إذا كان الملف ظاهريا باستخدام طريقة مشابهة لمقتطف الشفرة التالي.
private boolean isVirtualFile(Uri uri) {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false;
    }

    Cursor cursor = getContentResolver().query(
        uri,
        new String[] { DocumentsContract.Document.COLUMN_FLAGS },
        null, null, null);

    int flags = 0;
    if (cursor.moveToFirst()) {
        flags = cursor.getInt(0);
    }
    cursor.close();

    return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}


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

private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter)
    throws IOException {

    ContentResolver resolver = getContentResolver();

    String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);

    if (openableMimeTypes == null ||
        openableMimeTypes.length < 1) {
        throw new FileNotFoundException();
    }

    return resolver
        .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
        .createInputStream();

للحصول على مزيد من المعلومات حول الملفات الظاهرية وكيفية التعامل معها في التطبيق عميل التخزين الوصول إلى الإطار الخاص بك، شاهد الفيديو الملفات الظاهرية في إطار الوصول التخزين .

إنشاء موفر مستندات مخصص
في هذه الوثيقة تظهر أكثر
قائمة
انكماش
إنشاء فئة فرعية من دوكومينتسبروفيدر
الأمان
الطبقات الرئيسية
DocumentsProvider
DocumentsContract
أشرطة فيديو
ديفبيتس: أندرويد 4.4 إطار الوصول إلى التخزين: المزود
إطار الوصول إلى التخزين: إنشاء دوكومينتسبروفيدر
الملفات الظاهرية في إطار الوصول التخزين
عينات التعليمات البرمجية
مزود التخزين
StorageClient
أنظر أيضا
إنشاء دوكومينتسبروفيدر
افتح الملفات باستخدام فريميورك أسيس فريميورك
أساسيات موفر المحتوى
إذا كنت بصدد تطوير تطبيق يوفر خدمات تخزين للملفات (مثل خدمة حفظ السحاب)، فيمكنك جعل ملفاتك متاحة من خلال إطار وصول التخزين (ساف) عن طريق كتابة موفر مستندات مخصص. تصف هذه الصفحة كيفية إنشاء موفر مستندات مخصص.

لمزيد من المعلومات حول كيفية عمل "إطار الوصول إلى التخزين"، راجع نظرة عامة على "إطار الوصول التخزين" .

قائمة
لتنفيذ موفر مستندات مخصص، قم بإضافة ما يلي إلى بيان التطبيق الخاص بك:

الهدف من مستوى أبي 19 أو أعلى.
عنصر <provider> يعلن موفر التخزين المخصص.
السمة android:name تعيين android:name إلى اسم الفئة دوكومنتسبروفيدر الفرعية، وهو اسم الفئة، بما في ذلك اسم الحزمة:
com.example.android.storageprovider.MyCloudProvider .
السمة android:authority com.example.android.storageprovider سمة، وهو اسم الحزمة (في هذا المثال، com.example.android.storageprovider ) بالإضافة إلى نوع موفر المحتوى ( documents ).
السمة android:exported مجموعة إلى "true" . يجب تصدير مزود الخاص بك بحيث تطبيقات أخرى يمكن أن نرى ذلك.
السمة android:grantUriPermissions تعيين إلى "true" . يسمح هذا الإعداد للنظام بمنح تطبيقات أخرى إمكانية الدخول إلى المحتوى في مزود الخدمة. للحصول على مناقشة حول كيفية الاستمرار في منح وثيقة معينة، راجع الأذونات بيرسيست .
الإذن MANAGE_DOCUMENTS . افتراضيا مزود متاح للجميع. تؤدي إضافة هذا الإذن إلى تقييد مزود الخدمة بالنظام. هذا التقييد مهم للأمن.
مرشح نية يتضمن إجراء android.content.action.DOCUMENTS_PROVIDER ، بحيث يظهر مقدم الخدمة في منتقي عندما يبحث النظام لموفري الخدمة.
في ما يلي مقتطفات من بيان نموذجي يتضمن مقدم خدمة:

<manifest... >
    ...
    <uses-sdk
        android:minSdkVersion="19"
        android:targetSdkVersion="19" />
        ....
        <provider
            android:name="com.example.android.storageprovider.MyCloudProvider"
            android:authorities="com.example.android.storageprovider.documents"
            android:grantUriPermissions="true"
            android:exported="true"
            android:permission="android.permission.MANAGE_DOCUMENTS">
            <intent-filter>
                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
            </intent-filter>
        </provider>
    </application>

</manifest>

دعم الأجهزة التي تعمل بنظام التشغيل أندرويد 4.3 وأقل
لا ACTION_OPEN_DOCUMENT القصد من ACTION_OPEN_DOCUMENT إلا على الأجهزة التي تعمل بالإصدار 4.4 من نظام التشغيل أندرويد والإصدارات الأحدث. إذا كنت تريد أن يدعم التطبيق ACTION_GET_CONTENT لاستيعاب الأجهزة التي تعمل بنظام التشغيل أندرويد 4.3 والإصدارات ACTION_GET_CONTENT ، فيجب تعطيل فلتر القصد ACTION_GET_CONTENT في البيان الخاص بالأجهزة التي تعمل بالإصدار 4.4 من نظام التشغيل أندرويد أو ACTION_GET_CONTENT الأحدث. يجب اعتبار موفر المستندات و ACTION_GET_CONTENT . إذا كنت تدعم كلا منهما في وقت واحد، يظهر التطبيق مرتين في واجهة مستخدم منتقي النظام، ويقدم طريقتين مختلفتين للوصول إلى البيانات المخزنة. هذا أمر مربك للمستخدمين.

إليك الطريقة الموصى بها لتعطيل فلتر القصد ACTION_GET_CONTENT للأجهزة التي تعمل بنظام التشغيل أندرويد الإصدار 4.4 أو الإصدارات الأحدث:

في ملف الموارد bool.xml تحت res/values/ bool.xml res/values/ ، أضف هذا السطر:


In your bool.xml resources file under res/values/, add this line:

<bool name="atMostJellyBeanMR2">true</bool>
في ملف الموارد bool.xml تحت res/values-v19/ bool.xml res/values-v19/ ، أضف هذا السطر:
In your bool.xml resources file under res/values-v19/, add this line:
<bool name="atMostJellyBeanMR2">false</bool>
أضف اسما مستعارا للنشاط لتعطيل فلتر القصد ACTION_GET_CONTENT للإصدارات 4.4 (مستوى واجهة برمجة التطبيقات 19) وأعلى. فمثلا:

Add an activity alias to disable the ACTION_GET_CONTENT intent filter for versions 4.4 (API level 19) and higher. For example:
<!-- This activity alias is added so that GET_CONTENT intent-filter
     can be disabled for builds on API level 19 and higher. -->
<activity-alias android:name="com.android.example.app.MyPicker"
        android:targetActivity="com.android.example.app.MyActivity"
        ...
        android:enabled="@bool/atMostJellyBeanMR2">
    <intent-filter>
        <action android:name="android.intent.action.GET_CONTENT" />
        <category android:name="android.intent.category.OPENABLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
        <data android:mimeType="video/*" />
    </intent-filter>
</activity-alias>
Contracts
انكماش
عادة عند كتابة موفر محتوى مخصص، فإن إحدى المهام هي تنفيذ فئات العقود، كما هو موضح في دليل مطوري موفري المحتوى . فئة العقد هي فئة public final تحتوي على تعريفات ثابتة لعناوين ورل وأسماء الأعمدة وأنواع مايم والبيانات الوصفية الأخرى المتعلقة بالموفر. توفر القوات المسلحة السودانية هذه الطبقات العقد بالنسبة لك، لذلك لا تحتاج إلى كتابة بنفسك:

DocumentsContract.Document
DocumentsContract.Root
على سبيل المثال، في ما يلي الأعمدة التي قد ترجع في المؤشر عند الاستعلام عن موفر المستند للمستندات أو الجذر:

private static final String[] DEFAULT_ROOT_PROJECTION =
        new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES,
        Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
        Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
        Root.COLUMN_AVAILABLE_BYTES,};
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new
        String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
        Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
        Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};

يجب أن يتضمن مؤشر الجذر بعض الأعمدة المطلوبة. هذه الأعمدة هي:

COLUMN_ROOT_ID
COLUMN_ICON
COLUMN_TITLE
COLUMN_FLAGS
COLUMN_DOCUMENT_ID
يجب أن يتضمن مؤشر المستندات الأعمدة المطلوبة التالية:

COLUMN_DOCUMENT_ID
COLUMN_DISPLAY_NAME
COLUMN_MIME_TYPE
COLUMN_FLAGS
COLUMN_SIZE
COLUMN_LAST_MODIFIED
إنشاء فئة فرعية من دوكومينتسبروفيدر
الخطوة التالية في كتابة موفر مستندات مخصص هو الفئة الفرعية فئة دوكومنتسبروفيدر مجردة. في الحد الأدنى، يجب تنفيذ الطرق التالية:

queryRoots()
queryChildDocuments()
queryDocument()
openDocument()
هذه هي الأساليب الوحيدة التي تحتاجها بدقة لتنفيذ، ولكن هناك العديد من أكثر قد ترغب في. راجع DocumentsProvider للحصول على التفاصيل.

تحديد الجذر
يحتاج تطبيق queryRoots() إلى إرجاع مؤشر يشير إلى كافة الدلائل الجذر لموفر المستند باستخدام الأعمدة المعرفة في DocumentsContract.Root .

في المقتطف التالي، تمثل معلمة projection الحقول المحددة التي يريد المتصل العودة إليها. ينشئ المقتطف مؤشر جديد ويضيف صف واحد إلى جذر واحد، وهو دليل المستوى الأعلى، مثل التنزيلات أو الصور. معظم مقدمي الخدمات لديهم جذر واحد فقط. قد يكون لديك أكثر من حساب واحد، على سبيل المثال، في حالة حسابات المستخدمين المتعددة. في هذه الحالة، ما عليك سوى إضافة صف ثان إلى المؤشر.
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {

    // Use a MatrixCursor to build a cursor
    // with either the requested fields, or the default
    // projection if "projection" is null.
    final MatrixCursor result =
            new MatrixCursor(resolveRootProjection(projection));

    // If user is not logged in, return an empty root cursor.  This removes our
    // provider from the list entirely.
    if (!isUserLoggedIn()) {
        return result;
    }

    // It's possible to have multiple roots (e.g. for multiple accounts in the
    // same app) -- just add multiple cursor rows.
    final MatrixCursor.RowBuilder row = result.newRow();
    row.add(Root.COLUMN_ROOT_ID, ROOT);

    // You can provide an optional summary, which helps distinguish roots
    // with the same title. You can also use this field for displaying an
    // user account name.
    row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));

    // FLAG_SUPPORTS_CREATE means at least one directory under the root supports
    // creating documents. FLAG_SUPPORTS_RECENTS means your application's most
    // recently used documents will show up in the &quot;Recents&quot; category.
    // FLAG_SUPPORTS_SEARCH allows users to search all documents the application
    // shares.
    row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
            Root.FLAG_SUPPORTS_RECENTS |
            Root.FLAG_SUPPORTS_SEARCH);

    // COLUMN_TITLE is the root title (e.g. Gallery, Drive).
    row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title));

    // This document id cannot change after it's shared.
    row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));

    // The child MIME types are used to filter the roots and only present to the
    // user those roots that contain the desired type somewhere in their file hierarchy.
    row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
    row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
    row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);

    return result;
}

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

Uri rootsUri = DocumentsContract.buildRootsUri(BuildConfig.DOCUMENTS_AUTHORITY);
context.getContentResolver().notifyChange(rootsUri, null);

قائمة الوثائق في الموفر
يجب أن يقوم تنفيذ queryChildDocuments() بإرجاع مؤشر يشير إلى كافة الملفات في الدليل المحدد باستخدام الأعمدة المعرفة في DocumentsContract.Document .

يتم استدعاء هذا الأسلوب عندما يختار المستخدم الجذر الخاص بك في واجهة المستخدم منتقي. تسترد الطريقة الأطفال من معرف المستند المحدد بواسطة COLUMN_DOCUMENT_ID . ثم يقوم النظام باستدعاء هذه الطريقة في أي وقت يقوم المستخدم بتحديد دليل فرعي داخل موفر المستندات.

هذا المقتطف يجعل مؤشر جديد مع الأعمدة المطلوبة، ثم يضيف معلومات حول كل طفل فوري في الدليل الرئيسي إلى المؤشر. يمكن للطفل أن يكون صورة، دليل آخر-أي ملف:
@Override
public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
                              String sortOrder) throws FileNotFoundException {

    final MatrixCursor result = new
            MatrixCursor(resolveDocumentProjection(projection));
    final File parent = getFileForDocId(parentDocumentId);
    for (File file : parent.listFiles()) {
        // Adds the file's display name, MIME type, size, and so on.
        includeFile(result, null, file);
    }
    return result;
}

الحصول على معلومات المستند
يجب أن يقوم تنفيذ queryDocument() بإرجاع مؤشر يشير إلى الملف المحدد باستخدام الأعمدة المعرفة في DocumentsContract.Document .

ترجع الطريقة queryDocument() نفس المعلومات التي تم تمريرها في queryChildDocuments() ، ولكن بالنسبة إلى ملف معين:




@Override
public Cursor queryDocument(String documentId, String[] projection) throws
        FileNotFoundException {

    // Create a cursor with the requested projection, or the default projection.
    final MatrixCursor result = new
            MatrixCursor(resolveDocumentProjection(projection));
    includeFile(result, documentId, null);
    return result;
}

يمكن لموفر المستند أيضا توفير صور مصغرة لمستند عن طريق تجاوز طريقة DocumentsProvider.openDocumentThumbnail() وإضافة العلم FLAG_SUPPORTS_THUMBNAIL إلى الملفات المعتمدة. يوفر مقتطف التعليمات البرمجية التالي مثالا على كيفية تنفيذ DocumentsProvider.openDocumentThumbnail() .

@Override
public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint,
                                                     CancellationSignal signal)
        throws FileNotFoundException {

    final File file = getThumbnailFileForDocId(documentId);
    final ParcelFileDescriptor pfd =
        ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
    return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
}

تنبيه : يجب ألا يعرض موفر المستندات الصور المصغرة أكثر من ضعف الحجم المحدد بواسطة المعلمة sizeHint .

افتح مستندا
يجب تنفيذ openDocument() لإرجاع ParcelFileDescriptor يمثل الملف المحدد. تطبيقات أخرى يمكن استخدام ParcelFileDescriptor عاد لتيار البيانات. يستدعي النظام هذه الطريقة بعد أن يقوم المستخدم بتحديد ملف، ويطلب التطبيق العميل الوصول إليه عن طريق استدعاء openFileDescriptor() . فمثلا:


@Override
public ParcelFileDescriptor openDocument(final String documentId,
                                         final String mode,
                                         CancellationSignal signal) throws
        FileNotFoundException {
    Log.v(TAG, "openDocument, mode: " + mode);
    // It's OK to do network operations in this method to download the document,
    // as long as you periodically check the CancellationSignal. If you have an
    // extremely large file to transfer from the network, a better solution may
    // be pipes or sockets (see ParcelFileDescriptor for helper methods).

    final File file = getFileForDocId(documentId);
    final int accessMode = ParcelFileDescriptor.parseMode(mode);

    final boolean isWrite = (mode.indexOf('w') != -1);
    if(isWrite) {
        // Attach a close listener if the document is opened in write mode.
        try {
            Handler handler = new Handler(getContext().getMainLooper());
            return ParcelFileDescriptor.open(file, accessMode, handler,
                        new ParcelFileDescriptor.OnCloseListener() {
                @Override
                public void onClose(IOException e) {

                    // Update the file with the cloud server. The client is done
                    // writing.
                    Log.i(TAG, "A file with id " +
                    documentId + " has been closed! Time to " +
                    "update the server.");
                }

            });
        } catch (IOException e) {
            throw new FileNotFoundException("Failed to open document with id"
            + documentId + " and mode " + mode);
        }
    } else {
        return ParcelFileDescriptor.open(file, accessMode);
    }
}

إذا كان مزود المستند يتدفق الملفات أو يتعامل مع هياكل البيانات المعقدة، فكر في تنفيذ createReliablePipe() أو createReliableSocketPair() . هذه الأساليب تسمح لك لإنشاء زوج من الكائنات ParcelFileDescriptor ، حيث يمكنك العودة واحدة وإرسال الأخرى عبر ParcelFileDescriptor.AutoCloseOutputStream أو ParcelFileDescriptor.AutoCloseInputStream .

دعم الوثائق الأخيرة والبحث
يمكنك تقديم قائمة المستندات التي تم تعديلها مؤخرا تحت جذر موفر المستند الخاص بك عن طريق تجاوز طريقة queryRecentDocuments() والعودة FLAG_SUPPORTS_RECENTS ، يظهر مقتطف التعليمات البرمجية التالي مثال كيفية تنفيذ أساليب queryRecentDocuments() .

@Override
public Cursor queryRecentDocuments(String rootId, String[] projection)
        throws FileNotFoundException {

    // This example implementation walks a
    // local file structure to find the most recently
    // modified files.  Other implementations might
    // include making a network call to query a
    // server.

    // Create a cursor with the requested projection, or the default projection.
    final MatrixCursor result =
        new MatrixCursor(resolveDocumentProjection(projection));

    final File parent = getFileForDocId(rootId);

    // Create a queue to store the most recent documents,
    // which orders by last modified.
    PriorityQueue<File> lastModifiedFiles =
        new PriorityQueue<File>(5, new Comparator<File>() {

        public int compare(File i, File j) {
            return Long.compare(i.lastModified(), j.lastModified());
        }
    });

    // Iterate through all files and directories
    // in the file structure under the root.  If
    // the file is more recent than the least
    // recently modified, add it to the queue,
    // limiting the number of results.
    final LinkedList<File> pending = new LinkedList<File>();

    // Start by adding the parent to the list of files to be processed
    pending.add(parent);

    // Do while we still have unexamined files
    while (!pending.isEmpty()) {
        // Take a file from the list of unprocessed files
        final File file = pending.removeFirst();
        if (file.isDirectory()) {
            // If it's a directory, add all its children to the unprocessed list
            Collections.addAll(pending, file.listFiles());
        } else {
            // If it's a file, add it to the ordered queue.
            lastModifiedFiles.add(file);
        }
    }

    // Add the most recent files to the cursor,
    // not exceeding the max number of results.
    for (int i = 0; i < Math.min(MAX_LAST_MODIFIED + 1, lastModifiedFiles.size()); i++) {
        final File file = lastModifiedFiles.remove();
        includeFile(result, null, file);
    }
    return result;
}

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

دعم إنشاء المستند
يمكنك السماح لتطبيقات العميل بإنشاء ملفات داخل موفر المستندات. إذا كان تطبيق عميل يرسل نية ACTION_CREATE_DOCUMENT ، يمكن لموفر المستند السماح لتطبيق العميل هذا بإنشاء مستندات جديدة داخل موفر المستند.

لدعم إنشاء المستند، يجب أن يحتوي الجذر على العلم FLAG_SUPPORTS_CREATE . يجب أن تحتوي الدلائل التي تسمح بإنشاء ملفات جديدة داخلها على علامة FLAG_DIR_SUPPORTS_CREATE .

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

يوضح مقتطف الشفرة التالي كيفية إنشاء ملف جديد داخل موفر مستندات.
@Override
public String createDocument(String documentId, String mimeType, String displayName)
        throws FileNotFoundException {

    File parent = getFileForDocId(documentId);
    File file = new File(parent.getPath(), displayName);
    try {
        file.createNewFile();
        file.setWritable(true);
        file.setReadable(true);
    } catch (IOException e) {
        throw new FileNotFoundException("Failed to create document with name " +
                displayName +" and documentId " + documentId);
    }
    return getDocIdForFile(file);
}

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

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

يوفر الجدول التالي طريقة COLUMN_FLAGS و DocumentsProvider التي يحتاج موفر الوثائق لتنفيذها لفضح ميزات محددة.

ميزة علم طريقة
حذف ملف FLAG_SUPPORTS_DELETE deleteDocument()
إعادة تسمية ملف FLAG_SUPPORTS_RENAME renameDocument()
نسخ ملف إلى دليل الأصل الجديد داخل موفر الوثيقة FLAG_SUPPORTS_COPY copyDocument()
نقل ملف من دليل إلى آخر داخل موفر المستند FLAG_SUPPORTS_MOVE moveDocument()
إزالة ملف من الدليل الرئيسي FLAG_SUPPORTS_REMOVE removeDocument()
دعم الملفات الافتراضية وتنسيقات الملفات البديلة
الملفات الظاهرية ، وهي ميزة أدخلت في الروبوت 7.0 (أبي مستوى 24)، يسمح لموفري الوثائق لتوفير إمكانية الوصول إلى الملفات التي لم يكن لديك تمثيل بتيكود مباشرة. لتمكين تطبيقات أخرى لعرض الملفات الظاهرية، يحتاج موفر المستند إلى إنتاج تمثيل ملف قابل للفتح بديل للملفات الظاهرية.

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

لإعلان أن المستند في موفر الظاهري، تحتاج إلى إضافة العلم FLAG_VIRTUAL_DOCUMENT إلى الملف الذي تم إرجاعه بواسطة الأسلوب queryDocument() . تنبه هذه العلامة تطبيقات العميل أن الملف ليس له تمثيل بتيكود مباشر ولا يمكن فتحه مباشرة.

إذا كنت تعلن أن ملف في موفر المستند الخاص بك هو الظاهري، فمن المستحسن أن تجعله متاحا في نوع مايم آخر مثل صورة أو بدف. يعلن موفر المستندات أنواع مايم البديلة التي يدعمها لعرض ملف ظاهري من خلال تجاوز الأسلوب getDocumentStreamTypes() . عند استدعاء تطبيقات العميل الأسلوب getStreamTypes(android.net.Uri, java.lang.String) ، يستدعي النظام الأسلوب getDocumentStreamTypes() موفر المستند. ثم تقوم الطريقة getDocumentStreamTypes() بإرجاع صفيف من أنواع مايم البديلة التي يدعمها موفر المستند للملف.

بعد أن يحدد العميل أن موفر الوثيقة يمكن أن تنتج المستند بتنسيق ملف قابل للعرض، التطبيق العميل يدعو الأسلوب openTypedAssetFileDescriptor() ، الذي يستدعي داخليا أسلوب موفر openTypedDocument() موفر openTypedDocument() . يقوم موفر الوثيقة بإرجاع الملف إلى تطبيق العميل بتنسيق الملف المطلوب.

يوضح مقتطف الشفرة التالي تنفيذ بسيط من getDocumentStreamTypes() و openTypedDocument() .

public static String[] SUPPORTED_MIME_TYPES = {"image/png", "image/jpg"};

@Override
public AssetFileDescriptor openTypedDocument(String documentId,
    String mimeTypeFilter,
    Bundle opts,
    CancellationSignal signal) {

    try {

        // Determine which supported MIME type the client app requested.
        if ("image/png".equals(mimeTypeFilter) ||
            "image/*".equals(mimeTypeFilter) ||
            "*/*".equals(mimeTypeFilter)) {

            // Return the file in the specified format.
            return openPngDocument(documentId);

        } else if ("image/jpg".equals(mimeTypeFilter)) {
            return openJpgDocument(documentId);
        } else {
            throw new IllegalArgumentException("Invalid mimeTypeFilter " + mimeTypeFilter);
        }

    } catch (Exception ex) {
        Log.e(TAG, ex.getMessage());
    } finally {
        return null;
    }
}

@Override
public String[] getDocumentStreamTypes(String documentId, String mimeTypeFilter) {

    // Return all supported MIME tyupes if the client app
    // passes in '*/*' or 'image/*'.
    if ("*/*".equals(mimeTypeFilter) ||
        "image/*".equals(mimeTypeFilter)) {
        return SUPPORTED_MIME_TYPES;
    }

    ArrayList<String> requestedMimeTypes = new ArrayList<>();

    // Iterate over the list of supported mime types to find a match.
    for (int i=0; i < SUPPORTED_MIME_TYPES.length; i++) {
        if (SUPPORTED_MIME_TYPES[i].equals(mimeTypeFilter)) {
            requestedMimeTypes.add(SUPPORTED_MIME_TYPES[i]);
        }
    }
    return (String[])requestedMimeTypes.toArray();
}

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


public Cursor queryRoots(String[] projection) throws FileNotFoundException {
...
    // If user is not logged in, return an empty root cursor.  This removes our
    // provider from the list entirely.
    if (!isUserLoggedIn()) {
        return result;
}



الخطوة الأخرى هي استدعاء getContentResolver().notifyChange() . تذكر DocumentsContract ؟ نحن نستخدمها لجعل هذا أوري. يخبرك المقتطف التالي النظام بالاستعلام عن جذور مقدم المستند عندما تتغير حالة تسجيل الدخول للمستخدم. إذا لم يتم تسجيل المستخدم، مكالمة إلى queryRoots() بإرجاع مؤشر فارغ، كما هو موضح أعلاه. وهذا يضمن أن تكون وثائق المزود متاحة فقط إذا تم تسجيل المستخدم في موفر الخدمة.
private void onLoginButtonClick() {
    loginOrLogout();
    getContentResolver().notifyChange(DocumentsContract
            .buildRootsUri(AUTHORITY), null);
}


ليست هناك تعليقات:

إرسال تعليق

للوضع الليلي في التطبيق DayNight

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