前言:
上一篇介紹廣播接收者(BroadcastReceover),本篇繼續,記敘下Android四大組件最後一個不常用,但是非常有用的ContentProvider。Google文檔
本文主要從以下幾個方面介紹:
- 特點
- 優缺點
- 數據訪問
- 創建內容提供器
- 工作機制
特點
- Android四大組件之一,需要進行註冊,一般有name,authorities,export等屬性
- 是一種定義數據共享的接口,並是不android數據存儲的方式之一
- 跨進程數據訪問
- android系統很多系統應用都是使用ContentProvider方式進行儲存的(圖片,通訊錄,視頻,音頻等)
- 數據更新監聽方便
優缺點
優點:
- 爲數據訪問提供統一的接口,解決了不同儲存方式需要不同的API的訪問所帶來的繁瑣
- 跨進程數據的訪問,實現了不同App數據訪問提供了很大的便利
缺點:
- 不能單獨使用,必須需要和其他的儲存方式結合使用
數據訪問
下面示例如何獲取通訊錄信息:
Activity:
public class ContentProviderActivity extends AppCompatActivity {
private ContentResolver mContentResolver;//
StringBuilder builder=new StringBuilder("");
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_provider);
mContentResolver=getContentResolver();
tv= (TextView) findViewById(R.id.tv_show_contract);
findViewById(R.id.tv_press_contract).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//動態申請權限
if(ContextCompat.checkSelfPermission(ContentProviderActivity.this, android.Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(ContentProviderActivity.this,new String[]{Manifest.permission.READ_CONTACTS},1);
}else{
readContacts();
}
}
});
}
private void readContacts(){
Cursor cursor=null;
try {
cursor=mContentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
if(cursor!=null){
while(cursor.moveToNext()){
String displayName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
builder.append(displayName);
builder.append(": ");
builder.append(number);
builder.append(" ");
Log.i("wy",displayName+" "+number);
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(cursor!=null){
cursor.close();
}
tv.setText(builder);
}
}
}
以上:
- 藉助ContentResolver類訪問內容提供器中共享的數據,其提供了一系列的增刪改查(CRUD)操作,其中insert方法用於添加數據,update更新數據,delete刪除數據,query用於查詢數據。和SQLiteDatabase類似,只是參數上稍微有些區別,ContentResolver中接收的參數是不接收表名參數的,而是使用一個Uri參數代碼(關於Uri內容歡迎參考一篇就夠了系列之Activity全解析中關於Scheme的介紹)。
- 由於聯繫人信息是危險權限,所以需要動態申請,同時manifest中也需要申明
- 代碼中的查詢等方法可以看到是個常量,其實也源碼的一個封裝。
創建內容提供器
public class MyContentProvider extends ContentProvider {
public static final int DIR1=0;
public static final int ITEM1=1;
public static final int DIR2=2;
public static final int ITEM2=3;
public static final String AUTHRITY="com.wenyi.interview.provider";
private static UriMatcher uriMatcher;
private MyDatabaseHelper dbhelper;
static {
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHRITY,"book",DIR1);
uriMatcher.addURI(AUTHRITY,"book/#",ITEM1);
uriMatcher.addURI(AUTHRITY,"category",DIR2);
uriMatcher.addURI(AUTHRITY,"category/#",ITEM2);
}
/**
* 初始化內容提供器的時候調用,通常會在這裏完成對數據庫的創建和升級等操作
* @return true表示初始化成功,false表示初始化失敗
* 只有當contentResolver嘗試訪問我們的程序中的數據是,內容提供器纔會被初始化
*/
@Override
public boolean onCreate() {
dbhelper=new MyDatabaseHelper(getContext(),"MyStore.db",null,2);
return true;
}
/**
* 查詢數據
* @param uri 確定查詢哪張表
* @param projection 確定查詢哪些列
* @param selection 約束查詢哪些行
* @param selectionArgs 約束查詢哪些行
* @param sortOrder 用於對結果進行排序,查詢的結果存放在Cursor對象中返回
* @return
*/
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
SQLiteDatabase db=dbhelper.getReadableDatabase();
Cursor cursor=null;
switch (uriMatcher.match(uri)){
case DIR1:
cursor=db.query("Book",projection,selection,selectionArgs,null,null,sortOrder);
break;
case ITEM1:
String bookid=uri.getPathSegments().get(1);
cursor=db.query("Book",projection,"id=?",new String[]{bookid},null,null,sortOrder);
break;
case DIR2:
cursor=db.query("Category",projection,selection,selectionArgs,null,null,sortOrder);
break;
case ITEM2:
String categoryid=uri.getPathSegments().get(1);
cursor=db.query("Category",projection,"id=?",new String[]{categoryid},null,null,sortOrder);
break;
default:
break;
}
return cursor;
}
/**
*
* @param uri
* @return 傳入的內容URI來返回相應的MIME類型
*/
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri)){
case DIR1:
return "vnd.android.cursor.dir/vnd.com.wenyi.interview.provider.book";
case ITEM1:
return "vnd.android.cursor.item/vnd.com.wenyi.interview.provider.book";
case DIR2:
return "vnd.android.cursor.dir/vnd.com.wenyi.interview.provider.category";
case ITEM2:
return "vnd.android.cursor.item/vnd.com.wenyi.interview.provider.category";
default:
break;
}
return null;
}
/**
* 添加數據
* @param uri 確定要添加到的表
* @param values 待添加的數據
* @return 添加完成後,返回一個用於表示這條心記錄的URI
*/
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
/**
* 刪除數據
* @param uri 確定刪除哪一張表中的數據
* @param selection 約束刪除哪些行
* @param selectionArgs 約束刪除哪些行
* @return 被刪除的行
*/
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
/**
* 更新數據
* @param uri 更新的表
* @param values 新數據保存在其中
* @param selection 約束更新的行
* @param selectionArgs 約束更新的行
* @return 受影響的行作爲返回值
*/
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
代碼有點長,現在來細細分析:
- 自定義MyContentProvider繼承自ContentProvider,重寫相應的方法,方法都有解釋
- 四個常量分別表示訪問Book表中的說有數據,訪問Book表中的單條數據,訪問Category表中的所有數據和訪問Category表中的單條數據
- 在manifest中進行註冊,
<provider
android:exported="true"
android:enabled="true"
android:authorities="com.wenyi.interview.provider"
android:name=".FourComponet.contentProvider.MyContentProvider"/> - 代碼中,UriMatche類,可以用來配皮Uri類型數據,其中,“*”和“#”分別表示匹配任意長度的任意字符和任意長度的數字。
- 創建MyDatabaseHelper對象,繼承自SQLiteOpenHelper,方便數據可操作,因爲內容提供着只是接口標準,需要和數據存儲方式結合使用
- 只在query中寫了相應代碼,其他方法也需要寫類似代碼
- getType方法中,返回值很奇特。實際上,這是MIME字符格式,MIME主要由3部分組成,在Android中,格式爲: a: 必須以vnd開頭 b: 如果內容Uri以路徑結尾,則後面接 android.cursor.dir/。如果內容Uri以id結尾,則後面接android.cursor.item/ c: 在最後面接 vnd.authority.path
- 這樣,我們的內容提供器就創建完成了,通過這種機制,必須知道URI,才能順利的訪問該數據庫中的內容,比保證了可訪問,又保證了安全性。
工作機制
暫不分析
歡迎閱讀其他系列文章: