Content Provider — کامپوننت Android است که یک رابط یکپارچه برای دسترسی به دادهها بین برنامهها فراهم میکند. این کامپوننت ذخیرهسازی فیزیکی (SQLite، فایلها، منابع شبکه) را انتزاع میکند و تبادل امن اطلاعات را از طریق ContentResolver تضمین میکند. بر اساس Android Developer Guide, 2026، Content Provider یکی از چهار کامپوننت اصلی برنامه Android همراه با Activity، Service و BroadcastReceiver است. وظیفه آن — در دسترس قرار دادن دادهها برای سایر برنامهها با کنترل مجوزهای خواندن و نوشتن است.
نکات اصلی
Content Provider — کامپوننت Android است که دسترسی به مخزن داده متمرکز را مدیریت میکند و آنها را از طریق یک رابط قراردادی یکپارچه در اختیار سایر برنامهها قرار میدهد. این کامپوننت جزئیات پیادهسازی ذخیرهسازی را پنهان میکند: دادهها میتوانند در SQLite، سیستم فایل، ابر یا نتیجه یک درخواست شبکه باشند.
Android شامل Content Providerهای داخلی برای دادههای سیستمی است — ContactsContract، MediaStore، CalendarContract، CallLog. برنامههای شخص ثالث نیز میتوانند ارائهدهندگان خود را برای تبادل امن دادهها ایجاد کنند. هر ارائهدهنده در AndroidManifest.xml با مشخص کردن authority — یک رشته یکتا که بخش اول URI را تشکیل میدهد — ثبت میشود.
Content Provider بر اساس مدل مشتری-سرور کار میکند. ارائهدهنده به عنوان سروری عمل میکند که شش روش اجباری را پیادهسازی میکند: query، insert، update، delete، getType و onCreate. مشتری (برنامه دیگر) از طریق ContentResolver به ارائهدهنده دسترسی پیدا میکند که فراخوانیها را از طریق مکانیزم IPC Android به روشهای مربوطه ارائهدهنده منتقل میکند.
هر Content Provider با URI طرح content:// شناسایی میشود. به عنوان مثال، content://com.example.app.provider/items. بخش اول authority (com.example.app.provider) به کلاس ارائهدهنده در مانیفست متصل میشود. مسیر /items به جدول و /items/5 به رکورد خاص با ID=5 اشاره میکند.
هنگامی که یک برنامه ContentResolver.query(URI) را فراخوانی میکند، Android مجوزهای بسته فراخواننده را بررسی میکند، ارائهدهنده را با authority پیدا میکند و اگر هنوز راهاندازی نشده باشد، فرآیند آن را شروع میکند. ارائهدهنده درخواست را اجرا میکند و Cursor — شیئی که حاوی نتیجه است و به مشتری امکان پیمایش روی رکوردها را میدهد — برمیگرداند.
کلاس ContentProvider نیاز به پیادهسازی شش روش انتزاعی دارد. هر روش URI را دریافت میکند و نتیجه متناسب با نوع عملیات را برمیگرداند. سیستم این روشها را از هر فرآیندی فراخوانی میکند، بنابراین آنها باید thread-safe باشند و اجرا را برای مدت طولانی مسدود نکنند.
روش query URI، آرایه ستونهای projection، رشته selection با آرگومانها و ترتیب مرتبسازی را دریافت میکند. یک Cursor با دادهها برمیگرداند. در پیادهسازی باید URI را با UriMatcher تجزیه کرد و درخواست SQL مربوطه را به پایگاه داده اجرا کرد.
این روشها دادهها را در مخزن تغییر میدهند. insert ContentValues — جفتهای کلید-مقدار را دریافت میکند و URI رکورد جدید را برمیگرداند. update و delete selection را برای فیلتر کردن رکوردها دریافت میکنند و تعداد سطرهای تحت تأثیر را برمیگردانند. پس از تغییر دادهها، ارائهدهنده باید از طریق ContentResolver.notifyChange اطلاع دهد.
| روش | هدف | بازگشت |
|---|---|---|
| query | دریافت دادهها با URI | Cursor یا null |
| insert | افزودن رکورد جدید | URI رکورد جدید |
| update | بهروزرسانی رکوردهای موجود | int (تعداد سطرها) |
| delete | حذف رکوردها | int (تعداد سطرها) |
| getType | نوع MIME برای URI | String |
| onCreate | راهاندازی ارائهدهنده | boolean |
ContentResolver — یک نقطه دسترسی واحد برای کار با تمام Content Providerها در سیستم است. برنامه مشتری هرگز روشهای ارائهدهنده را مستقیماً فراخوانی نمیکند — فقط از طریق ContentResolver که Android از context دریافت میکند. عملیات CRUD در ContentResolver همان نامهای ارائهدهنده را دارند اما به جای ارجاع مستقیم، URI دریافت میکنند.
برای تجزیه URIهای ورودی در داخل ارائهدهنده از UriMatcher استفاده میشود. این امکان را میدهد که URI با یک کد عددی مطابقت داده شود — به عنوان مثال، URI content://authority/items کد 1 و content://authority/items/# کد 2 را میدهد. این کار نیاز به تجزیه دستی رشته URI در هر روش را از بین میبرد.
// استفاده از ContentResolver برای دسترسی به مخاطبین
val uri = ContactsContract.Contacts.CONTENT_URI
val cursor = contentResolver.query(
uri,
arrayOf(ContactsContract.Contacts.DISPLAY_NAME),
null, null, null
)
cursor?.use {
while (it.moveToNext()) {
val name = it.getString(it.getColumnIndexOrThrow(
ContactsContract.Contacts.DISPLAY_NAME
))
Log.d("مخاطبین", "نام: $name")
}
}
Cursor پس از استفاده باید حتماً بسته شود — در مثال بالا use (تابع توسعهای Kotlin) این کار را انجام میدهد. اگر Cursor بسته نشود، نشت حافظه رخ میدهد زیرا مرجعی به دادهها در حافظه Binder نگه میدارد. برای سناریوهای UI از CursorLoader یا Room با LiveData/Flow استفاده کنید.
ایجاد Content Provider شخصی خود با ارثبری از کلاس ContentProvider شروع میشود. ارائهدهنده با پایگاه داده SQLite از طریق SQLiteOpenHelper کار میکند و از UriMatcher برای تعیین نوع درخواست استفاده میکند. بیایید یک پیادهسازی حداقلی برای مدیریت لیست یادداشتها را بررسی کنیم.
ارائهدهنده در AndroidManifest.xml در داخل تگ application ثبت میشود. ویژگی authorities یک شناسه یکتا را تعیین میکند و exported مشخص میکند که آیا سایر برنامهها میتوانند به ارائهدهنده دسترسی داشته باشند. بدون exported=true ارائهدهنده فقط در داخل برنامه شما قابل دسترسی است.
// مثال Content Provider برای یادداشتها
class NotesProvider : ContentProvider() {
companion object {
const val AUTHORITY = "com.example.app.notes"
const val NOTES_PATH = "notes"
const val NOTES_URI = "content://$AUTHORITY/$NOTES_PATH"
const val NOTES_ID = "content://$AUTHORITY/$NOTES_PATH/#"
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, NOTES_PATH, 1)
addURI(AUTHORITY, "$NOTES_PATH/#", 2)
}
}
override fun query(uri: Uri, projection: Array<String>?,
selection: String?, args: Array<String>?, sort: String?): Cursor? {
return when (uriMatcher.match(uri)) {
1 -> dbHelper.readableDatabase.query(TABLE_NOTES,
projection, selection, args, null, null, sort)
2 -> dbHelper.readableDatabase.query(TABLE_NOTES,
projection, "_id=?", arrayOf(uri.lastPathSegment), null, null, null)
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
val id = dbHelper.writableDatabase.insert(TABLE_NOTES, null, values)
context?.contentResolver?.notifyChange(uri, null)
return ContentUris.withAppendedId(uri, id)
}
override fun delete(uri: Uri, selection: String?, args: Array<String>?): Int {
val count = dbHelper.writableDatabase.delete(TABLE_NOTES, selection, args)
context?.contentResolver?.notifyChange(uri, null)
return count
}
// getType, update, onCreate برای اختصار حذف شده است
}
پس از ایجاد کلاس، ارائهدهنده باید در مانیفست با ویژگیهای android:authorities و android:exported (true اگر ارائهدهنده عمومی است) ثبت شود. سیستم نمونه ارائهدهنده را در اولین مراجعه به آن ایجاد میکند — این در رشته UI رخ میدهد، بنابراین onCreate باید سریع اجرا شود.
Content Provider امکان مدیریت دسترسی به دادهها را در دو سطح فراهم میکند: مجوزهای خواندن و مجوزهای نوشتن. آنها در مانیفست با ویژگیهای android:readPermission و android:writePermission تنظیم میشوند. اگر برنامه مشتری مجوز مربوطه را نداشته باشد، سیستم فراخوانی را با SecurityException رد میکند.
Android از مجوزهای موقت از طریق پرچمهای FLAG_GRANT_READ_URI_PERMISSION و FLAG_GRANT_WRITE_URI_PERMISSION پشتیبانی میکند. این زمانی مفید است که برنامه URI یک فایل را از طریق Intent به برنامه دیگری ارسال میکند — دریافتکننده فقط به URI خاص برای مدت محدود دسترسی پیدا میکند. سیستم مجوز موقت را پس از اتمام کار برنامه دریافتکننده لغو میکند.
برای ارائهدهندگان سیستمی، Android نیاز به مشخص کردن مجوزهای خاص در مانیفست برنامه دارد. به عنوان مثال، برای دسترسی به مخاطبین READ_CONTACTS، برای تقویم — READ_CALENDAR نیاز است. از Android 6، این مجوزها در زمان اجرا درخواست میشوند، نه هنگام نصب.
سوالات متداول
Content Provider — کامپوننت Android است که یک رابط استاندارد برای تبادل داده بین برنامهها از طریق ContentResolver فراهم میکند. این کامپوننت روش ذخیرهسازی (SQLite، فایلها، شبکه) را انتزاع میکند و دسترسی امن به دادهها را با کنترل مجوزهای خواندن و نوشتن تضمین میکند.
Authority — یک رشته شناسه یکتای ارائهدهنده است که در AndroidManifest.xml مشخص میشود. این رشته بخش اول URI content://authority/path را تشکیل میدهد و توسط سیستم برای مسیریابی فراخوانیهای ContentResolver به ارائهدهنده مناسب استفاده میشود. Authority باید در بین تمام برنامههای روی دستگاه یکتا باشد.
UriMatcher URIها را با کدهای عددی مطابقت میدهد. شما الگوها را از طریق addURI اضافه میکنید، سپس match را برای دریافت کد URI ورودی فراخوانی میکنید. این امکان را در روشهای query، insert، update، delete فراهم میکند تا تعیین کنید کدام جدول یا رکورد درخواست شده است و عملیات مربوطه را روی پایگاه داده اجرا کنید.
بله، Cursor حتماً باید پس از استفاده بسته شود. اگر Cursor بسته نشود، نشت حافظه رخ میدهد زیرا مرجع Binder به دادههای ارائهدهنده را نگه میدارد. در Kotlin از تابع use برای بستن خودکار و در Java از try-with-resources یا فراخوانی cursor.close() در finally استفاده کنید.
Content Provider — کامپوننتی برای دسترسی بین برنامهای به دادهها است، در حالی که SQLiteDatabase مخزن داخلی یک برنامه است. Content Provider رابط URI و کنترل مجوز را فراهم میکند، در حالی که SQLiteDatabase مستقیماً با پایگاه داده بدون مکانیزمهای امنیتی در سطح سیستمعامل کار میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید