在 Android 系統中,應用程式之間通常彼此隔離,預設情況下無法直接存取對方的資料。
但有時候,應用之間需要安全且有規範地共享資料,例如:
為了解決這個跨應用資料存取的需求,Android 提供了 ContentProvider 機制。
ContentProvider 是 Android 四大元件之一,負責「封裝資料並提供統一介面進行 CRUD 操作(Create、Read、Update、Delete)」。
它讓不同應用之間能透過 URI(Uniform Resource Identifier) 安全地交換資料。
ContentProvider 位於 Android Framework 的 Application 層與 Binder IPC 橋接層之間,屬於 應用層資料共享框架。
+-----------------------------------------------------+
| Application 層 |
| (App A) (App B) |
| ┌─────────────────┐ ┌─────────────────────┐ |
| | ContentProvider│ | ContentResolver │ |
| └────────┬────────┘ └────────┬────────────┘ |
| │ │ |
| ▼ ▼ |
| <------ Binder IPC 通信層 ------> |
| (ActivityManagerService 等) |
+-----------------------------------------------------+
| Native 層 & Kernel |
+-----------------------------------------------------+
元件 | 說明 |
---|---|
ContentProvider | 定義資料表結構、URI、以及 CRUD 操作邏輯。 |
ContentResolver | 提供統一的 API 讓其他 App 查詢資料(query、insert、update、delete)。 |
UriMatcher | 用來解析傳入的 URI 並對應到具體的資料操作。 |
ContentObserver | 觀察資料變化事件(例如資料庫更新時通知 UI)。 |
Permission 管控 | 透過 AndroidManifest.xml 指定 android:readPermission、android:writePermission 進行授權控制。 |
###URI 與資料定位
ContentProvider 使用 URI 來標識資料來源。
URI 格式如下:content://<authority>/<path>/<id>
例如:content://com.android.contacts/contacts/5
代表:
(1) 啟動流程概觀
App B AMS App A
│ query() │ │
│ ───────────────────────────▶│ │
│ │──檢查Provider是否已啟動──▶
│ │ │──建立Provider實例──▶
│ │◀────註冊Provider Binder──│
│◀────────返回Proxy────────────│ │
│──透過Binder呼叫query()──────▶│ │
│◀────────回傳Cursor───────────│ │
ContentProvider 定義五個核心介面方法:
方法 | 功能 | 範例 |
---|---|---|
query() | 查詢資料 | resolver.query(uri, projection, ...) |
insert() | 新增資料 | resolver.insert(uri, values) |
update() | 更新資料 | resolver.update(uri, values, ...) |
delete() | 刪除資料 | resolver.delete(uri, ...) |
getType() | 回傳 MIME 類型 | vnd.android.cursor.dir/vnd.<authority>.<path> |
Ex. 查詢聯絡人
val uri = Uri.parse("content://com.android.contacts/contacts")
val cursor = contentResolver.query(uri, null, null, null, null)
while (cursor?.moveToNext() == true) {
val name = cursor.getString(cursor.getColumnIndex("display_name"))
Log.d("Contact", name)
}
cursor?.close()
為了防止惡意 App 存取敏感資料,ContentProvider 提供多層安全防護:
<provider
android:name=".ContactsProvider"
android:authorities="com.example.contacts"
android:readPermission="com.example.permission.READ_CONTACTS"
android:writePermission="com.example.permission.WRITE_CONTACTS" />
當資料透過 ContentProvider 更新時,可通知相關 UI 或服務更新畫面。
contentResolver.registerContentObserver(
Uri.parse("content://com.example.provider/items"),
true,
object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
Log.d("Observer", "資料已更新!")
}
}
)
當 insert()、update()、或 delete() 被呼叫後,可觸發:context.contentResolver.notifyChange(uri, null)
雖然 ContentProvider 常以 SQLite 資料庫為底層存儲介質,但它並不限定必須使用 SQLite。
底層可對應:
Provider 名稱 | Authority | 功能 |
---|---|---|
ContactsProvider | com.android.contacts | 聯絡人資料 |
MediaStore | media | 媒體檔案 (照片、影片、音訊) |
SettingsProvider | settings | 系統設定項目 |
DownloadsProvider | downloads | 檔案下載紀錄 |
class NoteProvider : ContentProvider() {
override fun onCreate(): Boolean {
dbHelper = NotesDbHelper(context)
return true
}
override fun query(...) = db.query("notes", ...)
override fun insert(...) = db.insert("notes", null, values)
override fun update(...) = db.update("notes", values, where, args)
override fun delete(...) = db.delete("notes", where, args)
}
ContentProvider 不僅是 App 之間共享資料的介面,也被 Android Framework 廣泛使用:
特性 | 說明 |
---|---|
主要功能 | 在不同應用間安全共享資料 |
通訊機制 | 透過 Binder IPC |
資料識別 | 使用 content URI |
權限控制 | 支援 read/write 權限與臨時授權 |
常見用途 | 聯絡人、媒體庫、設定、下載紀錄等 |
替代技術 | 若僅限 App 內部共享,可使用 Room、Repository、或 Jetpack DataStore |
+-----------------+ +----------------------+
| App A (Provider) | | App B (Client) |
| ┌───────────────┐ | | ┌───────────────┐ |
| | ContentProvider│◄──────────┤ | ContentResolver│ |
| └──────┬────────┘ | | └───────────────┘ |
| │ | | |
| SQLite/Files/API | | |
+---------------------+ +----------------------+
▲
│
notifyChange() → ContentObserver
ContentProvider 是跨應用資料共享的核心機制