ContentProvider與資料共享
Android系統雖然可以同時執行多個應用程式,但各應用程式是各自獨立的,不但有自己的處理程序,同時也具有自己特定的資料儲存空間,而這空間內的資料是完全私有的,其他應用程式無法使用。 這樣設計的好處是安全,資料完全由自己管理,避免資料誤用與損壞的機會,但也造成因為應用程式無法共享資料,降低了資料重覆使用的彈性。
為了解決這個問題,Android SDK提供了「Content Provider」的概念;Content Provider就如同應用程式資料之間的橋樑,讓應用程式可以透過這座橋樑共享資料,不過橋樑的通行權卻仍在我們手中控制,使應用程式之間有共享資料的彈性,但也仍然為資料提供了適當的保護。
一、認識Content Provider 內容提供者(Content Provider)是Android SDK提供的一個抽象類別,可用以保存及管理資料,更是應用程式之間共享資料的唯一方法。 非資料擁有者的應用程式要從Content Provider存取資料,必須透過「ContentResolver」物件,此物件就如同Content Provider的客戶;ContentResolver對Content Provider的實體提出需求,Content Provider依需求執行必要的動作並回傳動作的結果。
「ContentResolver」物件具有處理資料「CRUD」 (Create:建立、Retrieve:讀取、Update:更新、Delete:刪除)的方法,這些方法會呼叫Content Provider實體物件中的同名方法。 重要的CRUD方法定義如下: 查詢:public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 更新:public final int update (Uri uri, ContentValues values, String selection, String[] selectionArgs)
新增:public final Uri insert (Uri url, ContentValues values) 刪除:public final int delete (Uri url, String selection, String[] selectionArgs) 前面方法定義中,第一個參數都是Uri物件類型的參數,這參數主要是用來識別內容提供者內部資料,它由3個部分組成:以「content://」開始的起始部分;後面接著provider套件名稱,稱為授權(authority);最後為可選擇部分的資料類型與記錄的識別碼(ID)。
要provider提供資料,請求的應用程式必須先在自己的AndroidManifest 要provider提供資料,請求的應用程式必須先在自己的AndroidManifest.xml組態檔申請此provider的使用許可(uses-permission)。如果只是要讀取資料,申請provider的讀取使用許可即可;如果會變動資料(更新、新增與刪除),則還需要申請provider的寫入使用許可。
二、使用Contacts Provider 聯絡簿是個相當複雜的SQLite資料庫,內含有3個資料表: ContactsContract.Contacts資料表:其中每一筆記錄(即一列)相當於一位聯絡人的資料。 ContactsContract.RawContacts資料表:每一筆記錄相當於一位聯絡人的資料摘要,特別是使用者帳號與類型。 ContactsContract.Data資料表:每一筆記錄為聯絡人原始資料的細節,如Email、電話等。
在模擬器(或實際Android裝置上) 為連絡簿增加連絡人資料,請依以下步驟進行: 啟動模擬器。 敲Home螢幕下方的「Contacts」(聯絡簿)圖示。
載入聯絡簿資料庫,此資料庫初始狀態為空的,在開啟的畫面中有3個按鈕分別執行:新增聯絡人、登入帳號、匯入聯絡簿等工作,利用第1個按鈕為聯絡簿增加4筆聯絡人記錄。
敲「Create a new contact」鈕,出現訊息通知因為無帳號,此聯絡簿將無法備份,詢問是否要增加帳號?因為只是要以此聯絡簿資料庫進行練習Content Provider,因此請選「Keep local」鈕。 輸入聯絡人姓名,然後敲返回鈕將資料儲存。
按螢幕右下角增加記錄圖示,繼續增加另外3筆 記錄,記錄內容為「Chen」、「Len」、「Hang」、「Dan」。
ContactProvider Test範例 範例將提供2個主要按鈕「Query」與「Insert」。 按下「Query」按鈕,會顯示聯絡人姓名,並提供另一個「Next」按鈕,讓使用者可以透過此鈕逐筆顯示其他聯絡人姓名。 按下「Insert」按鈕,程式將以目前畫面上的姓名新建一筆聯絡人記錄,因此按此鈕之前要記得先輸入資料。 範例需於AndroidManifest.xml檔中設定聯絡簿讀取(READ_CONTACTS)與寫入許可(WRITE_CONTACTS)。 範例詳細內容請參考原書。
三、建立自己的Content Provider 建立自己的Content Provider其實就是建立延伸自Content Provider的子類別,建立時應注意以下3點: 決定提供資料的方式 Content Provide可以用2種方式提供資料:檔案或SQLite資料庫。 設計Content URI Content URI的目的是識別Provider內的資料,在Content Provide必須實作的方法中,除了「onCreate()」方法以外,呼叫時都必須提供Content URI。
實作Content Provider需要的方法 為了協助依Content URI內容執行不同的處理,我們可以用「UriMatcher」物件透過樣本比對的方式,將方法中的Content URI參數映射到整數常數,然後以switch架構為此參數選擇適合的處理動作。 URI樣本可以用UriMatcher物件的「addURI()」方法加入,樣本中可以有2種萬用字,其中「*」表示任意個數的字元,而「#」表示任意位數的數字。 實作Content Provider需要的方法 因此定義Content Provider抽象類別子類別時必須實作6個方法:query()、insert()、update()、delete()、getType()、onCreate() 。 getType(Uri):這方法回傳一個MIME格式的字串,此字串描述Content URI參數回傳的資料型式。 onCreate():此方法回傳表示建立是否成功的布林值,在這方法內應該只進行一些簡單的初始化工作。
CustomProvider Test範例 建立一個可以對外提供SQLite資料庫資料的Content Provider 。 自建的Provider必須在組態檔AndroidManifest.xml中宣告它的名稱與授權字串,這樣才能從外部透過此Provider存取資料。 範例的Provider要提供SQLite資料庫資料,因此我們也需要建立一個SQLiteOpenHelper子類別(EmployeeDatabaseHelper),幫助我們管理資料庫內容。
範例使用的資料庫為「Company.db」,它含有一個「Employee」資料表,資料表每一筆記錄有4個資料欄:_id,為整數型式的主鍵值,內容自動產生;name,存放員工姓名的字串型式資料欄;age,存放員工年齡的整數型式資料欄;salary,存放員工薪資的整數型式資料欄。 建立ContentProvider子類別EmployeeProvider作為Employee資料表的提供者,在此Provider中,除了實作必要的6個方法以外,我們也定義了Provider的Content URI,此Content URI設計成可以指向整個資料表,或只指向資料表中的一筆記錄。
為了決定資料需求者提出的處理需求是資料表或單筆記錄,我們以UriMatcher物件比對資料需求者提供的Uri資料,並依據比對的結果執行不同動作。 在資料需求者的活動佈局中,以TextView元件顯示記錄_id資料欄的內容,以3個EditText元件顯示記錄其他資料欄的內容,另以4個按鈕分別控制記錄顯示(下一筆)、更新、新增、刪除等功能。 資料需求者以ContentResolver物件將資料處理需求傳給Provider,再由Provider實際處理資料。 範例詳細內容請參考原書。
對於資料需求程式而言,使用外部資料庫或自己私有的資料庫,其實變化並不大,主要的差別在於使用自己的私有資料庫時,是透過SQLiteOpenHelper協助存取資料;而使用外部資料庫的時候,則必須借助Content Provider的幫忙。 但更重要的一點是,在資料處理上的方法(query、update、insert、delete),二者也幾乎完全相同,只是使用Provider時要多提供一個Content URI資料。