Content Provider คือคอมโพเนนต์ของ Android ที่ให้อินเทอร์เฟซแบบรวมศูนย์สำหรับการเข้าถึงข้อมูลระหว่างแอปพลิเคชัน มันทำการซ่อนรายละเอียดการจัดเก็บจริง (SQLite, ไฟล์, แหล่งข้อมูลเครือข่าย) และเปิดให้มีการแลกเปลี่ยนข้อมูลอย่างปลอดภัยผ่าน ContentResolver จาก Android Developer Guide, 2026, Content Provider เป็นหนึ่งในสี่คอมโพเนนต์หลักของแอปพลิเคชัน Android ร่วมกับ Activity, Service และ BroadcastReceiver หน้าที่ของมันคือทำให้ข้อมูลพร้อมใช้งานสำหรับแอปพลิเคชันอื่นด้วยการควบคุมสิทธิ์การอ่านและการเขียน
ประเด็นสำคัญ
Content Provider คือคอมโพเนนต์ Android ที่จัดการการเข้าถึงพื้นที่เก็บข้อมูลแบบรวมศูนย์และให้ข้อมูลแก่แอปพลิเคชันอื่นผ่านอินเทอร์เฟซสัญญาแบบรวมศูนย์ มันซ่อนรายละเอียดการนำพื้นที่จัดเก็บไปใช้: ข้อมูลสามารถเก็บใน SQLite, ในระบบไฟล์, ในคลาวด์ หรือเป็นผลลัพธ์ของการร้องขอเครือข่าย
Android มี Content Providers ในตัวสำหรับข้อมูลระบบ — 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 และส่งคืนผลลัพธ์ที่สอดคล้องกับประเภทการดำเนินการ ระบบเรียกเมธอดเหล่านี้จากกระบวนการใดก็ได้ ดังนั้นจึงต้องปลอดภัยต่อเธรดและไม่บล็อกการทำงานเป็นเวลานาน
เมธอด 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 Providers ทั้งหมดในระบบ แอปพลิเคชันไคลเอนต์ไม่เคยเรียกเมธอดผู้ให้บริการโดยตรง — เฉพาะผ่าน ContentResolver ซึ่ง Android ได้รับจากบริบท การดำเนินการ 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 สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม