建立內容供應器

內容供應器會管理資料中央存放區的存取權。而是實作 做為 Android 應用程式中的一或多個類別,以及 資訊清單檔案中您的其中一個類別會實作 ContentProvider,這是供應商和 或其他應用程式。

雖然內容供應者 應用程式中的活動可讓使用者 查詢並修改供應商管理的資料。

本頁說明建立內容供應器和清單的基本程序 要使用的 API

開始建構前的注意事項

開始建立供應器前,請考慮下列事項:

  • 決定是否需要內容供應者。您需要製作 提供下列一或多項功能:
    • 您想要為其他應用程式提供複雜的資料或檔案。
    • 您想讓使用者將複雜的資料從自家應用程式複製到其他應用程式。
    • 您想要使用搜尋架構提供自訂搜尋建議。
    • 您想向小工具公開應用程式資料。
    • 您想要實作 AbstractThreadedSyncAdapter CursorAdapterCursorLoader 類別

    您「不需要」供應商,就能使用資料庫或其他類型的 永久儲存空間 (如果用途完全在您的應用程式中) 而且你不需要有前述任何功能不過,您可以 我們會使用 kubectl 指令 資料與檔案儲存空間總覽

  • 如果您還沒有這樣做,請參閱 內容供應器基礎知識,進一步瞭解供應商及其運作方式。

接下來,請按照下列步驟建立提供者:

  1. 為資料設計原始儲存空間。內容供應器以兩種方式提供資料:
    檔案資料
    通常會發生在檔案中的資料,例如 相片、音訊或影片將檔案儲存在應用程式的非公開 空白鍵。為了回應來自其他應用程式的檔案要求,您的 供應商可提供檔案的處理常式。
    「結構化」資料
    通常會流入資料庫、陣列或類似結構的資料。 將資料儲存成與由列和欄相容的格式。單列 代表實體,例如使用者或商品目錄中的商品。資料欄則代表 或實體的部分資料,例如個人姓名或商品價格。將容器 會將這類資料儲存在 SQLite 資料庫中,但您可以使用任何類型的 永久儲存空間如要進一步瞭解 Android 系統,請參閱 設計資料儲存空間一節。
  2. 定義 ContentProvider 類別的具體實作並 所需的方法這個類別是您的資料與其餘部分之間的介面 Android 系統。如要進一步瞭解這個課程,請參閱 實作 ContentProvider 類別區段。
  3. 定義供應器的授權字串、內容 URI 和資料欄名稱。如果您希望 供應意圖的應用程式,同時定義意圖動作、額外資料 和旗標此外,您也可以定義應用程式所需的權限 存取資料。您不妨考慮在 獨立的合約類別您之後可以將這個類別公開給其他開發人員。如要 內容 URI 相關資訊,請參閱 設計內容 URI 一節。 如要進一步瞭解意圖,請參閱 意圖和資料存取權」一節。
  4. 新增其他選用部分,例如範例資料或實作 的 AbstractThreadedSyncAdapter 中,可同步處理 和雲端資料

設計資料儲存空間

內容供應器是指以結構化格式儲存的資料介面。建立前的準備 介面,決定資料儲存方式。您可以使用任何形式儲存的資料 然後設計可讀取及寫入資料的介面。

以下是 Android 可用的一些資料儲存技術:

  • 處理結構化資料時,請考慮使用關聯資料庫 例如 SQLite 或非關聯鍵/值資料儲存庫,例如 LevelDB。你在工作 用於音訊、圖片或影片媒體等非結構化資料 將檔案視為檔案您可以組合及比對不同類型的儲存空間 使用單一內容供應器
  • Android 系統可以與 Room 持續性程式庫互動, 可讓您存取 Android 供應商的 SQLite 資料庫 API 用於儲存資料表導向資料如要建立資料庫 將叢集的子類別執行個體化 RoomDatabase,如 使用 Room 將資料儲存在本機資料庫

    您不需要使用資料庫導入存放區。提供者 會以一組資料表的形式對外呈現,這類似於關聯資料庫 並非提供者內部實作的要求。

  • 如要儲存檔案資料,Android 提供多種檔案導向 API。 如要進一步瞭解檔案儲存空間,請參閱 資料與檔案儲存空間總覽。如果您是 設計提供媒體相關資料 (例如音樂或影片) 的供應商,您可以 可提供結合資料表資料和檔案的供應器
  • 在極少數的情況下,建議您為 單一應用程式舉例來說,若要與小工具分享部分資料,您可以使用 一個內容供應器,並公開另一組資料來與他人分享 應用程式。
  • 如要處理網路資料,請使用 java.net 中的類別和 android.net。您也可以將網路資料與本機資料同步 儲存資料,然後將資料以資料表或檔案的形式提供。

