Android學習筆記37:使用Content Providers方式共享數據


  在Android中一共提供了5種數據存儲方式,分別為:

  (1)Files:通過FileInputStream和FileOutputStream對文件進行操作。具體使用方法可以參閱博文《Android學習筆記34:使用文件存儲數據》。

  (2)Shared Preferences:常用來存儲鍵值對形式的數據,對系統配置信息進行保存。具體使用方法可以參閱博文《Android學習筆記35:使用Shared Preferences方式存儲數據》。

  (3)Content Providers:數據共享,用於應用程序之間數據的訪問。

  (4)SQLite:Android自帶的輕量級關系型數據庫,支持SQL語言,用來存儲大量的數據,並且能夠對數據進行使用、更新、維護等操作。具體使用方法可以參閱博文《Android學習筆記36:使用SQLite方式存儲數據》。

  (5)Network:通過網絡來存儲和獲取數據。

  本篇博文介紹第三種方式,通過Content Providers實現應用程序之間的數據共享。

 

1.Content Providers簡介

  在Android系統中,不存在一個公共的數據存儲區供所有的應用程序訪問,也就是說數據在各個應用程序中是私有的。那么,如何在一個應用程序中訪問另一個應用程序中的數據,實現應用程序之間的數據共享呢?

  當然,你可以通過《Android學習筆記34:使用文件存儲數據》一文中講到的設置openFileOutput()方法中的第二個參數mode為Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE,讓別的應用程序可以讀寫該應用程序中的文件。但是,使用這種方式的弊端也是顯而易見的,不僅需要知道該文件的存儲路徑,而且會將該文件內容完全的暴露出去,對於內容提供者和內容訪問者來說都是不方便和不安全的。

  為此,Android系統提供了Content Providers,用以方便安全的實現應用程序間的數據共享。

1.1ContentResolver

  所有的Content Providers都會實現一些共同的接口,包括數據的查詢、添加、更改和刪除。在應用程序中,我們可以通過使用getContentResolver()方法來取得一個ContentResolver對象,然后就可以通過這個ContentResolver對象來操作你需要的Content Provider了。ContentResolver類提供的用來操作Content Provider的方法主要有insert()、delete()、update()和query()。

  通常,對於開發者而言,並不需要同Content Provider對象直接打交道。系統運行時,會將所有的ContentProvider對象實例化,對於每一種類型的ContentProvider只有一個實例。這個實例可以與在不同的程序或進程中的多個ContentResolver對象進行通信。而這些進程間的交互則是由ContentResolver和ContentProvider類進行處理的。

  對於Content Providers而言,最重要的就是數據存儲結構和URI。

1.2數據存儲結構

  Content Providers將其存儲的數據以數據表的形式提供給訪問者,在數據表中,每一行為一條記錄,每一列為具有特定類型和意義的數據。比如,聯系人的Content Provider數據存儲結構如圖1所示。

圖1 Content Provider數據存儲結構示例

  可以看出,每條記錄都有一個_ID字段用來唯一的標識該記錄,類似於數據庫中的主鍵。

1.3URI

  每一個Content Provider都對外提供一個能夠唯一標識自己數據集的公開URI,如果一個Content Provider管理多個數據集,則需要為每一個數據集都分配一個獨立的URI。

  Android規定,所有Content Provider的URI都必須以“content://”開頭。通常,URI由3部分組成:“content://”、數據的路徑、標識ID(可選)。

  比如,以下是系統提供的一些URI:

  (1)content://media/internal/images

  (2)content://contacts/people/2

  (3)content://contacts/people

  其中,(1)將返回設備上存儲的所有圖片;(2)將返回聯系人信息中ID為5的聯系人記錄;(3)將返回設備上所有的聯系人信息。

  每個ContentResolver對象都將URI作為其第一個參數,URI決定了ContentResolver將與哪一個Content Provider對話。

 

2.獲取Content Provider內容

  Android系統為一些常見的數據類型(如音頻、視頻、圖像、通訊錄聯系人等)內置了一系列的Content Provider。以下就以通訊錄聯系人為例,講講如何獲取Content Provider內容。

  首先,我們需要在模擬器中運行“聯系人”應用程序程序,並在其中添加聯系人。如圖2所示。

