ارائهدهنده محتوا دسترسی به مخزن مرکزی دادهها را مدیریت میکند. شما یک
ارائهدهنده را بهعنوان یک یا چند کلاس در برنامه Android، همراه با عناصر موجود در
فایل مانیفست پیادهسازی میکنید. یکی از کلاسهای شما زیرکلاسی از
ContentProvider را پیادهسازی میکند که واسط بین ارائهدهنده شما و
برنامههای دیگر است.
اگرچه ارائهدهندگان محتوا برای دردسترس قرار دادن دادهها برای سایر برنامهها طراحی شدهاند، میتوانید فعالیتهایی در برنامهتان داشته باشید که به کاربر اجازه میدهد دادههای مدیریتشده توسط ارائهدهنده شما را پُرسمان و اصلاح کند.
این صفحه حاوی فرایند اصلی برای ساختن ارائهدهنده محتوا و فهرستی از میاناهای برنامهسازی کاربردی برای استفاده است.
قبلاز شروع ساختن
قبلاز شروع به ساختن ارائهدهنده، موارد زیر را درنظر بگیرید:
-
تصمیم بگیرید که آیا به ارائهدهنده محتوا نیاز دارید یا نه. اگر میخواهید یک یا چند ویژگی زیر را ارائه دهید، باید ارائهدهنده محتوا بسازید:
- میخواهید دادهها یا فایلهای پیچیده را به برنامههای دیگر ارائه دهید.
- میخواهید به کاربران اجازه دهید دادههای پیچیده را از برنامه شما در برنامههای دیگر کپی کنند.
- میخواهید بااستفاده از چارچوب جستجو، پیشنهادهای جستجوی سفارشی ارائه دهید.
- میخواهید دادههای برنامهتان را دراختیار ابزارهها قرار دهید.
- میخواهید کلاسهای
AbstractThreadedSyncAdapter،CursorAdapter، یاCursorLoaderرا پیادهسازی کنید.
اگر استفاده کاملاً در برنامه خودتان باشد و به هیچکدام از ویژگیهای فهرستشده در بالا نیاز نداشته باشید، برای استفاده از پایگاههای داده یا انواع دیگر فضای ذخیرهسازی پایدار به ارائهدهنده نیاز ندارید. درعوض، میتوانید از یکی از سیستمهای ذخیرهسازی شرحدادهشده در نمای کلی ذخیرهسازی داده و فایل استفاده کنید.
- اگر قبلاً این کار را نکردهاید، مبانی ارائهدهنده محتوا را بخوانید تا درباره ارائهدهندگان و نحوه عملکرد آنها بیشتر بدانید.
سپس، این مراحل را برای ساختن ارائهدهنده خود دنبال کنید:
-
فضای ذخیرهسازی خام را برای دادههایتان طراحی کنید. ارائهدهنده محتوا دادهها را به دو روش ارائه میدهد:
- دادههای فایل
- دادههایی که معمولاً در فایلها قرار میگیرند، مثل عکس، صدا، یا ویدیو. فایلها را در فضای خصوصی برنامهتان ذخیره کنید. در پاسخ به درخواست فایل از برنامهای دیگر، ارائهدهنده شما میتواند دستهای برای فایل ارائه دهد.
- دادههای «ساختاریافته»
- دادههایی که معمولاً وارد پایگاه داده، آرایه، یا ساختار مشابهی میشوند. دادهها را در قالبی ذخیره کنید که با جدولهای ردیفی و ستونی سازگار باشد. هر ردیف نشاندهنده یک نهاد است، مثل یک شخص یا یک مورد در موجودی. ستون نشاندهنده برخیاز دادههای نهاد است، مانند نام شخص یا قیمت کالا. یک روش رایج برای ذخیره کردن این نوع دادهها در پایگاه داده SQLite است، اما میتوانید از هر نوع فضای ذخیرهسازی پایداری استفاده کنید. برای کسب اطلاعات بیشتر درباره انواع فضای ذخیرهسازی موجود در سیستم Android، به بخش طراحی فضای ذخیرهسازی داده مراجعه کنید.
-
پیادهسازی عینی کلاس
ContentProviderو روشهای موردنیاز آن را تعریف کنید. این کلاس رابط بین دادههای شما و بقیه سیستم Android است. برای اطلاعات بیشتر درباره این کلاس، بخش پیادهسازی کلاس ContentProvider را ببینید. - رشته مرجع ارائهدهنده، شناسه منبع یکسان محتوا، و نام ستونها را تعریف کنید. اگر میخواهید برنامه ارائهدهنده هدفها را مدیریت کند، کنشهای هدف، دادههای اضافی، و پرچمها را نیز تعریف کنید. همچنین اجازههایی را که برای برنامههایی که میخواهند به دادههایتان دسترسی داشته باشند لازم دارید تعریف کنید. همه این مقادیر را بهعنوان ثابت در کلاس قرارداد جداگانهای تعریف کنید. بعداً میتوانید این کلاس را برای توسعهدهندگان دیگر آشکار کنید. برای اطلاعات بیشتر درباره شناسه منبع یکسان محتوا، بخش طراحی شناسه منبع یکسان محتوا را ببینید. برای اطلاعات بیشتر درباره هدفها، به بخش هدفها و دسترسی به دادهها مراجعه کنید.
-
بخشهای اختیاری دیگر را اضافه کنید، مثلاً دادههای نمونه یا پیادهسازی
AbstractThreadedSyncAdapterکه میتواند دادهها را بین ارائهدهنده و دادههای مبتنی بر ابر همگامسازی کند.
طراحی فضای ذخیرهسازی دادهها
ارائهدهنده محتوا واسطی برای دادههای ذخیرهشده در قالب ساختاریافته است. قبلاز ایجاد میانای کاربری، تصمیم بگیرید که دادهها را چگونه ذخیره کنید. میتوانید دادهها را به هر شکلی که میخواهید ذخیره کنید و سپس میانای لازم را برای خواندن و نوشتن دادهها طراحی کنید.
برخیاز فناوریهای ذخیرهسازی داده دردسترس در Android عبارتاند از:
- اگر با دادههای ساختاریافته کار میکنید، یک پایگاه داده رابطهای مانند SQLite یا یک پایگاه داده غیررابطهای کلید-مقدار مانند LevelDB را درنظر بگیرید. اگر با دادههای بدون ساختار مانند رسانههای صوتی، تصویری، یا ویدیویی کار میکنید، ذخیره کردن دادهها بهصورت فایل را درنظر بگیرید. میتوانید چندین نوع مختلف فضای ذخیرهسازی را ترکیب و مطابقت دهید و درصورت لزوم آنها را بااستفاده از یک ارائهدهنده محتوا نمایش دهید.
-
سیستم Android میتواند با کتابخانه پایداری Room تعامل داشته باشد، که
دسترسی به میانای برنامهسازی کاربردی پایگاه داده SQLite را فراهم میکند که ارائهدهندگان خود Android
از آن برای ذخیره دادههای جدولمحور استفاده میکنند. برای ایجاد پایگاه داده بااستفاده از این
کتابخانه، یک زیرکلاس از
RoomDatabaseرا نمونهسازی کنید، همانطور که در ذخیره کردن دادهها در پایگاه داده محلی بااستفاده از Room شرح داده شده است.برای پیادهسازی مخزن خود نیازی به استفاده از پایگاه داده ندارید. ارائهدهنده بهصورت خارجی بهعنوان مجموعهای از جدولها، مشابه پایگاه داده رابطهای، ظاهر میشود، اما این الزامی برای پیادهسازی داخلی ارائهدهنده نیست.
- برای ذخیره کردن دادههای فایل، Android انواع مختلفی از میاناهای برنامهسازی کاربردی فایلمحور دارد. برای کسب اطلاعات بیشتر درباره فضای ذخیرهسازی فایل، نمای کلی فضای ذخیرهسازی داده و فایل را بخوانید. اگر ارائهدهندهای طراحی میکنید که دادههای مرتبط با رسانه مانند موسیقی یا ویدیو ارائه میدهد، میتوانید ارائهدهندهای داشته باشید که دادههای جدول و فایلها را ترکیب میکند.
- در موارد نادر، ممکن است از پیادهسازی بیشاز یک ارائهدهنده محتوا برای یک برنامه بهرهمند شوید. برای مثال، ممکن است بخواهید برخیاز دادهها را بااستفاده از یک ارائهدهنده محتوا با ابزارهای همرسانی کنید و مجموعه داده دیگری را برای همرسانی با برنامههای دیگر درمعرض دید قرار دهید.
-
برای کار با دادههای مبتنی بر شبکه، از کلاسهای
java.netوandroid.netاستفاده کنید. همچنین میتوانید دادههای مبتنی بر شبکه را با یک فضای ذخیرهسازی داده محلی مانند پایگاه داده همگامسازی کنید و سپس دادهها را بهصورت جدول یا فایل ارائه دهید.
توجه: اگر تغییری در مخزن خود ایجاد کنید که با نسخه قدیمی سازگار نباشد، باید مخزن را با شماره نسخه جدید علامتگذاری کنید. همچنین باید شماره نسخه برنامهتان را که ارائهدهنده محتوای جدید را پیادهسازی میکند افزایش دهید. این تغییر از خرابی سیستم درنتیجه تنزل سیستم هنگام تلاش برای بازنصب برنامهای که ارائهدهنده محتوای ناسازگار دارد جلوگیری میکند.
ملاحظات طراحی داده
در اینجا چند نکته برای طراحی ساختار داده ارائهدهنده شما آورده شده است:
-
دادههای جدول همیشه باید ستون «کلید اصلی» داشته باشد که ارائهدهنده آن را بهعنوان مقدار عددی یکتا برای هر ردیف حفظ میکند. میتوانید از این مقدار برای پیوند دادن ردیف به ردیفهای مرتبط در جدولهای دیگر استفاده کنید (بااستفاده از آن بهعنوان «کلید خارجی»). اگرچه میتوانید از هر نامی برای این ستون استفاده کنید، استفاده از
BaseColumns._IDبهترین انتخاب است، زیرا پیوند دادن نتایج پُرسمان ارائهدهنده بهListViewمستلزم این است که یکی از ستونهای بازیابیشده نام_IDرا داشته باشد. -
اگر میخواهید تصاویر بیتمپ یا سایر قطعات بسیار بزرگ دادههای فایلمحور را ارائه دهید،
دادهها را در یک فایل ذخیره کنید و سپس آن را بهصورت غیرمستقیم ارائه دهید، نه اینکه مستقیماً در یک
جدول ذخیره کنید. اگر این کار را انجام دهید، باید به کاربران ارائهدهنده خود بگویید که برای دسترسی به دادهها باید از روش فایل
ContentResolverاستفاده کنند. -
از نوع داده شیء بزرگ دودویی (BLOB) برای ذخیره کردن دادههایی که اندازه متغیر دارند یا ساختارشان
متغیر است استفاده کنید. برای مثال، میتوانید از ستون BLOB برای ذخیره کردن
میانگیر پروتکل یا
ساختار JSON استفاده کنید.
همچنین میتوانید از BLOB برای پیادهسازی جدول مستقل از طرح استفاده کنید. در این نوع جدول، یک ستون کلید اصلی، یک ستون نوع MIME، و یک یا چند ستون عمومی را بهعنوان BLOB تعریف میکنید. معنی دادهها در ستونهای BLOB با مقدار موجود در ستون نوع MIME نشان داده میشود. این کار به شما امکان میدهد انواع مختلف ردیف را در یک جدول ذخیره کنید. جدول «داده» ارائهدهنده مخاطبین
ContactsContract.Dataنمونهای از جدول مستقل از طرحواره است.
طراحی شناسه منبع یکسان محتوا
شناسه منبع یکسان محتوا شناسه منبع یکسانی است که دادهها را در ارائهدهنده شناسایی میکند. شناسههای منبع یکسان محتوا شامل
نام نمادین کل ارائهدهنده (مرجع آن) و
نامی است که به جدول یا فایل (مسیر) اشاره میکند. بخش شناسه اختیاری به
ردیف خاصی در جدول اشاره میکند. هر روش دسترسی به داده
ContentProvider دارای یک شناسه منبع یکسان محتوا بهعنوان آرگومان است. این کار به شما اجازه میدهد
جدول، ردیف، یا فایل موردنظر را برای دسترسی تعیین کنید.
برای کسب اطلاعات درباره شناسه منبع یکسان محتوا، به مبانی ارائهدهنده محتوا مراجعه کنید.
طراحی مرجع
ارائهدهنده معمولاً یک مرجع واحد دارد که بهعنوان نام داخلی Android آن عمل میکند. برای اجتناب از تداخل با سایر ارائهدهندگان، از مالکیت دامنه اینترنتی (بهصورت معکوس) بهعنوان مبنای مرجع ارائهدهنده خود استفاده کنید. ازآنجاییکه این توصیه برای نام بستههای Android نیز صادق است، میتوانید مرجع ارائهدهنده خود را بهعنوان افزونهای از نام بستهای که حاوی ارائهدهنده است تعریف کنید.
برای مثال، اگر نام بسته Android شما
com.example.<appname> است، به ارائهدهنده خود
اختیار com.example.<appname>.provider را بدهید.
طراحی ساختار مسیر
توسعهدهندگان معمولاً شناسه منبع یکسان محتوا را از مرجع با افزودن مسیرهایی که به جدولهای تکی اشاره میکنند ایجاد میکنند. برای مثال، اگر دو جدول table1 و
table2 داشته باشید، میتوانید آنها را با مرجع مثال قبلی ترکیب کنید تا
شناسههای منبع یکسان محتوای
com.example.<appname>.provider/table1 و
com.example.<appname>.provider/table2 را بهدست آورید. مسیرها به یک بخش محدود نمیشوند و لازم نیست برای هر سطح از مسیر یک جدول وجود داشته باشد.
مدیریت شناسههای شناسه منبع یکسان محتوا
طبق قرارداد، ارائهدهندگان با پذیرفتن شناسه منبع یکسان محتوا با مقدار شناسه برای ردیف در انتهای شناسه منبع یکسان،
دسترسی به یک ردیف در جدول را ارائه میدهند. همچنین طبق قرارداد، ارائهدهندگان مقدار شناسه را با ستون _ID جدول مطابقت میدهند و دسترسی درخواستی را برای ردیف منطبق انجام میدهند.
این قرارداد الگوی طراحی مشترکی را برای برنامههایی که به ارائهدهنده دسترسی دارند تسهیل میکند. برنامه
پُرسمانی را درمقابل ارائهدهنده انجام میدهد و Cursor حاصل را
در ListView بااستفاده از CursorAdapter نمایش میدهد.
تعریف CursorAdapter مستلزم این است که یکی از ستونها در
Cursor، _ID باشد
سپس کاربر یکی از ردیفهای نمایشدادهشده را از میانای کاربر انتخاب میکند تا دادهها را مشاهده یا اصلاح کند. برنامه ردیف مربوطه را از Cursor که از ListView پشتیبانی میکند دریافت میکند، مقدار _ID را برای این ردیف دریافت میکند، آن را به شناسه منبع یکسان محتوا اضافه میکند، و درخواست دسترسی را به ارائهدهنده ارسال میکند. سپس ارائهدهنده میتواند
پرسوجو یا اصلاح را در همان ردیفی که کاربر انتخاب کرده است انجام دهد.
الگوهای شناسه منبع یکسان محتوا
برای کمک به شما در انتخاب کنشی که باید برای شناسه منبع یکسان محتوای ورودی انجام دهید، میانای برنامهنویسی کاربردی ارائهدهنده شامل
کلاس راحتی 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/* -
با شناسه منبع یکسان محتوا برای جدولهای
dataset1وdataset2مطابقت دارد، اما با شناسه منبع یکسان محتوا برای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() مقدار عدد صحیح را برای URI برمیگرداند. عبارت switch
بین پُرسمان کل جدول و پُرسمان برای یک سابقه انتخاب میکند.
کاتلین
private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. 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. */ 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. */ addURI("com.example.app.provider", "table3/#", 2) } ... class ExampleProvider : ContentProvider() { ... // Implements ContentProvider.query() override fun query( uri: Uri?, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor? { var localSortOrder: String = sortOrder ?: "" var localSelection: String = selection ?: "" when (sUriMatcher.match(uri)) { 1 -> { // If the incoming URI was for all of table3 if (localSortOrder.isEmpty()) { localSortOrder = "_ID ASC" } } 2 -> { // If the incoming URI was for a single row /* * 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. */ localSelection += "_ID ${uri?.lastPathSegment}" } else -> { // If the URI isn't recognized, // do some error handling here } } // Call the code to actually do the query } }
جاوا
public class ExampleProvider extends ContentProvider { ... // Creates a UriMatcher object. private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to one. No wildcard is used * in the path. */ uriMatcher.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. */ uriMatcher.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 (uriMatcher.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 isn't recognized, do some error handling here } // Call the code to actually do the query }
کلاس دیگری، ContentUris، روشهای راحتی را برای کار با بخش id از شناسه منبع یکسان محتوا ارائه میدهد. کلاسهای Uri و
Uri.Builder شامل روشهای راحتی برای تجزیه کردن اشیای
Uri موجود و ساختن اشیای جدید است.
پیادهسازی کلاس ContentProvider
نمونه ContentProvider دسترسی به مجموعه ساختاریافتهای از دادهها را با رسیدگی به درخواستهای برنامههای دیگر مدیریت میکند. همه انواع
دسترسی درنهایت ContentResolver را فراخوانی میکنند، که سپس یک روش واقعی
ContentProvider را برای دریافت دسترسی فراخوانی میکند.
روشهای الزامی
کلاس انتزاعی ContentProvider شش روش انتزاعی را تعریف میکند که
شما بهعنوان بخشی از زیرکلاس واقعی خود پیادهسازی میکنید. همه این روشها بهجز
onCreate() توسط برنامه کارخواهی فراخوانده میشوند
که در تلاش است به ارائهدهنده محتوای شما دسترسی پیدا کند.
-
query() -
دادهها را از ارائهدهندهتان بازیابی کنید. از آرگومانها برای انتخاب جدول موردنظر برای پُرسمان، ردیفها و ستونهای موردنظر برای برگرداندن، و ترتیب مرتبسازی نتیجه استفاده کنید.
دادهها را بهعنوان شیء
Cursorبرمیگرداند. -
insert() - ردیف جدیدی در ارائهدهندهتان درج کنید. از آرگومانها برای انتخاب جدول مقصد و دریافت مقادیر ستون برای استفاده استفاده کنید. یک شناسه منبع یکسان محتوا برای ردیف جدیداً درجشده برگردانید.
-
update() - ردیفهای موجود را در ارائهدهندهتان بهروز کنید. از آرگومانها برای انتخاب جدول و ردیفها برای بهروزرسانی و دریافت مقادیر ستون بهروزشده استفاده کنید. تعداد ردیفهای بهروزشده را برمیگرداند.
-
delete() - ردیفها را از ارائهدهندهتان حذف کنید. از آرگومانها برای انتخاب جدول و ردیفهای موردنظر برای حذف استفاده کنید. تعداد ردیفهای حذفشده را برمیگرداند.
-
getType() - نوع MIME مربوط به شناسه منبع یکسان محتوا را برمیگرداند. این روش با جزئیات بیشتر در بخش پیادهسازی انواع MIME ارائهدهنده محتوا شرح داده شده است.
-
onCreate() -
ارائهدهندهتان را مقداردهی اولیه کنید. سیستم Android این روش را بلافاصله پساز ایجاد ارائهدهنده شما فراخوانی میکند. ارائهدهنده شما تا زمانی که شیء
ContentResolverتلاش نکند به آن دسترسی پیدا کند ایجاد نمیشود.
این روشها امضای یکسانی با روشهای ContentResolver با نام یکسان دارند.
پیادهسازی شما از این روشها باید موارد زیر را درنظر بگیرد:
-
همه این روشها بهجز
onCreate()میتوانند بهطور همزمان توسط چندین رشته فراخوانی شوند، بنابراین باید رشتهای ایمن باشند. برای کسب اطلاعات بیشتر درباره رشتههای متعدد، نمای کلی فرایندها و رشتهها را ببینید. -
از انجام عملیات طولانی در
onCreate()خودداری کنید. تکالیف راهاندازی را تا زمانی که واقعاً به آنها نیاز باشد بهتعویق بیندازید. بخش مربوط به پیادهسازی روش onCreate() این موضوع را با جزئیات بیشتری مورد بحث قرار میدهد. -
اگرچه باید این روشها را پیادهسازی کنید، کد شما لازم نیست کاری انجام دهد بهجز
برگرداندن نوع داده موردانتظار. برای مثال، میتوانید با نادیده گرفتن فراخوانی
insert()و برگرداندن 0، از درج داده در برخی جدولها توسط برنامههای دیگر جلوگیری کنید.
پیادهسازی روش query()
روش
ContentProvider.query() باید شیء Cursor را برگرداند یا، اگر
ناموفق بود، Exception را برگرداند. اگر از پایگاه داده SQLite بهعنوان فضای ذخیرهسازی دادههایتان استفاده میکنید، میتوانید Cursor را که توسط یکی از روشهای query() کلاس SQLiteDatabase برگردانده شده است برگردانید.
اگر پُرسمان با هیچ ردیفی مطابقت نداشته باشد، یک نمونه Cursor برگردانید که روش getCount() آن مقدار ۰ را برمیگرداند.
فقط درصورتی null را برگردانید که خطای داخلی درطول فرایند پُرسمان رخ داده باشد.
اگر از پایگاه داده SQLite بهعنوان فضای ذخیرهسازی دادههایتان استفاده نمیکنید، از یکی از زیرکلاسهای واقعی
Cursor استفاده کنید. برای مثال، کلاس MatrixCursor
مکاننمایی را پیادهسازی میکند که در آن هر ردیف آرایهای از نمونههای Object است. با این کلاس،
از addRow() برای افزودن ردیف جدید استفاده کنید.
سیستم Android باید بتواند Exception را در سراسر مرزهای فرایند منتقل کند. Android میتواند این کار را برای استثناهای زیر که در رسیدگی به خطاهای پُرسمان مفید هستند انجام دهد:
-
IllegalArgumentException. اگر ارائهدهنده شما شناسه منبع یکسان محتوای نامعتبری دریافت کند، میتوانید این استثنا را ایجاد کنید. -
NullPointerException
پیادهسازی روش insert()
روش insert() بااستفاده از مقادیر موجود در آرگومان ContentValues، ردیف جدیدی به جدول مناسب اضافه میکند. اگر نام ستونی در آرگومان ContentValues نباشد،
شاید بخواهید مقدار پیشفرضی برای آن در کد ارائهدهنده یا در طرحواره پایگاه دادهتان
ارائه دهید.
این روش شناسه منبع یکسان محتوا را برای ردیف جدید برمیگرداند. برای ساختن این، کلید اصلی ردیف جدید را که معمولاً مقدار _ID است، بااستفاده از withAppendedId() به شناسه منبع یکسان محتوای جدول اضافه کنید.
پیادهسازی روش delete()
روش delete()
نباید ردیفها را از فضای ذخیرهسازی دادههایتان حذف کند. اگر از وفقدهنده همگامسازی
با ارائهدهندهتان استفاده میکنید، ردیف حذفشده را
با پرچم «حذف» علامتگذاری کنید و ردیف را بهطور کامل برندارید. وفقدهنده همگامسازی میتواند
ردیفهای حذفشده را بررسی کند و آنها را قبلاز حذف کردن از ارائهدهنده، از سرور بردارد.
پیادهسازی روش update()
روش update() همان متغیر مستقل ContentValues را که توسط
insert() استفاده میشود و همان متغیرهای مستقل selection و selectionArgs را که توسط
delete() و
ContentProvider.query() استفاده میشود میگیرد.
این کار ممکن است به شما اجازه دهد از کد بین این روشها دوباره استفاده کنید.
پیادهسازی روش onCreate()
سیستم Android هنگام راهاندازی ارائهدهنده،
onCreate() را فراخوانی میکند. در این روش، فقط کارهای مقداردهی اولیه با اجرای سریع را انجام دهید و ایجاد پایگاه داده و بار کردن دادهها را تا زمانی که ارائهدهنده واقعاً درخواستی برای دادهها دریافت کند بهتعویق بیندازید. اگر تکالیف طولانی را در
onCreate() انجام دهید، راهاندازی ارائهدهنده
را کند میکنید. این امر بهنوبه خود باعث میشود پاسخ ارائهدهنده به برنامههای دیگر کند شود.
دو قطعه کد زیر تعامل بین
ContentProvider.onCreate() و
Room.databaseBuilder() را نشان میدهد. اولین
قطعه کد پیادهسازی
ContentProvider.onCreate() را نشان میدهد که در آن
شیء پایگاه داده ساخته میشود و دسترسی به اشیاء دسترسی داده ایجاد میشود:
کاتلین
// Defines the database name private const val DBNAME = "mydb" ... class ExampleProvider : ContentProvider() { // Defines a handle to the Room database private lateinit var appDatabase: AppDatabase // Defines a Data Access Object to perform the database operations private var userDao: UserDao? = null override fun onCreate(): Boolean { // Creates a new database object appDatabase = Room.databaseBuilder(context, AppDatabase::class.java, DBNAME).build() // Gets a Data Access Object to perform the database operations userDao = appDatabase.userDao return true } ... // Implements the provider's insert method override fun insert(uri: Uri, values: ContentValues?): Uri? { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
جاوا
public class ExampleProvider extends ContentProvider // Defines a handle to the Room database private AppDatabase appDatabase; // Defines a Data Access Object to perform the database operations private UserDao userDao; // Defines the database name private static final String DBNAME = "mydb"; public boolean onCreate() { // Creates a new database object appDatabase = Room.databaseBuilder(getContext(), AppDatabase.class, DBNAME).build(); // Gets a Data Access Object to perform the database operations userDao = appDatabase.getUserDao(); return true; } ... // Implements the provider's insert method public Cursor insert(Uri uri, ContentValues values) { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
پیادهسازی انواع MIME در ContentProvider
کلاس ContentProvider دو روش برای برگرداندن انواع MIME دارد:
-
getType() - یکی از روشهای الزامی که برای هر ارائهدهندهای پیادهسازی میکنید.
-
getStreamTypes() - روشی که انتظار میرود اگر ارائهدهنده شما فایل ارائه میدهد، آن را پیادهسازی کنید.
انواع MIME برای جدولها
روش getType() یک
String در قالب MIME برمیگرداند که نوع داده برگرداندهشده توسط شناسه منبع یکسان محتوا
را شرح میدهد. آرگومان Uri میتواند بهجای URI خاص، الگو باشد.
در این مورد، نوع دادههای مرتبط با شناسه منبع یکسان محتوا را که با الگو مطابقت دارد برگردانید.
برای انواع رایج دادهها مانند نوشتار، HTML، یا JPEG،
getType() نوع MIME استاندارد را برای آن داده برمیگرداند. فهرست کامل این انواع استاندارد در وبسایت
انواع رسانه IANA MIME
دردسترس است.
برای شناسه منبع یکسان محتوایی که به یک یا چند ردیف از دادههای جدول اشاره میکند،
getType()
نوع MIME را در قالب MIME مختص فروشنده Android برمیگرداند:
-
بخش نوع:
vnd -
بخش زیرنوع:
-
اگر الگوی URI برای یک ردیف باشد:
android.cursor.item/ -
اگر الگوی URI برای بیشاز یک ردیف باشد:
android.cursor.dir/
-
اگر الگوی URI برای یک ردیف باشد:
-
بخش مختص ارائهدهنده:
vnd.<name>.<type><name>و<type>را شما ارائه میدهید. مقدار<name>در سطح جهانی یکتا است، و مقدار<type>برای الگوی URI متناظر یکتا است. انتخاب خوب برای<name>، نام شرکت شما یا بخشی از نام بسته Android برنامهتان است. انتخاب خوب برای<type>رشتهای است که جدول مرتبط با URI را شناسایی میکند.
برای مثال، اگر مرجع ارائهدهنده
com.example.app.provider باشد و جدولی بهنام
table1 را نمایش دهد، نوع MIME برای چند ردیف در table1 عبارت است از:
vnd.android.cursor.dir/vnd.com.example.provider.table1
برای یک ردیف table1، نوع MIME عبارت است از:
vnd.android.cursor.item/vnd.com.example.provider.table1
انواع MIME برای فایلها
اگر ارائهدهنده شما فایل ارائه میدهد،
getStreamTypes() را پیادهسازی کنید.
این روش آرایه String از انواع MIME را برای فایلهایی که ارائهدهنده شما
میتواند برای شناسه منبع یکسان محتوای دادهشده برگرداند، برمیگرداند. انواع MIME را که ارائه میدهید براساس آرگومان فیلتر نوع MIME فیلتر کنید تا فقط انواع MIME را که کارخواه میخواهد مدیریت کند برگردانید.
برای مثال، ارائهدهندهای را درنظر بگیرید که تصاویر عکس را بهصورت فایلهایی در قالب JPG،
PNG، و GIF ارائه میدهد.
اگر برنامهای ContentResolver.getStreamTypes() را با رشته فیلتر image/* برای چیزی که «تصویر» است فراخوانی کند،
روش ContentProvider.getStreamTypes() آرایه زیر را برمیگرداند:
{ "image/jpeg", "image/png", "image/gif"}
اگر برنامه فقط به فایلهای JPG نیاز داشته باشد، میتواند
ContentResolver.getStreamTypes() را با رشته فیلتر *\/jpeg فراخوانی کند، و
getStreamTypes() این موارد را برمیگرداند:
{"image/jpeg"}
اگر ارائهدهنده شما هیچکدام از انواع MIME درخواستشده در رشته فیلتر را ارائه ندهد،
getStreamTypes()
null را برمیگرداند.
پیادهسازی کلاس قرارداد
کلاس قرارداد یک کلاس public final است که حاوی تعاریف ثابت برای
URI، نام ستون، نوع MIME، و دیگر فرادادههای مربوط به ارائهدهنده است. این کلاس
با اطمینان از اینکه ارائهدهنده میتواند بهدرستی دردسترس قرار گیرد، حتی اگر تغییراتی در مقادیر واقعی URIها، نام ستونها،
و غیره وجود داشته باشد، قراردادی بین ارائهدهنده و سایر برنامهها برقرار میکند.
کلاس قرارداد به توسعهدهندگان نیز کمک میکند زیرا معمولاً نامهای یادیاری برای ثابتهایش دارد، بنابراین احتمال اینکه توسعهدهندگان از مقادیر نادرست برای نامهای ستون یا URI استفاده کنند کمتر است. ازآنجاییکه این یک کلاس است، میتواند حاوی اسناد Javadoc باشد. محیطهای توسعه یکپارچه مانند Android Studio میتوانند نامهای ثابت را از کلاس قرارداد تکمیل خودکار کنند و Javadoc را برای ثابتها نمایش دهند.
توسعهدهندگان نمیتوانند از برنامه شما به فایل کلاس کلاس قرارداد دسترسی داشته باشند، اما میتوانند آن را بهصورت ایستا از فایل JAR که ارائه میدهید در برنامه خودشان کامپایل کنند.
کلاس ContactsContract و کلاسهای تودرتوی آن نمونههایی از
کلاسهای قرارداد هستند.
اجرای اجازههای ارائهدهنده محتوا
اجازهها و دسترسی به همه جنبههای سیستم Android بهطور مفصل در نکات امنیتی شرح داده شده است. نمای کلی فضای ذخیرهسازی داده و فایل همچنین امنیت و اجازههایی را که برای انواع مختلف فضای ذخیرهسازی اعمال میشود شرح میدهد. بهطور خلاصه، نکات مهم عبارتند از:
- بهطور پیشفرض، فایلهای داده ذخیرهشده در فضای ذخیرهسازی داخلی دستگاه برای برنامه و ارائهدهنده شما خصوصی است.
-
SQLiteDatabaseپایگاه دادهای که ایجاد میکنید برای برنامه و ارائهدهنده شما خصوصی است. - بهطور پیشفرض، فایلهای دادهای که در فضای ذخیرهسازی خارجی ذخیره میکنید عمومی و قابلخواندن برای همه هستند. نمیتوانید از ارائهدهنده محتوا برای محدود کردن دسترسی به فایلها در فضای ذخیرهسازی خارجی استفاده کنید، زیرا برنامههای دیگر میتوانند از فراخوانیهای میانای برنامهسازی کاربردی دیگر برای خواندن و نوشتن آنها استفاده کنند.
- فراخوانیهای روش برای باز کردن یا ایجاد فایلها یا پایگاههای داده SQLite در حافظه داخلی دستگاهتان میتواند بهطور بالقوه دسترسی خواندن و نوشتن را به همه برنامههای دیگر بدهد. اگر از فایل یا پایگاه داده داخلی بهعنوان مخزن ارائهدهنده استفاده میکنید و به آن دسترسی «قابلخواندن برای همه» یا «قابلنوشتن برای همه» میدهید، اجازههایی که برای ارائهدهنده در مانیفست آن تنظیم میکنید از دادههایتان محافظت نمیکند. دسترسی پیشفرض برای فایلها و پایگاههای داده در حافظه داخلی «خصوصی» است؛ این مورد را برای مخزن ارائهدهندهتان تغییر ندهید.
اگر میخواهید از اجازههای ارائهدهنده محتوا برای کنترل دسترسی به دادههایتان استفاده کنید، دادههایتان را در فایلهای داخلی، پایگاههای داده SQLite، یا فضای ابری، مثلاً در سرور راه دور، ذخیره کنید و فایلها و پایگاههای داده را برای برنامهتان خصوصی نگه دارید.
اجرای اجازهها
بهطور پیشفرض، همه برنامهها میتوانند از ارائهدهنده شما بخوانند یا در آن بنویسند، حتی اگر دادههای اساسی
خصوصی باشد، زیرا بهطور پیشفرض اجازهای برای ارائهدهنده شما تنظیم نشده است. برای تغییر این مورد،
اجازههای ارائهدهندهتان را در فایل مانیفست بااستفاده از مشخصهها یا عناصر فرزند
عنصر
<provider> تنظیم کنید. میتوانید اجازههایی را تنظیم کنید که برای کل ارائهدهنده،
برای جدولهای خاص، برای رکوردهای خاص، یا هر سه اعمال شود.
اجازههای ارائهدهنده را با یک یا چند عنصر
<permission> در فایل مانیفست تعریف میکنید. برای اینکه
اجازه برای ارائهدهندهتان منحصربهفرد باشد، از محدوده سبک Java برای
مشخصه
android:name استفاده کنید. برای مثال، نام اجازه خواندن را
com.example.app.provider.permission.READ_PROVIDER بگذارید.
فهرست زیر محدوده اجازههای ارائهدهنده را شرح میدهد، که با اجازههایی که برای کل ارائهدهنده اعمال میشود شروع میشود و سپس جزئیتر میشود. اجازههای دقیقتر بر اجازههایی که دامنه بزرگتری دارند اولویت دارند.
- اجازه سطح ارائهدهنده خواندن/ نوشتن تکی
-
یک اجازه که هم دسترسی خواندن و هم دسترسی نوشتن را برای کل ارائهدهنده کنترل میکند، که با
مشخصه
android:permissionاز عنصر<provider>مشخص میشود. - اجازههای خواندن و نوشتن جداگانه در سطح ارائهدهنده
-
اجازه خواندن و اجازه نوشتن برای کل ارائهدهنده. شما آنها را با مشخصههای
android:readPermissionوandroid:writePermissionعنصر<provider>مشخص میکنید. این اجازهها بر اجازه موردنیازandroid:permissionاولویت دارند. - اجازه سطح مسیر
-
اجازه خواندن، نوشتن، یا خواندن/نوشتن برای شناسه منبع یکسان محتوا در ارائهدهنده شما. هر URI را که میخواهید کنترل کنید با عنصر فرزند
<path-permission>از عنصر<provider>مشخص میکنید. برای هر شناسه منبع یکسان محتوایی که مشخص میکنید، میتوانید اجازه خواندن/نوشتن، اجازه خواندن، اجازه نوشتن، یا هر سه را مشخص کنید. اجازههای خواندن و نوشتن بر اجازه خواندن/نوشتن اولویت دارند. همچنین، اجازه سطح مسیر بر اجازههای سطح ارائهدهنده اولویت دارد. - اجازه موقت
-
سطح اجازهای که دسترسی موقت به برنامه را اعطا میکند، حتی اگر برنامه
اجازههایی را که معمولاً موردنیاز است نداشته باشد. ویژگی دسترسی موقت تعداد اجازههایی را که برنامه باید در مانیفست خود درخواست کند کاهش میدهد. وقتی اجازههای موقت را روشن میکنید، تنها برنامههایی که برای ارائهدهنده شما به اجازههای دائمی نیاز دارند، برنامههایی هستند که بهطور مداوم به همه دادههای شما دسترسی دارند.
برای مثال، اجازههایی را درنظر بگیرید که اگر ارائهدهنده ایمیل و برنامهای را پیادهسازی میکنید و میخواهید به برنامه نمایشگر تصویر خارجی اجازه دهید پیوستهای عکس را از ارائهدهنده شما نمایش دهد، به آنها نیاز دارید. برای اینکه به نمایشگر تصویر دسترسی لازم را بدون نیاز به اجازهها بدهید، میتوانید اجازههای موقت برای شناسه منبع یکسان محتوا برای عکسها تنظیم کنید.
برنامه ایمیلتان را طوری طراحی کنید که وقتی کاربر میخواهد عکسی را نمایش دهد، برنامه قصدی حاوی شناسه منبع یکسان محتوای عکس و پرچمهای اجازه را به نمایشگر تصویر ارسال کند. سپس نمایشگر تصویر میتواند از ارائهدهنده ایمیل شما درخواست کند عکس را بازیابی کند، حتی اگر نمایشگر اجازه خواندن معمولی را برای ارائهدهنده شما نداشته باشد.
برای روشن کردن اجازههای موقت، یا ویژگی
android:grantUriPermissionsعنصر<provider>را تنظیم کنید یا یک یا چند عنصر فرزند<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"است.
عنصر <provider>
مانند مؤلفههای Activity و Service،
یک زیرکلاس از ContentProvider
در فایل مانیفست برای برنامه آن تعریف شده است، بااستفاده از
عنصر
<provider>. سیستم Android اطلاعات زیر را از
عنصر دریافت میکند:
-
مرجع
(
android:authorities) - نامهای نمادینی که کل ارائهدهنده را در سیستم شناسایی میکنند. این مشخصه با جزئیات بیشتر در بخش طراحی شناسه منبع یکسان محتوا شرح داده شده است.
-
نام کلاس ارائهدهنده
(
android:name) -
کلاسی که
ContentProviderرا پیادهسازی میکند. این کلاس با جزئیات بیشتر در بخش پیادهسازی کلاس ContentProvider شرح داده شده است. - اجازهها
-
ویژگیهایی که اجازههایی را که برنامههای دیگر باید داشته باشند تا بتوانند به دادههای ارائهدهنده دسترسی پیدا کنند
مشخص میکنند:
-
android:grantUriPermissions: پرچم اجازه موقت -
android:permission: اجازه خواندن/نوشتن تکی در سطح ارائهدهنده -
android:readPermission: اجازه خواندن در سطح ارائهدهنده -
android:writePermission: اجازه نوشتن در سراسر ارائهدهنده
اجازهها و مشخصههای مربوط به آنها با جزئیات بیشتر در بخش پیادهسازی اجازههای ارائهدهنده محتوا شرح داده شده است.
-
- مشخصههای راهاندازی و کنترل
-
این مشخصهها تعیین میکنند که سیستم Android چگونه و چه زمانی ارائهدهنده را راهاندازی کند،
ویژگیهای پردازش ارائهدهنده، و دیگر تنظیمات زمان اجرا:
-
android:enabled: پرچم اجازه دادن به سیستم برای شروع ارائهدهنده -
android:exported: پرچم اجازه دادن به برنامههای دیگر برای استفاده از این ارائهدهنده -
android:initOrder: ترتیبی که این ارائهدهنده شروع میشود، نسبت به سایر ارائهدهندگان در همان فرایند -
android:multiProcess: پرچمی که به سیستم اجازه میدهد ارائهدهنده را در همان فرایند کارخواه فراخواننده راهاندازی کند -
android:process: نام فرایندی که ارائهدهنده در آن اجرا میشود -
android:syncable: پرچمی که نشان میدهد دادههای ارائهدهنده باید با دادههای سرور همگامسازی شود
این مشخصهها بهطور کامل در راهنمای عنصر
<provider>مستند شدهاند. -
- مشخصههای اطلاعاتی
-
نماد و برچسب اختیاری برای ارائهدهنده:
-
android:icon: منبع ترسیمشدنی حاوی نماد ارائهدهنده. این نماد در کنار برچسب ارائهدهنده در فهرست برنامهها در تنظیمات > برنامهها > همه نشان داده میشود. -
android:label: برچسب اطلاعاتی که ارائهدهنده، دادههای آن، یا هر دو را شرح میدهد. این برچسب در فهرست برنامهها در تنظیمات > برنامهها > همه نشان داده میشود.
این مشخصهها بهطور کامل در راهنمای عنصر
<provider>مستند شدهاند. -
توجه: اگر Android نسخه ۱۱ یا بالاتر را هدفیابی میکنید، برای نیازهای پیکربندی بیشتر، سند رؤیتپذیری بسته را بررسی کنید.
قصدها و دسترسی به دادهها
برنامهها میتوانند با Intent بهطور غیرمستقیم به ارائهدهنده محتوا دسترسی داشته باشند.
برنامه هیچکدام از روشهای ContentResolver یا
ContentProvider را فراخوانی نمیکند. درعوض، قصدی ارسال میکند که فعالیتی را شروع میکند،
که اغلب بخشی از برنامه خود ارائهدهنده است. فعالیت مقصد مسئول بازیابی و نمایش دادهها در واسط کاربر خود است.
بسته به کنش موجود در هدف، فعالیت مقصد میتواند از کاربر بخواهد تغییراتی در دادههای ارائهدهنده ایجاد کند. یک هدف ممکن است حاوی دادههای «اضافی» نیز باشد که فعالیت مقصد در واسط کاربر نمایش میدهد. سپس کاربر این گزینه را دارد که این دادهها را قبلاز استفاده برای اصلاح دادهها در ارائهدهنده تغییر دهد.
میتوانید از دسترسی به قصد برای کمک به تمامیت دادهها استفاده کنید. ارائهدهنده شما ممکن است به درج، بهروزرسانی، و حذف دادهها براساس منطق کسبوکار بهدقت تعریفشده وابسته باشد. اگر اینطور باشد، اجازه دادن به برنامههای دیگر برای تغییر مستقیم دادههایتان میتواند منجر به دادههای نامعتبر شود.
اگر میخواهید توسعهدهندگان از دسترسی به قصد استفاده کنند، حتماً آن را بهطور کامل مستند کنید. توضیح دهید چرا دسترسی به هدف بااستفاده از میانای کاربر برنامه شما بهتر از تلاش برای اصلاح دادهها با کد آنها است.
مدیریت هدف ورودی که میخواهد دادههای ارائهدهنده شما را تغییر دهد، تفاوتی با مدیریت اهداف دیگر ندارد. با خواندن قصدها و فیلترهای قصد میتوانید درباره استفاده از قصدها بیشتر بدانید.
برای اطلاعات مرتبط بیشتر، به نمای کلی ارائهدهنده «تقویم» مراجعه کنید.