注意:如果您變更了存放區 回溯相容,您需要將存放區標示為新版本 號碼。您也必須增加應用程式的版本號碼 實作新的內容供應器進行這項變更可避免系統 導致系統在嘗試重新安裝 含有不相容的內容供應器的應用程式。

資料設計注意事項

以下為設計供應商資料結構的一些提示:

  • 資料表資料一律須有「主鍵」可以由供應商維護的 做為每個資料列的不重複數值這個值可用於將資料列連結至相關的 寫入其他資料表中的資料列 (當做「外部鍵」)。雖然您可以使用任何名稱 就這個資料欄而言,使用 BaseColumns._ID 是最好 因為您可以將供應商查詢結果連結到 「ListView」要求其中一個擷取的資料欄名稱 _ID
  • 如要提供點陣圖圖片或其他非常大的檔案導向資料,請儲存 然後間接提供資料,而不要直接將資料儲存在 表格。在此情況下,您必須告知供應商使用者,他們必須使用 存取資料的 ContentResolver 檔案方法。
  • 使用二進位大型物件 (BLOB) 資料類型,儲存大小各異或具有 設計不同的結構舉例來說,您可以使用 BLOB 資料欄儲存 通訊協定緩衝區JSON 結構

    您也可以使用 BLOB 實作獨立結構定義的資料表。於 您需要定義主鍵資料欄、MIME 類型資料欄,以及 較一般資料欄做為 BLOB。BLOB 資料欄中資料的意義 。這樣就能將不同資料列類型 同一個表格。聯絡人提供者的「資料」桌子 ContactsContract.Data 是與結構定義無關的示例 表格。

設計內容 URI

內容 URI 是用來識別供應器中資料的 URI。內容 URI 包含 整個提供者的符號名稱 (其主管機關),以及 名稱指向資料表或檔案 (路徑)。選用的 ID 部分指向 資料表中的個別資料列每種資料存取方法 ContentProvider 具有內容 URI 做為引數。這樣一來, 決定要存取的資料表、資料列或檔案。

如需內容 URI 的相關資訊,請參閱 內容供應器基礎知識

設計權威人士

提供者通常設有單一授權,也就是其 Android 內部名稱。目的地: 避免與其他供應商發生衝突,請善用網際網路網域擁有權 (反向操作) 做為供應商授權的基礎因為這項建議也適用於 Android 套件名稱,您可以將供應商授權定義為名稱的延伸 。

舉例來說,如果您的 Android 套件名稱是 com.example.<appname>,請將 授權單位:com.example.<appname>.provider

設計路徑結構

開發人員通常會透過附加指向 和個別資料表一樣舉例來說,如果您有兩個資料表:table1table2,您可以將這些值與上一個範例的授權結合,藉此產生 內容 URI com.example.<appname>.provider/table1com.example.<appname>.provider/table2。路徑無法 (僅限一個區隔) 且不必為每個路徑層級建立表格。

處理內容 URI ID

按照慣例,供應商接受內容 URI 後,就能存取表格中的單一資料列 並將資料列的 ID 值置於 URI 末端。此外,按照慣例,供應商將 ID 值新增到資料表的「_ID」資料欄,並針對 比對相符的資料列。

