1. 簡述 在實際開發中,常常需要進行不同應用程式之間的數據通信,例如讀取聯繫人列表等等,ContentProvider就是Android提供的用於實現不同進程之間進行數據通信的類。 ContentProvider的作用是對外提供對本應用的數據進行“增刪改查”的介面,而後在其它程式可通過Conten ...
1. 簡述
在實際開發中,常常需要進行不同應用程式之間的數據通信,例如讀取聯繫人列表等等,ContentProvider就是Android提供的用於實現不同進程之間進行數據通信的類。
ContentProvider的作用是對外提供對本應用的數據進行“增刪改查”的介面,而後在其它程式可通過ContentResolver類訪問提供的介面,從而實現跨應用數據通信。
2. ContentProvider類
首先,ContentProvider與其它幾個組件一樣也是一個抽象類,使用時必須實現一些方法,並且也需要在manifest中進行註冊。
另外,如果學過SQLite的操作,你會發現增刪改查幾個方法與SQLite資料庫的幾個操作方法極為相似。
註冊:
<provider
android:name=".MyContentProvider"
android:authorities="com.studying.myprovider"
android:enabled="true"
android:exported="true" />
PS:provider標簽寫在application中,與activity同級。
實現的方法:
boolean onCreate()
:在創建ContentProvider時會調用,在這裡完成一些初始化的操作,註意要返回true。
Uri insert(Uri uri, ContentValues values)
:添加方法。
關於Uri:參數中的uri是數據接收方指定哪一個ContentProvider的依據,格式為"content://authorities[/path]",authorities即manifest中註冊provider時的屬性,命名規則類似Java的包名,path則為路徑,可以沒有,例如"content://com.studying.myprovider"。(關於Uri的解析後面會介紹)
第二個參數values則是插入的值包,其中key值需與資料庫中的列名保持一致。另外,insert()方法的返回值為一個Uri,可以在傳入的Uri後加上成功插入的記錄的ID值作為返回的Uri,可用於判斷是否插入成功。添加ID與取出ID的方法為:
ContentUris.withAppendedId(Uri contentUri, long id)// 把id追加到contentUri後面
ContentUris.parseId(Uri uri)// 將id取出
int delete(Uri uri, String selection, String[] selectionArgs)
:selection為條件,selectionArgs為條件值,返回值為受操作影響的行數,例如刪除了1行,則返回1。
int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
:返回值同delete()方法。
Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
:projection為查詢的列,sortOrder為排序方法,query()方法返回一個Cursor對象。
String getType(Uri uri)
:返回數據的MIME類型,這個方法一般用不到,詳情可參考:ContentProvider資料庫共用之——MIME類型與getType()
3. ContentResolver類:
應用程式提供數據共用的介面時使用ContentProvider類,而需要訪問其它應用共用的數據時,則需要使用ContentResolver類。
獲取ContentResolver對象的方法:在Activity中直接getContentResolver()方法即返回一個ContentResolver對象。
使用方法非常簡單,調用ContentResolver對象中的insert()等方法會調用Uri匹配到的ContentProvider中的相應方法。
簡單例子:
ContentValues values = new ContentValues();
values.put("name", name);
values.put("age", age);
values.put("sex", sex);
Uri returnUri = resolver.insert(Uri.parse("content://com.studying.myprovider"), values);
long newItemId = ContentUris.parseId(returnUri);
Toast.makeText(this, "添加成功,新增加的學生ID為" + newItemId, Toast.LENGTH_SHORT).show();
4. URI的解析
URI即Uniform Resource Identifier 統一資源標識符,在這裡可以標識訪問哪一個ContentProvider,除此之外,還可以傳遞參數,以及通過匹配UriMatcher類制定的不同匹配規則進行相應處理。
(1)UriMatcher類:
UriMatcher類可以制定URI的匹配規則,然後可以通過在使用resolver對象時傳入不同格式的URI,使ContentProvider類做出不同的處理。
a)創建匹配規則:matcher.addURI(authorities, path, code),path為路徑,可使用#表示任意數字,*表示任意字元;code則為匹配碼。
b)在對應方法中匹配:int code = matcher.match(uri),返回的code即為匹配碼。
簡單例子:
首先在ContentProvider的onCreate()方法中制定規則:
matcher = new UriMatcher(UriMatcher.NO_MATCH);// 當所有匹配情況都無法匹配到時,則返回UriMatcher.NO_MATCH
matcher.addURI("com.studying.myprovider", "test1/#", 1001);
matcher.addURI("com.studying.myprovider", "test2/*", 1002);
然後在處理方法中做不同處理:
int columnCount = 0;
switch (matcher.match(uri)) {
case 1001:
Log.e("TAG", "匹配成功!匹配形式為:test1 + 任意數字");
break;
case 1002:
Log.e("TAG", "匹配成功!匹配形式為:test2 + 任意字元串");
break;
default:
columnCount = db.delete(TABLE_NAME, selection, selectionArgs);
Log.e("TAG", "刪除成功!");
break;
}
最後通過ContentResolver傳入需要的Uri進行使用:
ContentResolver resolver = getContentResolver();
resolver.delete(Uri.parse("content://com.studying.myprovider/test1/132"), null, null); // 匹配到1001
resolver.delete(Uri.parse("content://com.studying.myprovider/test2/1ac2"), null, null); // 匹配到1002
resolver.delete(Uri.parse("content://com.studying.myprovider/12"), null, null); // 匹配不到,則為UriMatcher.NO_MATCH
(2)Uri自帶解析方法
除了UriMatcher類,還可以通過Uri類自帶的解析方法進行傳值,傳遞參數的方法是:在Uri末尾加上“?”,而後後面加上參數,多個參數之間以“&”連接,例如"content://com.studying.myprovider?name=Tim&age=22"。
在Uri傳遞到ContentProvider之後,通過以下方法取出需要的部分:
uri.getAuthorities():獲取authorities部分。
uri.getPath():獲取path部分。
uri.getQuery():獲取“?”後面的全部字元串。
uri.getQueryParameter(parameterName):傳入相應參數名,獲取該參數值。例如String name = uri.getQueryParameter("name");
5. 訪問手機簡訊箱
訪問簡訊箱的方式非常簡單,跟上面例子的步驟基本一致,只需要額外到manifest中添加訪問短消息的許可權即可。
讀取許可權:android.permission.READ_SMS
寫入:android.permission.WRITE_SMS
Uri:
簡訊箱(全部短消息,包括發送的、接收的以及草稿):content://sms
ContentResolver resolver = getContentResolver();
Uri smsUri = Uri.parse("content://sms/inbox");
Cursor c = resolver.query(smsUri, null, null, null, null);
while (c != null && c.moveToNext()) {
// 3和13分別是號碼和短消息內容所在的列的索引
Log.e("TAG", c.getString(3) + " " + c.getString(13));
}
6. 讀取聯繫人列表
與短消息不同,安卓中存儲聯繫人的方式相對比較複雜,聯繫人的姓名和號碼是分開存儲的。簡單地理解,可以想象成是資料庫的形式,姓名和號碼分別存放在兩張數據表中,而互相之間通過一個唯一的ID值進行標識。因此,讀取聯繫人需要先獲取姓名和ID,再通過ID去獲取號碼。
許可權:android.permission.READ_CONTACTS
姓名所在的ContentProvider的URI:ContactsContract.Contacts.CONTENT_URI
姓名列的列名常量:ContactsContract.Contacts.DISPALY_NAME
ID列的列名常量:ContactsContract.Contacts._ID (PS:切勿遺漏了下劃線)
號碼所在ContentProvider的URI:ContactsContract.CommonDataKinds.Phone.CONTENT_URI
號碼外鍵列的列名常量:ContactsContract.CommonDataKinds.Phone.CONTACT_ID
號碼列的列名常量:ContactsContract.CommonDataKinds.Phone.NUMBER
// 首先獲取姓名和ID
ContentResolver resolver = getContentResolver();
Cursor nameCursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (nameCursor != null && nameCursor.moveToNext()) {
String name = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String _id = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.Contacts._ID));
// 再通過ID獲取相應的號碼
String selections = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?";
Cursor numberCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, selections, new String[] {_id}, null);
String result = "";
while (numberCursor != null && numberCursor.moveToNext()) {
String number = numberCursor.getString(numberCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
result += name + " " + number + " ";
}
Log.e("RESULT", result);
}
7. 添加聯繫人
讀取聯繫人時需要分開讀取,那麼寫入當然也要分開寫入。首先向一個存儲了一些其它數據的ContentProvider中插入一個空數據,從而獲取到一個新的ID,再通過這個ID分別插入姓名和號碼(因為每次插入時都需要指定插入數據的類型,因此需要分開插入)。
許可權:android.permission.WRITE_CONTACTS
Uri:
獲取ID的URI:ContactsContract.RawContacts.CONTENT_URI
插入數據的URI:ContactsContract.Data.CONTENT_URI
需要使用的常量:
姓名的列名:ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
ID的列名:ContactsContract.Data.RAW_CONTACT_ID
電話號碼的列名:ContactsContract.CommonDataKinds.Phone.NUMBER
指定MIME類型的列名:ContactsContract.Data.MIMETYPE
姓名的MIME類型:ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
電話號碼的MIME類型:ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
電話號碼類型的列名:ContactsContract.CommonDataKinds.Phone.TYPE
手機號碼:ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE
住宅號碼:ContactsContract.CommonDataKinds.Phone.TYPE_HOME
PS:常量非常多,要留意其所在的包,共用的一些常量位於ContactsContract.Data包下,例如ID、數據類型等,姓名相關的則在ContactsContract.CommonDataKinds.StructuredName包下,電話號碼相關的則在ContactsContract.CommonDataKinds.Phone包下,理清之後就容易記住了。
ContentResolver resolver = getContentResolver();
// 獲取一個新的ID
ContentValues values = new ContentValues();
Uri idUri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
long id = ContentUris.parseId(idUri);
// 插入姓名
values.put(ContactsContract.Data.RAW_CONTACT_ID, id);
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "Jay");
// 需要指定姓名的數據類型,也就是getType()方法將來返回的值
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
resolver.insert(ContactsContract.Data.CONTENT_URI, values);
// 插入號碼
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID, id);
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "13511223344");
// 指定聯繫號碼的類型,如手機號碼、住宅號碼等
values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
resolver.insert(ContactsContract.Data.CONTENT_URI, values);