讀古今文學網 > Android程序設計:第2版 > 定義提供者的公用API >

定義提供者的公用API

雖然在第3章已經就客戶端如何使用內容提供者進行了簡單介紹,在這裡我們仍將詳細介紹實現提供者的公用API的過程。對於要使用你實現的內容提供者的客戶端,需要創建一個公用API類,它包含一組常量,用戶可以使用這些常量來訪問提供者的查詢方法所返回的Cursor對象的字段。該類還定義了內容提供者的授權URI(authority URI),它是整個提供者URI通信機制的基礎。在本節中,FinchVideo.SimpleVideos類提供了使用SimpleFinchVideo的API。

下面將逐步剖析這個類,對其各個字段進行說明,然後再介紹整個列表。

定義CONTENT_URI

客戶端應用要查詢內容提供者數據時,需要傳遞一個URI,用以標識Android內容解析程序要訪問的數據。這些方法包括query、insert、update和delete,與之對應的是在P330「編寫並集成內容提供者」一節中定義的內容解析程序的方法。在接收這種調用時,內容解析程序會使用authority字符串匹配輸入的URI,每個內容提供者的CONTENT_URI會為客戶端找到正確的提供者。因此,CONTENT_URI定義了內容提供者可以處理的URI類型。

一條CONTENT_URI包含以下部分:

content://

該前綴告訴Android框架,讓其查找一個內容提供者來解析這個URI。

authority

該字符串唯一標識內容提供者,包含兩個部分:組織部分和提供者標識部分。組織部分唯一標識創建該內容提供者的組織,提供者部分標識了這個特定的內容提供者。集成在Android中的內容提供者,略去了組織部分。Android中內置的media authority,返回的是沒有包含authority的組織部分的一個或多個圖像。任何非Google的Android團隊所開發的內容提供者都必須同時定義內容提供者的兩個部分。因此,對於簡單的Finch視頻示例應用,其authority是com.oreilly.demo.pa.finchvideo.SimpleFinchVideo,其中組織部分是com.oreilly.demo.pa.finchvideo,provider標識是SimpleFinchVideo。Google文檔建議CONTENT_URI的authority部分最好是使用實現該內容提供者類的完整的限定修飾符。

Authority可以唯一標識出Android用以響應查詢的專用的內容提供者。

path

內容提供者可以隨意解釋URI的其他部分,但是它必須遵循以下要求:

·如果內容提供者可以返回多種數據類型,則URI的path的某些部分必須能夠指定要返回的數據類型。

·例如,內置的內容提供者「聯繫人」提供了很多種數據類型:people、phones、contact methods等。聯繫人內容提供者使用URI的字符串來區分用戶使用了哪一種數據類型。因此,請求特定person的URI如下所示:


content://contacts/people/1
  

·請求特定電話號碼的URI如下所示:


content://contacts/people/1/phone/3
  

·在第一個例子中,返回的MIME數據類型會是vnd.android.cursor.item/person,而第二個例子的MIME數據類型是vnd.android.cursor.item/phone。

·內容提供者必須能夠返回單個或一組標識符。當某個標識符出現在URI的最後部分時,內容提供者會返回一項。回顧之前的示例,URI content://contacts/people/1/phone/3返回一個電話號碼,其類型是vnd.android.cursor.item/phone。如果URI的類型是content://contacts/people/1/phone,則應用會返回標識符為1號的人的所有電話號碼,返回的數據的MIME類型則變為vnd.android.cursor.dir/phone。

正如前面所述,內容提供者可以解釋URI的路徑部分以滿足需求。這意味著路徑部分可以使用路徑中的項過濾返回給調用函數的數據。例如,內建的「媒體」內容提供者可以返回內部數據或外部數據,它取決於路徑中的URI中包含的是internal還是external。

簡單Finch視頻的完整CONTENT_URI是:content://com.oreilly.demo.pa.finchvideo.SimpleFinchVideo/video。

CONTENT_URI必須是public static final類型的。它在簡單視頻應用的Finch Video類中定義。在公共API類中,可以擴展類的BaseColumns,然後定義名為AUTHORITY的字符串:


public final class FinchVideo.SimpleVideos extends BaseColumns {
    public static final String SIMPLE_AUTHORITY =
      \"com.oreilly.demo.pa.finchvideo.FinchVideo\";
  

然後,定義自己的CONTENT_URI:


public static final class FinchVideo.SimpleVideos implements BaseColumns {
    public static final Uri CONTENT_URI =
      Uri.parse(\"content://\" + AUTHORITY + \"/video\");
  

更簡單地說,定義該URI只需要選取一個authority字符串,它應該使用應用中所使用的Java包作為組織標識符——使用公共的API包可能比從頭實現一個包要好一些,正如我們在P58「Java包」一節中所討論的。內容提供者標識符只是內容提供者類的名稱。簡單Finch視頻提供者的URI看起來如下所示:


\"content://\" + FinchVideo.FinchVideoContentProvider.SIMPLE_AUTHORITY + \"/\"   +            FinchVideo.SimpleVideos.VIDEO
  

創建字段名稱

內容提供者和客戶端交換數據的方式類似於SQL數據庫和數據庫應用之間的交換方式:使用能夠表示整條記錄及其字段數據的游標。內容提供者必須定義其支持的字段名稱,正如數據庫應用必須定義其支持的字段名稱。當內容提供者使用SQLite數據庫作為數據存儲時,最常見的解決方案是讓內容提供者的字段和數據庫的字段保持一致,SimpleFinchVideoContentProvider採用的就是這種方式。因此,SimpleFinchVideoContentProvider字段和底層的數據庫字段之間就不需要額外的映射了。

注意:有些應用不希望它的全部數據都能夠被所有客戶端的內容提供者所訪問,有些複雜的應用可能希望客戶端訪問其派生出的數據視圖。P335「.SimpleFinch VideoContent Provider類和實例變量」一節中描述的映射方式可以用於處理這種複雜的情況。

聲明字段規範字符串

本節要探討的FinchVideo.SimpleVideos類定義了字段SimpleFinchVideoProvider。每個內容提供者必須定義_id字段來保存每條記錄的記錄號。每個_id值在內容提供者範圍內必須是唯一的。當客戶端想要查詢一條記錄時,會把這個記錄的_id傳遞給內容提供者的vnd.android.cursor.item URI。

如果內容提供者的數據與SimpleFinchVideoProvider一樣也是保存在SQLite數據庫中,那麼其_id字段的類型應該是INTEGER PRIMARY KEY AUTOINCREMENT。在這種方式中,記錄有唯一的_id號,而且該_id號不會被重用,即使該條記錄被刪除了。通過確保每條記錄都有一個_id號而且這個_id號不會被重用,可以支持參考完整性。如果記錄的_id號被重用,就可能會導致緩存的URI指向了錯誤的數據。

以下是簡單的Finch視頻提供者API,即FinchVideo.SimpleVideos類的完整代碼。注意,這裡只給出了需要的常量。沒有定義內容提供者實現的所有常量,因為它們對於客戶端是沒有用的,可能會導致客戶端使用特定的內容提供者實現。我們致力於良好的軟件設計,確保軟件層是獨立的,客戶端不應該直接依賴內容提供者的實現。Finch視頻提供者API的完整代碼如下:


/**
 * Simple Videos columns
 */
public class FinchVideo {
    public static final class SimpleVideos implements BaseColumns {
        // This class cannot be instantiated
        private SimpleVideos {}
        // uri references all videos
        public static final Uri VIDEOS_URI = Uri.parse(\"content://\" +
            SIMPLE_AUTHORITY + \"/\" + SimpleVideos.VIDEO);
        /**
         * The content:// style URL for this table
         */
        public static final Uri CONTENT_URI = VIDEOS_URI;
1
        /**
         * The MIME type of {@link #CONTENT_URI} providing a directory of notes.
         */
        public static final String CONTENT_TYPE =
                \"vnd.android.cursor.dir/vnd.finch.video\";
2
        /**
         * The MIME type of a {@link #CONTENT_URI} sub-directory of a single
         * video.
         */
        public static final String CONTENT_VIDEO_TYPE =
                \"vnd.android.cursor.item/vnd.finch.video\";
3    
        /**
         * The video itself
         * <P>Type: TEXT</P>
         */
        public static final String VIDEO = \"video\";
        /**
         * Column name for the title of the video
         * <P>Type: TEXT</P>
         */
        public static final String TITLE = \"title\";
        /**
         * Column name for the description of the video.
         */
        public static final String DESCRIPTION = \"description\";
        /**
         * Column name for the media uri
         */
        public static final String URI = \"uri\";
        /**
         * Unique identifier for an element of media
         */
        public static final String MEDIA_ID = \"media_id\";
    }
...
// The API for FinchVideo.Videos is also defined in this class.
}
  

下面是這段代碼的幾個重點:

1 使用VIDEOS_URI定義CONTENT_URI的值。視頻URI包含所描述的內容URI。

2 這是提供者存儲的video項的MIME類型。P337「實現getType方法」一節將解釋內容提供者如何使用這個MIME類型。

3 這些是客戶端用來訪問提供者創建的Cursor對象的字段名稱。