1. 程式人生 > >基礎總結篇:ContentProvider之讀寫聯絡人

基礎總結篇:ContentProvider之讀寫聯絡人

靡不有初,鮮克有終。《詩經》

很多事情,絕大多數人都會在開始的時候滿懷熱情,而能堅持到底的卻是寥寥無幾。對待自己的目標,虎頭蛇尾絕不可取,半途而廢只會一無所成,我們必須持之以恆的做下去,堅持到底才能摘取勝利的果實。最近也忙了起來,忙著給自己充電,深知這項任務的艱鉅,不是一天兩天的事,所以也借用這句警言來告誡自己,堅持不懈的走下去。

今天我們來講解一下如何利用ContentProvider機制讀寫聯絡人資訊。

Android中,ContentProvider是一種資料包裝器,適合在不同程序間實現資訊的共享。例如,在Android中SQLite資料庫是一個典型的資料來源,我們可以把它封裝到ContentProvider中,這樣就可以很好的為其他應用提供資訊共享服務。其他應用在訪問ContentProvider時,可以使用一組類似REST的URI的方式進行資料操作,大大簡化了讀寫資訊的複雜度。例如,如果要從封裝圖書資料庫的ContentProvider獲取一組圖書,需要使用類似以下形式的URI:

content://com.scott.book.BookProvider/books

而要從圖書資料庫中獲取指定圖書(比如23號圖書),需要使用類似以下形式的URI:

content://com.scott.book.BookProvider/books/23

注:ContentProvider是一個抽象類,定義了一系列操作資料的方法模板,BookProvider需要實現這些方法,實現圖書資訊的各種操作。

那麼,現在知道了具體的URI之後,我們又如何操作進而取得資料呢?

此時,我們就要了解ContentResolver這個類,它跟ContentProvider是對應的關係,我們正是通過它來與ContentProvider進行資料交換的。android.content.Context類為我們定義了getContentResolver()方法,用於獲取一個ContentResolver物件,如果我們在執行期可以通過getContext()獲取當前Context例項物件,就可以通過這個例項物件所提供的getContentResolver()方法獲取到ContentResolver型別的例項物件,進而可以操作對應的資料。

下面我們就通過聯絡人例項對這種機制進行演示。

在Android中,聯絡人的操作都是通過一個統一的途徑來讀寫資料的,我們開啟/data/data/com.android.providers.contacts可以看到聯絡人的資料來源:


有興趣的朋友可以匯出這個檔案,用專業的工具軟體開啟看一下表結構。

對這個SQLite型別的資料來源的封裝後,聯絡人就以ContentProvider的形式為其他應用程序提供聯絡人的讀寫服務,我們就可以順利成章的操作自己的聯絡人資訊了。

為了方便測試,我們先新增兩個聯絡人到資料來源中,如圖所示:



我們看到,每個聯絡人都有兩個電話號碼和兩個郵箱賬號,分別為家庭座機號碼、移動手機號碼、家庭郵箱賬號和工作郵箱賬號。當然在新增聯絡人時有很多其他資訊,我們這裡都沒有填寫,只選擇了最常用的電話和郵箱,主要是方便演示這個過程。

在演示程式碼之前,我們需要了解一下android.provider.ContactsContract這個類(注:在較早的版本中是android.provider.Contacts這個類,不過現在已被廢棄,不建議使用),它定義了各種聯絡人相關的URI和每一種型別資訊的屬性資訊:


有興趣的朋友還可以讀一下原始碼,不過比較多,而且內部類使用的特別多,讀起來有一定的困難,還是要做好心理準備。