圖2 添加聯系人

  如圖2所示,我在“聯系人”應用程序程序中添加了兩個聯系人:李明和王磊。

  然后,我們需要創建一個自己的工程,該工程的主要功能就是得到持有聯系人信息的Content Provider中的數據。這里,我在布局文件中定義了一個TextView控件,用來將獲得的聯系人數據顯示出來,運行后的效果如圖3所示。

圖3 獲取Content Provider內容

  由圖3可以看出,我們自己創建的應用程序確實從“聯系人”應用程序程序中獲得了數據(ID和Name字段),當然,如果你需要,你可以獲取圖1中的更多的字段信息。

  下面的代碼給出了實現這一功能的一種方案。

 1     /*
 2      * Function  :    獲取聯系人列表信息
 3      * Author    :    博客園-依舊淡然
 4      */
 5     public String getResult() {
 6         
 7         String result = "";
 8         Uri uri = Uri.parse("content://contacts/people");        //聯系人Content Provider的URI
 9         String[] columns = {People._ID, People.NAME};            //聯系人的ID和Name
10         
11         ContentResolver contentResolver = getContentResolver();     //獲取ContentResolver對象
12         Cursor cursor = contentResolver.query(uri, columns, null, null, null);    //查詢Content Provider
13             int peopleId = cursor.getColumnIndex(People._ID);       //獲得ID字段的列索引
14             int peopleName = cursor.getColumnIndex(People.NAME);    //獲得Name字段的列索引
15             
16         //遍歷Cursor對象,提取數據
17         for(cursor.moveToFirst(); (!cursor.isAfterLast()); cursor.moveToNext()) {
18             result = result + cursor.getString(peopleId) + "\t\t";
19             result = result + cursor.getString(peopleName) + "\t\n";
20         }
21         cursor.close();
22         return result;
23     }

  通過以上的代碼可以看出,要獲取Content Provider內容,我們需要知道Content Provider的URI以及Content Provider的數據存儲形式(字段名稱和字段類型)。然后,我們便可以通過使用ContentResolver對象的query()方法對Content Provider進行查詢了,查詢的結果需要使用Cursor對象存儲(有關Cursor的介紹可以參閱《Android學習筆記36:使用SQLite方式存儲數據》一文)。最后遍歷Cursor對象,取出各個字段的信息即可。

  因為該應用程序需要訪問聯系人信息,所以還需要在AndroidManifest.xml文件中加入相應的權限許可,具體如下:

  <uses-permission android:name="android.permission.READ_CONTACTS"/>

  至此,我們便完成了獲取聯系人Content Provider內容的功能。

 

3.提供Content Provider內容

  上面介紹了如何從別的應用程序中獲取Content Provider內容,那么如何在自己的應用程序中提供Content Provider內容供別的應用程序訪問呢?

  一般來說,讓自己的數據被別的應用程序訪問有兩種方式:創建自己的Content Provider(即繼承自Content Provider的子類),或者是將自己的數據加入到已有的Content Provider中去。將自己的數據加入到已有的Content Provider中去有一定的局限性,因為要保證自己的數據和現有的Content Provider數據類型相同,並且具有該Content Provider的寫入權限。

  下面將說說如何創建一個自己的Content Provider,大致可以分為3個步驟。

3.1建立數據的存儲系統

  很顯然,要將自己應用程序中的數據共享給他人,肯定需要建立自己的數據存儲系統。當然了,選擇什么樣的存儲系統(文件存儲系統、SQLite數據庫等)完全由開發者決定。

  在《Android學習筆記36:使用SQLite方式存儲數據》一文中,我們搭建了一個簡單的SQLite數據庫系統。在該數據庫中,我們新建了一張具有3個字段(studentId、studentName、studentAge)的表,用來存儲學生信息。

  這里,我們就以該工程為例,講講如何將該工程中的學生信息通過Content Provider方式共享出去。