此慣例對於存取供應器的應用程式,有助於採用常見的設計模式。應用程式 對供應器執行查詢,並顯示產生的 CursorListView 中使用 CursorAdapter。 如要定義 CursorAdapter,您必須使用「 將 Cursor變更為_ID

然後從 UI 中選取任一列,以查看或修改 資料。應用程式會從備份資料的 Cursor 中取得對應的資料列 ListView,取得此列的 _ID 值,附加在 ,並將存取要求傳送給供應器。供應商接著就能 查詢或修改使用者所選的資料列。

內容 URI 模式

為協助您針對傳入的內容 URI 選擇要採取的動作,供應器 API 包含 便利類別 UriMatcher,可將內容 URI 模式對應至 整數值。您可以在 switch 陳述式中使用整數值: 針對符合特定模式的內容 URI 或 URI 選擇所需的動作。

內容 URI 模式會使用萬用字元來比對內容 URI:

  • * 符合任何長度的任何有效字元字串。
  • # 會比對任何長度的數值字元字串。

如要設計並編寫內容 URI 處理功能,請考慮採用 可辨識下列內容 URI 的主機名稱 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 的資料表。

如果提供者在內容 URI 附加資料列 ID,例如 content://com.example.app.provider/table3/1 代表所識別的資料列 「table3」的「1」。

可能的內容 URI 模式如下:

content://com.example.app.provider/*
比對供應器中的所有內容 URI。
content://com.example.app.provider/table2/*
比對 dataset1 資料表的內容 URI 和 dataset2,但與 table1table3
content://com.example.app.provider/table3/#
與內容 URI 相符 則針對 table3 中的單一資料列,例如 代表資料列:content://com.example.app.provider/table3/6 6

下列程式碼片段顯示 UriMatcher 中的方法運作方式。 這個程式碼會以不同方式處理整份資料表的 URI,與 URI 的 URI 不同 顯示一個資料列 content://<authority>/<path>:資料表和 content://<authority>/<path>/<id> 代表單一資料列。

addURI() 方法會將 整數值的授權和路徑match() 方法會傳回 URI 的整數值。switch 陳述式 可以選擇查詢整個資料表或查詢一筆記錄。

Kotlin

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
    }
}

Java

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) 提供便利的方法 內容 URI 的 id 部分UriUri.Builder 包含剖析現有資料的便利方法 Uri 物件及建立新物件。

實作 ContentProvider 類別

ContentProvider 執行個體會管理存取權 處理來自其他應用程式的要求,以處理結構化資料集。所有表單 存取要求最終呼叫 ContentResolver,然後呼叫 取得存取權的 ContentProvider 方法。

必要方法

抽象類別 ContentProvider 定義了六個抽象方法 實作為具體子類別的一部分上述所有方法,但 onCreate() 是由用戶端應用程式呼叫 嘗試存取的內容供應器

query()
向供應商擷取資料。使用引數選取資料表 要傳回的資料列與資料欄,以及結果的排序順序。 將資料以 Cursor 物件的形式傳回。
insert()
在供應器中插入新資料列。使用引數選取 並取得要使用的資料欄值針對 新插入的資料列
update()
更新供應器中的現有資料列。使用引數選取資料表和資料列 更新並取得更新後的資料欄值。傳回更新後的列數。
delete()
請從提供者中刪除資料列。使用引數選取資料表和要選取的資料列 刪除。傳回已刪除的列數。
getType()
傳回與內容 URI 對應的 MIME 類型。此方法會在 詳情可參閱「實作內容供應器 MIME 類型」一節。
onCreate()
初始化供應器。Android 系統會在發生此情況後立即呼叫此方法 建立提供者。只有在 ContentResolver 物件會嘗試存取該物件。

這些方法的簽章與名稱相同的 ContentResolver 方法。

導入這些方法時,必須考量下列事項:

  • 這些方法 (onCreate() 除外) 可以同時由多個執行緒呼叫,因此必須使用執行緒安全的執行緒。學習 如要進一步瞭解多個執行緒,請參閱 程序和執行緒總覽
  • 請避免在 onCreate() 中執行耗時較長的作業。將初始化工作延後到實際需要時再執行。 請參閱實作 onCreate() 方法一節 我們會詳細說明這一點
  • 雖然您必須實作這些方法,但您的程式碼不需執行任何操作, 傳回預期的資料類型舉例來說 您可以忽略呼叫 insert() 並傳回 0.