下面我們通過一個專案,來演示一下聯絡人操作的具體過程。新建一個名為provider的專案,建立一個名為ContactsReadTest的測試用例,如下:

  1. package com.scott.provider;  
  2. import java.util.ArrayList;  
  3. import android.content.ContentResolver;  
  4. import android.database.Cursor;  
  5. import android.net.Uri;  
  6. import android.provider.ContactsContract;  
  7. import android.test.AndroidTestCase;  
  8. import android.util.Log;  
  9. publicclass ContactsReadTest extends AndroidTestCase {  
  10.     privatestaticfinal String TAG = "ContactsReadTest";  
  11.     //[content://com.android.contacts/contacts]
  12.     privatestaticfinal Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI;  
  13.     //[content://com.android.contacts/data/phones]
  14.     privatestaticfinal Uri PHONES_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;  
  15.     //[content://com.android.contacts/data/emails]
  16.     privatestaticfinal Uri EMAIL_URI = ContactsContract.CommonDataKinds.Email.CONTENT_URI;  
  17.     privatestaticfinal String _ID = ContactsContract.Contacts._ID;  
  18.     privatestaticfinal String DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME;  
  19.     privatestaticfinal String HAS_PHONE_NUMBER = ContactsContract.Contacts.HAS_PHONE_NUMBER;  
  20.     privatestaticfinal String CONTACT_ID = ContactsContract.Data.CONTACT_ID;  
  21.     privatestaticfinal String PHONE_NUMBER = ContactsContract.CommonDataKinds.Phone.NUMBER;  
  22.     privatestaticfinal String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.TYPE;  
  23.     privatestaticfinal String EMAIL_DATA = ContactsContract.CommonDataKinds.Email.DATA;  
  24.     privatestaticfinal String EMAIL_TYPE = ContactsContract.CommonDataKinds.Email.TYPE;  
  25.     publicvoid testReadContacts() {  
  26.         ContentResolver resolver = getContext().getContentResolver();  
  27.         Cursor c = resolver.query(CONTACTS_URI, nullnullnullnull);  
  28.         while (c.moveToNext()) {  
  29.             int _id = c.getInt(c.getColumnIndex(_ID));  
  30.             String displayName = c.getString(c.getColumnIndex(DISPLAY_NAME));  
  31.             Log.i(TAG, displayName);  
  32.             ArrayList<String> phones = new ArrayList<String>();  
  33.             ArrayList<String> emails = new ArrayList<String>();  
  34.             String selection = CONTACT_ID + "=" + _id;  //the 'where' clause
  35.             //獲取手機號
  36.             int hasPhoneNumber = c.getInt(c.getColumnIndex(HAS_PHONE_NUMBER));  
  37.             if (hasPhoneNumber > 0) {  
  38.                 Cursor  phc = resolver.query(PHONES_URI, null, selection, nullnull);  
  39.                 while (phc.moveToNext()) {  
  40.                     String phoneNumber = phc.getString(phc.getColumnIndex(PHONE_NUMBER));  
  41.                     int phoneType = phc.getInt(phc.getColumnIndex(PHONE_TYPE));  
  42.                     phones.add(getPhoneTypeNameById(phoneType) + " : " + phoneNumber);  
  43.                 }  
  44.                 phc.close();  
  45.             }  
  46.             Log.i(TAG, "phones: " + phones);  
  47.             //獲取郵箱
  48.             Cursor emc = resolver.query(EMAIL_URI,null, selection, nullnull);  
  49.             while (emc.moveToNext()) {  
  50.                 String emailData = emc.getString(emc.getColumnIndex(EMAIL_DATA));  
  51.                 int emailType = emc.getInt(emc.getColumnIndex(EMAIL_TYPE));  
  52.                 emails.add(getEmailTypeNameById(emailType) + " : " + emailData);  
  53.             }  
  54.             emc.close();  
  55.             Log.i(TAG, "emails: " + emails);  
  56.         }  
  57.         c.close();  
  58.     }  
  59.     private String getPhoneTypeNameById(int typeId) {  
  60.         switch (typeId) {  
  61.         case ContactsContract.CommonDataKinds.Phone.TYPE_HOME: return"home";  
  62.         case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE: return"mobile";  
  63.         case ContactsContract.CommonDataKinds.Phone.TYPE_WORK: return"work";  
  64.         defaultreturn"none";  
  65.         }  
  66.     }  
  67.     private String getEmailTypeNameById(int typeId) {  
  68.         switch (typeId) {  
  69.         case ContactsContract.CommonDataKinds.Email.TYPE_HOME: return"home";  
  70.         case ContactsContract.CommonDataKinds.Email.TYPE_WORK: return"work";  
  71.         case ContactsContract.CommonDataKinds.Email.TYPE_OTHER: return"other";  
  72.         defaultreturn"none";  
  73.         }  
  74.     }  
  75. }  
為了使這個測試用例執行起來,我們需要在AndroidManifest.xml中配置一下測試裝置的宣告,它與<application>元素處於同一級別位置:
  1. <!-- 配置測試裝置的主類和目標包 -->
  2.     <instrumentationandroid:name="android.test.InstrumentationTestRunner"
  3.                      android:targetPackage="com.scott.provider"/>
然後再配置使用測試類庫宣告,它與<activity>元素處於同一級別位置:
  1. <!-- 配置測試要使用的類庫 -->
  2.    <uses-libraryandroid:name="android.test.runner"/>
最後,還有一個重要的宣告需要配置,就是讀取聯絡人許可權,宣告如下:
  1. <!-- 讀取聯絡人 -->
  2. <uses-permissionandroid:name="android.permission.READ_CONTACTS"/>
經過以上準備工作,這個測試用例就可以運轉起來了,我們執行一下testReadContacts()方法,列印結果如下:


看來聯絡人裡的資訊都被我們準確無誤的讀取出來了。

如果我們在一個Activity裡執行讀取聯絡人的程式碼,不僅可以使用ContentResolver直接進行讀取操作(即查詢),還可以使用Activity提供的managedQuery方法方便的實現同樣的效果,我們來看一下這個方法的具體程式碼:

  1. publicfinal Cursor managedQuery(Uri uri,  
  2.                                  String[] projection,  
  3.                                  String selection,  
  4.                                  String[] selectionArgs,  
  5.                                  String sortOrder)  
  6. {  
  7.     Cursor c = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);  
  8.     if (c != null) {  
  9.         startManagingCursor(c);  
  10.     }  
  11.     return c;  
  12. }  
我們發現,其實它還是使用了ContentResolver進行查詢操作,但是多了一步startManagingCursor的操作,它會根據Activity的生命週期對Cursor物件進行管理,避免了一些因Cursor是否釋放引起的問題,所以非常方便,大大簡化了我們的工作量。