3.2擴展Content Provider類

  在該工程中,我們需要新建了一個繼承自ContentProvider的類。用來將要共享的數據進行包裝並以ContentResolver對象和Cursor對象能夠訪問的形式對外展示。這里,我將這個類命名為了“StudentContentProvider”。

  在ContentProvider類中提供了6個抽象方法,分別為:

  (1)public abstract Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);

  (2)public abstract Uri insert (Uri uri, ContentValues values);

  (3)public abstract int update (Uri uri, ContentValues values, String selection, String[] selectionArgs);

  (4)public abstract int delete (Uri uri, String selection, String[] selectionArgs);

  (5)public abstract String getType (Uri uri);

  (6)public abstract boolean onCreate ();

  其中,query()方法用於將查詢到的數據以Cursor對象的形式返回;insert()方法用於向Content Provider中插入新數據記錄,該方法中的第二個參數ContentValues對象表示數據記錄的列名和列值的映射;update()方法用於更新Content Provider中的已存在的數據記錄;delete()方法用於從Content Provider中刪除數據記錄;getType()方法用於返回Content Provider中數據的(MIME)類型;onCreate()方法當Content Provider啟動時被調用。

  以上的6個方法將會在ContentResolver對象中被調用,所以很好的實現這些抽象方法就會為ContentResolver提供一個完善的外部接口。

  當然了,你可以根據自己應用程序的需要,有選擇的實現上述6個方法,比如,你可以只實現query()方法,這樣別的應用程序就自能對你提供的Content Provider進行查詢操作,而無法對你提供的Content Provider進行添加、刪除等操作,從而保證了數據的安全性。

  如下的代碼實現了query()方法。

1     /*
2      * Function  :    查詢方法
3      * Author    :    博客園-依舊淡然
4      */
5     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
6         db = mySQLiteOpenHelper.getWritableDatabase();
7         Cursor cursor = db.query("tab_student", projection, selection, selectionArgs, null, null, sortOrder);
8         return cursor;
9     }

  可以看出,查詢的核心其實還是調用SQLiteDatabase類提供的query()方法,將查詢到的結果存儲在Cursor對象中,最后直接將Cursor對象返回即可。

  此外,在繼承自ContentProvider類的“StudentContentProvider”中,我們還需要做一件很重要的事,那就是指定Content Provider的URI。該URI必須是唯一的,不能和系統的URI相同,更不能與其他應用程序提供的Content Provider的URI相同。

  這里我定義了Content Provider的URI為“content://com.example.sqlite.studentProvider/student”。

3.3聲明Content Provider的權限

  創建好的Content Provider必須在應用程序的AndroidManifest.xml文件中進行聲明,否則,該Content Provider對於Android系統是不可見的。

  具體的聲明方式如下:

1     <!-- 聲明內容提供者 -->
2     <provider                    
      android:name="com.example.android_datastorage_sqlite.provider.StudentContentProvider" 3 android:authorities="com.example.sqlite.studentProvider" > 4 </provider>

  其中,<provider></provider>標簽位於<application></application>標簽下。android:name屬性用於指明StudentContentProvider的全稱類名,android:authorities屬性唯一的標識了一個Content Provider。

  至此,我們便在該工程中創建了自己的Content Provider,並提供了query()方法供別的應用程序查詢該工程中的SQLite數據表。

3.4驗證

  在應用程序Android_DataStorage_SQLite的SQLite數據表中,我們添加了3條記錄,如圖4所示。


圖4 SQLite數據表信息

  新建一個應用程序Android_DataStorage_ContentProviders,用來獲取自定義的Content Provider內容。獲取自定義的Content Provider內容的方法,和前面講的獲取聯系人應用程序的Content Provider內容的方法類似。使用自定義的Content Provider的URI,並查詢數據表中相應的字段即可。可以看到查詢到的信息如圖5所示。

圖5 查詢自定義的Content Provider內容

  可以看出,在應用程序Android_DataStorage_ContentProviders中確實訪問到了應用程序Android_DataStorage_SQLite中的數據表信息,通過Content Provider方式實現了數據在應用程序之間的共享。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM