Android應用間數據共享之ContentProvider


    通常在android應用中,數據都是在本應用沙盒之內的,其他外部應用不能夠訪問,那么如果一個應用需要訪問另外一個應用的數據,怎么辦呢?那就把另外一個應用的數據公布出來,比如android中的通訊錄數據,這些數據是以ContentProvider方式提供與其他應用訪問的。

    那么我們也可以定義自己的ContentProvider來使跨應用共享數據。數據具體的存貯方式可以為數據庫、文件,持久化或非持久化存儲的其他形式。在這里我們還是使用sqlite數據庫存貯數據吧。

老規矩,先來點基礎知識。

一.基礎知識

1:URI是什么?統一資源標識符,用來標識某一資源的

通常一個Uri主要由以三部分組成:scheme、Authority、path
     1.scheme:ContentProvider(內容提供者)的scheme已經由Android系統規定為:content://
     2.主機名(或Authority):用於唯一標識這個ContentProvider,外部調用者可以根據這個標識來找到它。
     3.路徑(path):可以用來表示我們要操作的數據,路徑的構建應根據業務而定,如下
      要操作persion表中id為5的記錄,可以構建這樣的路徑:/persion/5
      要操作persion表中id為20的記錄的name字段, persion/name/10
      要操作persion表中的所有記錄,可以構建這樣的路徑:/persion
 使用Uri類中的parse()方法獲取Uri: Uri uri = Uri.parse("content://com.dongzi/persion")

 上面Uri的scheme      content://

            Authority:    com.dongzi

            path:             /contact


2、UriMatcher、ContentUrist和ContentResolver

Android系統提供了兩個用於操作Uri的工具類:UriMatcher 和ContentUris

UriMatcher:用於匹配Uri:

View Code
static final int CODES=2;
static final int CODE=1;
static final String AUTHORITY="com.dongzi"; //授權
static final UriMatcher uriMatcher; //Uri匹配
static { //注冊匹配的Uri以及返回碼
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH); //不匹配任何路徑返回-1
uriMatcher.addURI(AUTHORITY, "persion", CODES); //匹配content://com.dongzi/persion 返回2
uriMatcher.addURI(AUTHORITY, "persion/#", CODE); //匹配content://com.dongzi/persion/1234 返回1
}

ContentUris:用於獲取Uri路徑后面的ID部分,它有兩個比較實用的方法:
•         withAppendedId(uri, id)用於為路徑加上ID部分
•         parseId(uri)方法用於從路徑中獲取ID部分

View Code
//為Uri添加ID
Uri uri=Uri.parse("content://"+AUTHORITY+"/persion");
ContentUris.withAppendedId(uri, 1234);
//生成后的Uri為:content://com.dongzi/person/1234

//獲取Uri后面的ID
long id=ContentUris.parseId(Uri.parse("content://com.dongzi/person/1234"));
//得到ID為:1234

ContentResolver提供了如下主要方法:

View Code
@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
//該方法用於供外部應用從ContentProvider刪除數據。
return 0;
}
@Override
public String getType(Uri uri) {
//該方法用於返回當前Url所代表數據的MIME類型。
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
//該方法用於供外部應用往ContentProvider添加數據。
return null;
}
@Override
public boolean onCreate() {
//在其它應用第一次訪問它時被創建。
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//該方法用於供外部應用從ContentProvider中獲取數據。
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
//該方法用於供外部應用更新ContentProvider中的數據。
return 0;
}

這里主要說下Url所代表數據的MIME類型:

如果操作的數據屬於集合類型,那么MIME類型字符串應該以vnd.android.cursor.dir/開頭,

例如:要得到所有person記錄的Uri為content://com.dongzi/person,那么返回的MIME類型字符串應該為:"vnd.android.cursor.dir/person"。

如果要操作的數據屬於非集合類型數據,那么MIME類型字符串應該以vnd.android.cursor.item/開頭,

例如:得到id為1234的person記錄,Uri為content://com.dongzi/person/1234,那么返回的MIME類型字符串為:"vnd.android.cursor.item/person"。

使用ContentResolver操作ContentProvider中的數據

當外部應用需要對ContentProvider中的數據進行添加、刪除、修改和查 詢操作時,可以使用ContentResolver 類來完成,要獲取ContentResolver 對象,可以使用Activity提供的getContentResolver()方法。ContentResolver提供了ContentProvider對應的增、刪、改、查方法。

監聽ContentProvider中數據的變化

如果我們需要得到數據變化通知,可以使用ContentObserver對數據(數據采用uri描述)進行通知更改以及監聽。

View Code
//通知內容以及發生改變,同時注冊了內容監聽,監聽到內容變化,就調用onChange方法
this.getContext().getContentResolver().notifyChange(uri, new ContentObserver(new Handler()){
public void onChange(boolean selfChange) {
//此處可以進行相應的業務處理
}
});

二.實戰

說了那么多,是時候了解ContentProvider的使用了,我們這里采用sqlite存貯數據。當然,我們直接采用聯系人數據不是更好?

1:首先我們定義DBHelper繼承SQLiteOpenHelper,並建表。