實作 query() 方法

ContentProvider.query() 方法必須傳回 Cursor 物件;如果是的話則傳回 失敗,請擲回 Exception。如果使用 SQLite 資料庫做為資料 儲存空間則可傳回Cursor SQLiteDatabase 類別的 query() 方法。

如果查詢沒有任何相符的資料列,就會傳回 Cursor getCount() 方法傳回 0 的執行個體。 只有在查詢程序期間發生內部錯誤時,才會傳回 null

如果您並未使用 SQLite 資料庫做為資料儲存空間,請使用其中一個具體子類別 (共 Cursor 個)。例如 MatrixCursor 類別 實作一個遊標,其中每個資料列都是 Object 例項的陣列。有了本課程 使用 addRow() 新增資料列。

Android 系統必須能夠將 Exception 跨越程序界限。Android 可以處理以下幾個實用的例外狀況: 處理查詢錯誤:

實作 insert() 方法

insert() 方法會新增 將新資料列加入適當表格,並使用 ContentValues 中的值 引數。如果 ContentValues 引數中沒有資料欄名稱,則 可以在您的供應商程式碼或資料庫內,提供預設值 結構定義。

這個方法會傳回新資料列的內容 URI。如要建立這個結構,請將新的 資料列的主鍵,通常是 _ID 值至資料表的內容 URI,使用 withAppendedId()

實作 delete() 方法

delete() 方法 不必從資料儲存空間刪除資料列如果您使用同步轉換介面 與您的供應商互動,請考慮將已刪除的資料列標示為已刪除 具有「delete」狀態標記,而不必將資料列完全移除。同步轉換介面可以 檢查是否有已刪除的資料列,並將其從伺服器中移除,再將這些資料列從供應器中刪除。

實作 update() 方法

update() 方法使用與ContentValues insert()和 相同的 selectionselectionArgs 引數 delete()ContentProvider.query()。 這樣或許就能在這些方法之間重複使用程式碼。

實作 onCreate() 方法

Android 系統會呼叫 onCreate() 啟動供應器時只執行快速執行初始化 並延後建立資料庫及載入資料,直到供應器實際運作為止 接收資料的要求。如果在大型語言模型 onCreate(),您的 啟動條件這麼做可以將供應器的回應速度放慢給 應用程式。

以下兩個程式碼片段示範了 「ContentProvider.onCreate()」和 Room.databaseBuilder()。第一個 程式碼片段顯示 ContentProvider.onCreate(),其中 並建立資料存取物件的處理程序:

Kotlin

// 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.
    }
}

Java

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.
    }
}

實作 ContentProvider MIME 類型

ContentProvider 類別提供兩種傳回 MIME 類型的方法:

getType()
您為任何供應商實作的其中一種必要方法。
getStreamTypes()
如果供應商有提供檔案,您應實作的檔案方法。

資料表的 MIME 類型

getType() 方法會傳回 MIME 格式的 String,說明內容傳回的資料類型 URI 引數。Uri 引數可以是模式,而非特定 URI。 在此情況下,傳回與 。

對於常見的資料類型,例如文字、HTML 或 JPEG getType() 會傳回標準 該資料的 MIME 類型。如需這些標準類型的完整清單,請前往 IANA MIME 媒體類型 網站。

如果是指向表格資料列或資料列的內容 URI 可退貨 (費用:getType()) Android 供應商專用 MIME 格式的 MIME 類型:

  • 輸入部分:vnd
  • 子類型部分:
    • 如果 URI 模式適用於單一資料列:android.cursor.item/
    • 如果 URI 模式含有多列資料:android.cursor.dir/
  • 供應商特定部分:vnd.<name><type>

    您需要提供 <name><type><name> 值在全域範圍內不重複。 且 <type> 值是對應的 URI 的專屬值 。<name> 的理想選擇是貴公司名稱,或 應用程式的 Android 套件名稱的一部分以負責任的方式 <type> 字串可識別與 URI。