接下來我們將要嘗試將一個聯絡人資訊新增到系統聯絡人的資料來源中,實現對聯絡人的寫入操作。我們新建一個名為ContactsWriteTest的測試用例,如下:

  1. package com.scott.provider;  
  2. import java.util.ArrayList;  
  3. import android.content.ContentProviderOperation;  
  4. import android.content.ContentProviderResult;  
  5. import android.content.ContentResolver;  
  6. import android.net.Uri;  
  7. import android.provider.ContactsContract;  
  8. import android.test.AndroidTestCase;  
  9. import android.util.Log;  
  10. publicclass ContactsWriteTest extends AndroidTestCase {  
  11.     privatestaticfinal String TAG = "ContactsWriteTest";  
  12.     //[content://com.android.contacts/raw_contacts]
  13.     privatestaticfinal Uri RAW_CONTACTS_URI = ContactsContract.RawContacts.CONTENT_URI;  
  14.     //[content://com.android.contacts/data]
  15.     privatestaticfinal Uri DATA_URI = ContactsContract.Data.CONTENT_URI;  
  16.     privatestaticfinal String ACCOUNT_TYPE = ContactsContract.RawContacts.ACCOUNT_TYPE;  
  17.     privatestaticfinal String ACCOUNT_NAME = ContactsContract.RawContacts.ACCOUNT_NAME;  
  18.     privatestaticfinal String RAW_CONTACT_ID = ContactsContract.Data.RAW_CONTACT_ID;  
  19.     privatestaticfinal String MIMETYPE = ContactsContract.Data.MIMETYPE;  
  20.     privatestaticfinal String NAME_ITEM_TYPE = ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE;  
  21.     privatestaticfinal String DISPLAY_NAME = ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME;  
  22.     privatestaticfinal String PHONE_ITEM_TYPE = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;  
  23.     privatestaticfinal String PHONE_NUMBER = ContactsContract.CommonDataKinds.Phone.NUMBER;  
  24.     privatestaticfinal String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.TYPE;  
  25.     privatestaticfinalint PHONE_TYPE_HOME = ContactsContract.CommonDataKinds.Phone.TYPE_HOME;  
  26.     privatestaticfinalint PHONE_TYPE_MOBILE = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE;  
  27.     privatestaticfinal String EMAIL_ITEM_TYPE = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;  
  28.     privatestaticfinal String EMAIL_DATA = ContactsContract.CommonDataKinds.Email.DATA;  
  29.     privatestaticfinal String EMAIL_TYPE = ContactsContract.CommonDataKinds.Email.TYPE;  
  30.     privatestaticfinalint EMAIL_TYPE_HOME = ContactsContract.CommonDataKinds.Email.TYPE_HOME;  
  31.     privatestaticfinalint EMAIL_TYPE_WORK = ContactsContract.CommonDataKinds.Email.TYPE_WORK;  
  32.     privatestaticfinal String AUTHORITY = ContactsContract.AUTHORITY;  
  33.     publicvoid testWriteContacts() throws Exception {  
  34.         ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();  
  35.         ContentProviderOperation operation = ContentProviderOperation.newInsert(RAW_CONTACTS_URI)  
  36.                                     .withValue(ACCOUNT_TYPE, null)  
  37.                                     .withValue(ACCOUNT_NAME, null)  
  38.                                     .build();  
  39.         operations.add(operation);  
  40.         //新增聯絡人名稱操作
  41.         operation = ContentProviderOperation.newInsert(DATA_URI)  
  42.                                     .withValueBackReference(RAW_CONTACT_ID, 0)  
  43.                                     .withValue(MIMETYPE, NAME_ITEM_TYPE)  
  44.                                     .withValue(DISPLAY_NAME, "Scott Liu")  
  45.                                     .build();  
  46.         operations.add(operation);  
  47.         //新增家庭座機號碼
  48.         operation = ContentProviderOperation.newInsert(DATA_URI)  
  49.                                     .withValueBackReference(RAW_CONTACT_ID, 0)  
  50.                                     .withValue(MIMETYPE, PHONE_ITEM_TYPE)  
  51.                                     .withValue(PHONE_TYPE, PHONE_TYPE_HOME)  
  52.                                     .withValue(PHONE_NUMBER, "01034567890")  
  53.                                     .build();  
  54.         operations.add(operation);  
  55.         //新增移動手機號碼
  56.         operation = ContentProviderOperation.newInsert(DATA_URI)  
  57.                                     .withValueBackReference(RAW_CONTACT_ID, 0)  
  58.                                     .withValue(MIMETYPE, PHONE_ITEM_TYPE)  
  59.                                     .withValue(PHONE_TYPE, PHONE_TYPE_MOBILE)  
  60.                                     .withValue(PHONE_NUMBER, "13034567890")  
  61.                                     .build();  
  62.         operations.add(operation);  
  63.         //新增家庭郵箱
  64.         operation = ContentProviderOperation.newInsert(DATA_URI)  
  65.                                     .withValueBackReference(RAW_CONTACT_ID, 0)  
  66.                                     .withValue(MIMETYPE, EMAIL_ITEM_TYPE)  
  67.                                     .withValue(EMAIL_TYPE, EMAIL_TYPE_HOME)  
  68.                                     .withValue(EMAIL_DATA, "[email protected]")  
  69.                                     .build();  
  70.         operations.add(operation);  
  71.         //新增工作郵箱
  72.         operation = ContentProviderOperation.newInsert(DATA_URI)  
  73.                                     .withValueBackReference(RAW_CONTACT_ID, 0)  
  74.                                     .withValue(MIMETYPE, EMAIL_ITEM_TYPE)  
  75.                                     .withValue(EMAIL_TYPE, EMAIL_TYPE_WORK)  
  76.                                     .withValue(EMAIL_DATA, "[email protected]")  
  77.                                     .build();  
  78.         operations.add(operation);  
  79.         ContentResolver resolver = getContext().getContentResolver();  
  80.         //批量執行,返回執行結果集
  81.         ContentProviderResult[] results = resolver.applyBatch(AUTHORITY, operations);  
  82.         for (ContentProviderResult result : results) {  
  83.             Log.i(TAG, result.uri.toString());  
  84.         }  
  85.     }  
  86. }  