View Code
package com.dongzi;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {

static final String DB_NAME = "dongzi.db";
static final int DB_VERSION = 1;
static final String TABLE="persion";
static final String TABLE_COLUMN_NAME="name";
static final String TABLE_COLUMN_PHONE="phone";
static final String CREATE_TABLE = "create table persion(id integer primary key autoincrement,name varchar(40) phone varchar(40))";
static final String DRPO_TABLE="drop table if exists persion";
public DBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);

}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//這里其實是比較版本,然后升級數據庫的,比如說是增加一個字段,或者刪除一個字段,或者增加表
db.execSQL(DRPO_TABLE);
onCreate(db);
}

}

2:然后定義MyContentProvider繼承ContentProvider,並且在類加載時候初始化UriMatcher匹配,以及授權AUTHORITY,同時,這個ContentProvider需要在AndroidManifest.xml中進行注冊,並添加授權。

代碼如下:

View Code
package com.dongzi;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;

public class MyContentProvider extends ContentProvider {

DBHelper dbHelper=null;
//MIME類型
static final String PERSIONS_TYPE="vnd.android.cursor.dir/person";
static final String PERSION_ITEM_TYPE="vnd.android.cursor.item/person";
//返回碼
static final int CODES=2;
static final int CODE=1;
//授權
static final String AUTHORITY="com.dongzi"; //授權
static final UriMatcher uriMatcher; //Uri匹配
static { //注冊匹配的Uri以及返回碼
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH); //不匹配任何路徑返回-1
uriMatcher.addURI(AUTHORITY, "persion", CODES); //匹配content://com.dongzi/persion 返回2
uriMatcher.addURI(AUTHORITY, "persion/#", CODE); //匹配content://com.dongzi/persion/1234 返回1
}

private void init(){
//為Uri添加ID
Uri uri=Uri.parse("content://"+AUTHORITY+"/persion");
ContentUris.withAppendedId(uri, 1234);
//生成后的Uri為:content://com.dongzi/person/1234

//獲取Uri后面的ID
long id=ContentUris.parseId(Uri.parse("content://com.dongzi/person/1234"));
//得到ID為:1234
}


@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//該方法用於供外部應用從ContentProvider刪除數據。
SQLiteDatabase db=dbHelper.getWritableDatabase();
int count=0;
switch(uriMatcher.match(uri)){
case CODES:
count = db.delete(DBHelper.DB_NAME, selection, selectionArgs);
break;
case CODE:
// 下面的方法用於從URI中解析出id,對這樣的路徑content://com.dongzi/persion/1234
// 進行解析,返回值為10
long id = ContentUris.parseId(uri);
String where = "id=" + id;// 刪除指定id的記錄
where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它條件附加上
count = db.delete(DBHelper.DB_NAME, where, selectionArgs);
break;
default:throw new IllegalArgumentException("throw Uri:"+uri.toString());

}
db.close();
return count;
}
@Override
public String getType(Uri uri) {
//該方法用於返回當前Url所代表數據的MIME類型。
switch(uriMatcher.match(uri)){
case CODES:
return PERSIONS_TYPE; //這里CODES代表集合,故返回的是集合類型的MIME
case CODE:
return PERSION_ITEM_TYPE;
default:throw new IllegalArgumentException("throw Uri:"+uri.toString());
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
//該方法用於供外部應用往ContentProvider添加數據。
SQLiteDatabase db= dbHelper.getWritableDatabase();
long id=0;
//匹配Uri
switch(uriMatcher.match(uri)){
//返回碼
case CODES:
id=db.insert(DBHelper.TABLE, DBHelper.TABLE_COLUMN_NAME, values);// 返回的是記錄的行號,主鍵為int,實際上就是主鍵值
return ContentUris.withAppendedId(uri, id);
case CODE:
id=db.insert(DBHelper.TABLE, DBHelper.TABLE_COLUMN_NAME, values);// 返回的是記錄的行號,主鍵為int,實際上就是主鍵值
String path = uri.toString();
return Uri.parse(path.substring(0, path.lastIndexOf("/"))+id); // 替換掉id
default:throw new IllegalArgumentException("throw Uri:"+uri.toString());
}
}
@Override
public boolean onCreate() {
//在其它應用第一次訪問它時被創建。
dbHelper =new DBHelper(getContext());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//該方法用於供外部應用從ContentProvider中獲取數據。
SQLiteDatabase db=dbHelper.getWritableDatabase();
Cursor cursor=null;
switch(uriMatcher.match(uri)){
case CODES:
cursor=db.query(DBHelper.DB_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
case CODE:
//下面的方法用於從URI中解析出id,對這樣的路徑content://com.dongzi/persion/1234
// 進行解析,返回值為10
long id = ContentUris.parseId(uri);
String where = "id=" + id;// 獲取指定id的記錄
where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它條件附加上
cursor=db.query(DBHelper.DB_NAME, projection, where, selectionArgs, null, null, sortOrder);
break;
default:break;
}
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
//該方法用於供外部應用更新ContentProvider中的數據。
return 0;
}

}

如果我們基本了解了上述說的基礎知識,那么這些代碼不難看懂,其實也非常簡單,不直接操作DBHelper類,而是通過ContentProvider間接操作,而操作ContentProvider又是太通過
ContentResolver這個類,實現不同應用都可以訪問這些數據。

 至於更新功能,大家還是自己動手吧。



免責聲明!

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



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