例如,如果供應商的授權為 com.example.app.provider,而且會顯示名為 table1table1 中多列的 MIME 類型為:

vnd.android.cursor.dir/vnd.com.example.provider.table1

針對 table1 的單一資料列,MIME 類型為:

vnd.android.cursor.item/vnd.com.example.provider.table1

檔案的 MIME 類型

如果供應商提供檔案,請將檔案 getStreamTypes()。 這個方法會針對您的供應器檔案傳回 String 類型的 MIME 類型 就可以針對指定的內容 URI 傳回內容依 MIME 類型篩選您提供的 MIME 類型 篩選引數,只傳回用戶端需要處理的 MIME 類型。

舉例來說,有一家提供相片圖片做為 JPG 檔的供應商。 PNG 和 GIF 格式。 如果應用程式使用篩選器字串 image/* 呼叫 ContentResolver.getStreamTypes(), 是「圖片」 則 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 個資料庫不會公開 應用程式和供應商
  • 根據預設,您儲存至外部儲存空間的資料檔案為公開狀態, 全球可讀取。您無法使用內容供應器限制檔案的存取權 外部儲存空間,因為其他應用程式可以使用其他 API 呼叫來讀取及寫入這些呼叫。
  • 這個方法會呼叫在裝置的內部,開啟或建立檔案或 SQLite 資料庫 儲存空間可能同時授予所有其他應用程式的讀取和寫入權限。如果發生以下情況: 使用內部檔案或資料庫做為供應商的存放區,而您則會提供 「全世界可讀取」或「world-writeable」存取權,您在該平台中為供應商設定的權限 資訊清單無法保護您的資料。檔案與資料庫的預設存取權 內部儲存空間為「私人」請將此項目變更為您供應商的存放區。

如要使用內容供應器的權限控管資料存取權,則 將資料儲存在內部檔案、SQLite 資料庫或雲端 ,而且檔案和資料庫不對外公開。

實作權限

根據預設,即使基礎資料是 私人狀態,因為根據預設,您的供應商並未設定權限。如要變更 使用屬性或子元素,在資訊清單檔案中設定提供者的權限 <provider> 元素的每個元素。您可以設定適用於整個供應商的權限 特定資料表、特定記錄或全部三個項目

您可以定義供應器的一或多個權限 <permission> 元素。為了讓 專屬權限,請使用 Java 式範圍的 android:name 屬性。例如為讀取權限命名 com.example.app.provider.permission.READ_PROVIDER

以下清單說明供應商權限的範圍,從 適用於整個供應商的權限,進而更精細地控管權限。 較精細的權限的優先順序高於範圍較大的權限。

單一讀取/寫入提供者層級權限
一項權限可控管整個供應器的讀取和寫入權限,已指定 其中 android:permission 屬性是 <provider> 元素。
分開讀取和寫入供應商層級權限
整個供應器的讀取權限和寫入權限。由您指定 使用 android:readPermission android:writePermission 屬性的 <provider> 元素。其優先順序高於 android:permission
路徑層級權限
讀取、寫入或讀取/寫入供應器中的內容 URI 權限。由您指定 要透過 Google Kubernetes Engine 符記的 <path-permission> 個子元素 <provider> 元素。您可以指定每個指定的內容 URI 讀取/寫入權限、讀取權限、寫入權限或全部三種。讀取和 寫入權限的優先順序高於讀取/寫入權限。此外,路徑層級 權限的優先順序高於供應商層級權限。
臨時權限
授予應用程式暫時存取權的權限等級 (即使應用程式) 但不具備一般需要的權限臨時 存取權功能可減少應用程式必須要求的權限數量 資訊清單。當您開啟臨時權限後,只會有需要的應用程式 永久權限是指能持續存取 資料。

舉例來說,如果您實作電子郵件服務供應商和應用程式,且 您想要讓外部影像檢視器應用程式顯示來自 。如要在不要求權限的情況下授予圖片檢視器必要的存取權, 您可以為相片的內容 URI 設定暫時權限。

設計電子郵件應用程式 當使用者想要顯示相片時,應用程式就會傳送包含 相片的內容 URI 和權限標記給圖片檢視器圖片檢視器 然後,向您的電子郵件供應商查詢擷取相片 (即使檢視器並未 具有供應器的一般讀取權限。

如要啟用臨時權限,請 android:grantUriPermissions 屬性 <provider> 元素或新增一或多個 包含 <grant-uri-permission> 個子元素 <provider> 元素。致電 Context.revokeUriPermission() (每當您從應用程式移除與臨時權限相關聯的內容 URI 支援時), 。

屬性值會決定系統是否能存取你的供應商。 如果屬性設為 "true",系統會授予暫時權限 授予整個供應商的權限,並覆寫任何其他必要的權限 替換為您的供應商層級或路徑層級權限

如果此標記設為 "false",則 包含 <grant-uri-permission> 個子元素 <provider> 元素。每個子元素都會指定內容 URI 或 已授予臨時存取權的 URI。

如要將暫時存取權委派給應用程式,意圖必須包含 FLAG_GRANT_READ_URI_PERMISSION 標記,則 FLAG_GRANT_WRITE_URI_PERMISSION 旗標或兩者皆有。這些 是透過 setFlags() 方法設定。

如果沒有 android:grantUriPermissions 屬性,系統會假設為 "false"

<provider>元素

例如 ActivityService 元件 ContentProvider 的子類別 定義於其應用程式的資訊清單檔案中, <provider> 元素。Android 系統會從 元素:

權威推薦 (android:authorities)。
用於在系統內識別整個供應商的符號名稱。這個 屬性的詳細說明請參閱 設計內容 URI 一節。
提供者類別名稱 (android:name)
實作 ContentProvider 的類別。這個課程 當中的 實作 ContentProvider 類別區段。
權限
指定其他應用程式存取所需的權限的屬性 供應商的資料:

詳細說明 詳細資料 「實作內容供應器權限」一節。

啟動與控制屬性
這些屬性會決定 Android 系統啟動供應器的方式和時間, 供應商的程序特性以及其他執行階段設定:

如需這些屬性的完整說明,請參閱 <provider> 元素。

資訊屬性
供應商的選用圖示和標籤:
  • android:icon:可繪製資源,包含供應器的圖示。 圖示會顯示在 Google Play 應用程式清單內的供應商標籤旁 「設定」>應用程式 >全部
  • android:label:說明供應商、其資訊標籤 或兩者並用標籤會顯示在以下位置的應用程式清單中: 「設定」>應用程式 >全部

如需這些屬性的完整說明,請參閱 <provider> 元素。

注意:如果您指定 Android 11 以上版本,請參閱 套件瀏覽權限說明文件 以便瞭解其他設定需求

意圖和資料存取權

應用程式可以透過 Intent 間接存取內容供應器。 應用程式不會呼叫 ContentResolver 的任何方法,或 ContentProvider。相反地,它會傳送啟動活動的意圖 通常是供應器自己的應用程式的一部分該目標活動負責 資料擷取並在其 UI 中顯示。

視意圖中的動作而定 目的地活動也可以提示使用者修改供應器的資料。 意圖也可能包含「額外項目」目標活動顯示的資料 調整過渡期因此,使用者可在使用相關資料來修改 資料。

您可以使用意圖存取權協助資料完整性。供應商可能會使用 ,根據明確定義的商業邏輯插入、更新及刪除資料。如果 發生這種情況時,讓其他應用程式直接修改您的資料, 無效的資料。

如要讓開發人員使用意圖存取權,請務必詳加說明。 說明為何使用應用程式 UI 比嘗試目的更好 用來修改資料與程式碼

處理想要修改供應器資料的傳入意圖也無妨 處理其他意圖如要進一步瞭解如何使用意圖,請參閱 意圖和意圖篩選器

如需其他相關資訊,請參閱 日曆供應程式總覽