在上面的程式碼中,我們把整個操作分為幾個ContentProviderOperation操作,並將他們做批處理操作,我們也許注意到,從第二個操作開始,每一項都有一個withValueBackReference(RAW_CONTACT_ID, 0)步驟,它參照了第一項操作新新增的聯絡人的id,因為是批處理,我們插入資料前並不知道id的值,不過這個不用擔心,在進行批處理插入資料時,它會重新引用新的id值,不會影響最終的結果。

當然,這個也不能忘了配置寫入聯絡人的許可權宣告:

  1. <!-- 寫入聯絡人 -->
  2. <uses-permissionandroid:name="android.permission.WRITE_CONTACTS"/>
經過以上步驟之後,我們執行一下testWriteContacts()方法,看看聯絡人是否新增進去了:


相關推薦

基礎總結ContentProvider聯絡人

靡不有初,鮮克有終。《詩經》 很多事情,絕大多數人都會在開始的時候滿懷熱情,而能堅持到底的卻是寥寥無幾。對待自己的目標,虎頭蛇尾絕不可取,半途而廢只會一無所成,我們必須持之以恆的做下去,堅持到底才能摘取勝利的果實。最近也忙了起來,忙著給自己充電,深知這項任務的艱鉅,不是一天兩天的事,所以也借用這句警言來告誡

基礎總結ContentProvider聯絡人

靡不有初,鮮克有終。《詩經》 很多事情,絕大多數人都會在開始的時候滿懷熱情,而能堅持到底的卻是寥寥無幾。對待自己的目標,虎頭蛇尾絕不可取,半途而廢只會一無所成,我們必須持之以恆的做下去,堅持到底才能摘取勝利的果實。最近也忙了起來,忙著給自己充電,深知這項任務的艱鉅,不是一天

原始碼分析ReentrantReadWriteLock

## 簡介 ReentrantReadWriteLock 從字面意思可以看出,是和重入、讀寫有關係的鎖,實際上 ReentrantReadWriteLock 確實也是支援可重入的讀寫鎖,並且支援公平和非公平獲取鎖兩種模式。 **為什麼會出現讀寫鎖?** 普通鎖可以保證共享資料在同一時刻只被一個執行緒訪問

基礎總結Activity的四種launchMode

合抱之木,生於毫末;九層之臺,起於累土;千里之行,始於足下。《老子》 今天在社群看到有朋友問“如何在半年內成為頂級架構師”,有網友道“關燈睡覺,不用半年的...”,的確,做夢還來的快一些。作為一個程式設計師,樹立遠大的目標是值得欣賞的,但不能只去空想,要一步一步地實踐

基於Python的介面自動化實戰-基礎配置檔案

引言         在編寫介面自動化測試指令碼時,有時我們需要在程式碼中定義變數並給變數固定的賦值。為了統一管理和操作這些固定的變數,咱們一般會將這些固定的變數以一定規則配置到指定的配置檔案中,後續需要用到這些變數和變數值時通過程式碼讀取或者寫入資料到該配置檔案即可,

Python-OpenCV基礎影象的,尺寸和儲存

為什麼使用Python-OpenCV 雖然python 很強大,而且也有自己的影象處理庫PIL,但是相對於OpenCV 來講,它還是弱小很多。跟很多開源軟體一樣OpenCV 也提供了完善的python 介面,非常便於呼叫。OpenCV 的穩定版是2.4.8,最新版是4.0,包含了超過2500

STM32CubeMX學習教程硬體I2CAT24C02

完整原始碼下載:https://github.com/simonliu009/STM32CubeMX-hardware-I2C-AT24C02網上有流傳已久一種說法,就是STM的I2C有bug,不好用。確實很多人在實際應用中都遇到了各種問題,所以絕大部分人都是在用軟體模擬II

Java執行緒總結(八)併發包------鎖ReadWriteLock的簡單例子詳細理解

初次接觸ReadWriteLock類時也在網上查了很多資料,很容易瞭解到ReadWriteLock是讀寫鎖,並且讀寫鎖的機制有以下三個特點:  讀鎖---讀鎖    (不互斥)  讀鎖---寫鎖     (互斥)  寫鎖---寫鎖     (互斥)什麼意思呢?網上很多資料,

從零開始學C++IO流類庫(三)檔案的、二進位制檔案的、檔案隨機

#include <cassert>#include <iostream>#include <fstream>#include <string>using namespace std;struct Test {     int a;     int b; };i

測開路二文件

alt bsp http 文件讀寫 info 讀寫 技術分享 分享圖片 文件 open函數: 讀: 寫: 測開之路二:文件讀寫

react基礎總結1,定義組件實現父子組件傳值

實現 efault () 語法 前端 bsp component 定義 我們 前端時間學習了vue,這幾天開始入手react了。 react項目搭建起來之後,我們一定會定義很多個組件。同樣的也會涉及到父子組件的傳值。今天來整理一下這個知識。 1,定義子組件步驟   1,引入

線程同步鎖(鎖操作的補充)

允許 資源 加鎖 函數 申請 tex bject def 讀取 輕量級的讀寫鎖(Slim Reader-Writer locks):讀寫鎖實際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作。這種鎖相對於

Java基礎總結01JDK與JRE概述

環境 開發工具 只需要 不用 基礎 好的 概述 spa runtime 1)JRE(Java Runtime Environment,Java運行時環境)   包括Java虛擬機(JVM Java Virtual Machine)和Java程序所需的核心類庫等,如果想要運行

多線程編程

|| 概念 release 線程編程 相關 修改 reader lin 實現   在《多線程編程之Linux環境下的多線程(二)》一文中提到了Linux環境下的多線程同步機制之一的讀寫鎖。本文再詳細寫一下讀寫鎖的概念和原理。 一、什麽是讀寫鎖   讀寫鎖(也叫共享-獨占鎖)

第十五JavaScript Dom操作

頁面 對象模型 面向 方法 bsp log gpo eva div 一、後臺管理頁面布局 二、JavaScript函數 三、eval以及時間操作 四、JavaScript作用域 五、JavaScript面向對象模型 六、DOM選擇器 七、DOM事件操作 八、DOM綁定事件的

網易cetus分離

cetus 中間件 讀寫分離 一、 簡介 Cetus是由C語言開發的關系型數據庫MySQL的中間件,主要提供了一個全面的數據庫訪問代理功能。Cetus連接方式與MySQL基本兼容,應用程序幾乎不用修改即可通過Cetus訪問數據庫,實現了數據庫層的水平擴展和高可用。Cetus由網易樂得專家技術團隊

Python的語言基礎總結(二)循環與字符串操作

整數 範圍 輸入 IV 字符串 spl 余額 upper ict 一、循環和分支   1.  條件分支語句   if  條件:     代碼塊   else:     代碼塊   2.  循環語句之while   思考1:求1+2+3+....+10的值    sum =

總結iOS下JS與原生OC互相調用

html中 web har 項目 ise ref text uia oc調用js iOS開發免不了要與UIWebView打交道,然後就要涉及到JS與原生OC交互,今天總結了一下JS與原生OC交互的兩種方式。 JS調用原生OC篇 方式一 第一種方式是用JS發起一個假的URL請

python file-like Object文件

ren wrap final 大文件 ons idt sts temp read 官網對文件操作解釋:open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, c

QT 登錄記住密碼方法之一Qt QSettings配置文件

mat lin syn use current ebe true erp sync 不過本文寫的是明文保存,最好還是加密一下,以防文件被非法讀取/**登錄初始化的時候處理這部分操作*/ Settings cfg("user.ini",QSettings::IniFormat