1、ContentProvider簡介
在Android官方指出的Android的數據存儲方式總共有五種,分別是:Shared Preferences、網絡存儲、文件存儲、外儲存儲、SQLite。但是我們知道一般這些存儲都只是在單獨的一個應用程序之中達到一個數據的共享,有時候我們需要操作其他應用程序的一些數據,例如我們需要操作系統裏的媒體庫、通訊錄等,這時我們就可能通過ContentProvider來滿足我們的需求了。
2、爲什麼要選擇ContentProvider?ContentProvider向我們提供了我們在應用程序之前共享數據的一種機制,而我們知道每一個應用程序都是運行在不同的應用程序的,數據和文件在不同應用程序之間達到數據的共享不是沒有可能,而是顯得比較複雜,而正好Android中的ContentProvider則達到了這一需求,比如有時候我們需要操作手機裏的聯繫人,手機裏的多媒體等一些信息,我們都可以用到這個ContentProvider來達到我們所需。
1)ContentProvider爲存儲和獲取數據提供了統一的接口。ContentProvide對數據進行封裝,不用關心數據存儲的細節。使用表的形式來組織數據。
2)使用ContentProvider可以在不同的應用程序之間共享數據。
3)Android爲常見的一些數據提供了默認的ContentProvider(包括音頻、視頻、圖片和通訊錄等)。
總的來說使用ContentProvider對外共享數據的好處是統一了數據的訪問方式。
3、Uri介紹
爲系統的每一個資源給其一個名字,比方說通話記錄。
1)每一個ContentProvider都擁有一個公共的URI,這個URI用於表示這個ContentProvider所提供的數據。
2)Android所提供的ContentProvider都存放在android.provider包中。 將其分爲A,B,C,D 4個部分:
A:標準前綴,用來說明一個Content Provider控制這些數據,無法改變的;"content://"
B:URI 的標識,用於唯一標識這個ContentProvider,外部調用者可以根據這個標識來找到它。它定義了是哪個Content Provider提供這些數據。對於第三方應用程序,爲了保證URI標識的唯一性,它必須是一個完整的、小寫的類名。這個標識在 元素的 authorities屬性中說明:一般是定義該ContentProvider的包.類的名稱
C:路徑(path),通俗的講就是你要操作的數據庫中表的名字,或者你也可以自己定義,記得在使用的時候保持一致就可以了;"content://com.bing.provider.myprovider/tablename"
D:如果URI中包含表示需要獲取的記錄的ID;則就返回該id對應的數據,如果沒有ID,就表示返回全部; "content://com.bing.provider.myprovider/tablename/#" #表示數據id。
PS:
路徑(path)可以用來表示我們要操作的數據,路徑的構建應根據業務而定,如下:
1)要操作person表中id爲10的記錄,可以構建這樣的路徑:/person/10
2)要操作person表中id爲10的記錄的name字段, person/10/name
3)要操作person表中的所有記錄,可以構建這樣的路徑:/person
4)要操作xxx表中的記錄,可以構建這樣的路徑:/xxx
5)當然要操作的數據不一定來自數據庫,也可以是文件、xml或網絡等其他存儲方式,如下:要操作xml文件中person節點下的name節點,可以構建這樣的路徑:/person/name
6)如果要把一個字符串轉換成Uri,可以使用Uri類中的parse()方法,如下:Uri uri = Uri.parse("content://com.bing.provider.personprovider/person")
4、UriMatcher類使用介紹
因爲Uri代表了要操作的數據,所以我們經常需要解析Uri,並從Uri中獲取數據。Android系統提供了兩個用於操作Uri的工具類,分別爲UriMatcher和ContentUris 。掌握它們的使用,會便於我們的開發工作。
UriMatcher類用於匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路徑全部給註冊上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.bing.procvide.personprovider/person路徑,返回匹配碼爲1
sMatcher.addURI("com.bing.procvide.personprovider", "person", 1);//添加需要匹配uri,如果匹配就會返回匹配碼
//如果match()方法匹配content://com.bing.provider.personprovider/person/230路徑,返回匹配碼爲2
sMatcher.addURI("com.bing.provider.personprovider", "person/#", 2);//#號爲通配符
switch (sMatcher.match(Uri.parse("content://com.ljq.provider.personprovider/person/10"))) {
case 1
break;
case 2
break;
default://不匹配
break;
}
註冊完需要匹配的Uri後,就可以使用sMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹配就返回匹配碼,匹配碼是調用addURI()方法傳入的第三個參數,假設匹配content://com.ljq.provider.personprovider/person路徑,返回的匹配碼爲15、ContentUris類使用介紹
ContentUris類用於操作Uri路徑後面的ID部分,它有兩個比較實用的方法:
withAppendedId(uri, id)用於爲路徑加上ID部分:
Uri uri = Uri.parse("content://com.bing.provider.personprovider/person")
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成後的Uri爲:content://com.bing.provider.personprovider/person/10
parseId(uri)方法用於從路徑中獲取ID部分:Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person/10")
long personid = ContentUris.parseId(uri);//獲取的結果爲:10
6、使用ContentProvider共享數據1)ContentProvider類主要方法的作用:
public boolean onCreate()//該方法在ContentProvider創建後就會被調用,Android開機後,ContentProvider在其它應用第一次訪問它時纔會被創建。
public Uri insert(Uri uri, ContentValues values)//該方法用於供外部應用往ContentProvider添加數據。
public int delete(Uri uri, String selection, String[] selectionArgs)//該方法用於供外部應用從ContentProvider刪除數據。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)//該方法用於供外部應用更新ContentProvider中的數據。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)//該方法用於供外部應用從ContentProvider中獲取數據。
public String getType(Uri uri)//該方法用於返回當前Url所代表數據的MIME類型。
2)如果操作的數據屬於集合類型,那麼MIME類型字符串應該以vnd.android.cursor.dir/開頭,例如:要得到所有person記錄的Uri爲content://com.bing.provider.personprovider/person,那麼返回的MIME類型字符串應該爲:"vnd.android.cursor.dir/person"。3)如果要操作的數據屬於非集合類型數據,那麼MIME類型字符串應該以vnd.android.cursor.item/開頭,例如:得到id爲10的person記錄,Uri爲content://com.bing.provider.personprovider/person/10,那麼返回的MIME類型字符串爲:"vnd.android.cursor.item/person"。
7、ContentResolver操作ContentProvider中的數據
1)當外部應用需要對ContentProvider中的數據進行添加、刪除、修改和查詢操作時,可以使用ContentResolver 類來完成,要獲取ContentResolver 對象,可以使用Activity提供的getContentResolver()方法。
2)ContentResolver 類提供了與ContentProvider類相同簽名的四個方法:
public Uri insert(Uri uri, ContentValues values)//該方法用於往ContentProvider添加數據。
public int delete(Uri uri, String selection, String[] selectionArgs)//該方法用於從ContentProvider刪除數據。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)//該方法用於更新ContentProvider中的數據。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)//該方法用於從ContentProvider中獲取數據。
這些方法的第一個參數爲Uri,代表要操作的ContentProvider和對其中的什麼數據進行操作,其實和contentprovider裏面的方法是一樣的.他們所對應的數據,最終是會被傳到我們在之前程序裏面定義的那個contentprovider類的方法,假設給定的是:Uri.parse("content://com.bing.providers.personprovider/person/10"),那麼將會對主機名爲com.bing.providers.personprovider的ContentProvider進行操作,操作的數據爲person表中id爲10的記錄。使用ContentResolver對ContentProvider中的數據進行添加、刪除、修改和查詢操作:
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.bing.provider.personprovider/person");
//添加一條記錄
ContentValues values = new ContentValues();
values.put("name", "bingxin");
values.put("age", 25);
resolver.insert(uri, values);
//獲取person表中所有記錄
Cursor cursor = resolver.query(uri, null, null, null, "personid desc");
while(cursor.moveToNext()){
Log.i("ContentTest", "personid="+ cursor.getInt(0)+ ",name="+ cursor.getString(1));
}
//把id爲1的記錄的name字段值更改新爲zhangsan
ContentValues updateValues = new ContentValues();
updateValues.put("name", "zhangsan");
Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
resolver.update(updateIdUri, updateValues, null, null);
//刪除id爲2的記錄
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);
8、監聽ContentProvider中數據的變化如果ContentProvider的訪問者需要知道ContentProvider中的數據發生變化,可以在ContentProvider發生數據變化時調用getContentResolver().notifyChange(uri, null)來通知註冊在此URI上的訪問者,例子如下:
public class PersonContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("person", "personid", values);
getContext().getContentResolver().notifyChange(uri, null);
}
}
如果ContentProvider的訪問者需要得到數據變化通知,必須使用ContentObserver對數據(數據採用uri描述)進行監聽,當監聽到數據變化通知時,系統就會調用ContentObserver的onChange()方法:getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),
true, new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver{
public PersonObserver(Handler handler) {
super(handler);
}
public void onChange(boolean selfChange) {
//此處可以進行相應的業務處理
}
}
二、ContentProvider的實現過程
1、定義一個CONTENT_URI常量,提供了訪問ContentProvider的標識符
final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transportationprovider");
其中:content是協議Com.exmaple.codelab.transportationprovider是類名,包含完整的包名。
Uri.parse將一個字符串轉換成Uri類型。
如果Provider包含子表,同樣定義包含字表的CONTENT_URI。
content://com.example.codelab.transportationprovider/train
content://com.example.codelab.transportationprovider/air/domestic
content://com.example.codelab.transportationprovider/air/international
然後定義列,確保裏面包含一個_id的列。2、定義一個類,繼承ContentProvider
public class FirstContentProvider extends ContentProvider
先介紹一下ContentProvider用到的UriMatcher。UriMatcher的一個重要的函數是match(Uri uri)。這個函數可以匹配Uri,根據傳入的不同Uri返回不同的自定義整形值,以表明Uri訪問的不同資源的類型。例如:
public static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(Book.AUTHORITY, "item", Book.ITEM);
uriMatcher.addURI(Book.AUTHORITY, "item/#", Book.ITEM_ID);
}
}
這裏UriMatcher類型的靜態字段是用來匹配傳入到ContentProvider中的Uri的類。其構造方法傳入的匹配碼是使用match()方法匹配根路徑時返回的值,這個匹配碼可以爲一個大於零的數表示匹配根路徑或傳入-1,即常量UriMatcher.NO_MATCH表示不匹配根路徑。
addURI()方法是用來增加其他URI匹配路徑的
第一個參數傳入標識ContentProvider的AUTHORITY字符串。
第二個參數傳入需要匹配的路徑,這裏的#號爲通配符,代表匹配任意數字,另外還可以用*來匹配任意文本。
第三個參數必須傳入一個大於零的匹配碼,用於match()方法對相匹配的URI返回相對應的匹配碼。 例如:sMatcher.addURI(“com.test.provider.personprovider”,“person”, 1);如果match()方法匹配content://com.test.provider.personprovider/person路徑,返回匹配碼爲1。
3、實現query,insert,update,delete,getType和onCreate方法。
4、在AndroidManifest.xml當中進行聲明。
三、實例
1、常量類
/**
* 提供ContentProvider對外的各種常量,當外部數據需要訪問的時候,就可以參考這些常量操作數據。
* @author HB
*
*/
public class ContentData {
public static final String AUTHORITY = "hb.android.contentProvider";
public static final String DATABASE_NAME = "teacher.db";
//創建 數據庫的時候,都必須加上版本信息;並且必須大於4
public static final int DATABASE_VERSION = 4;
public static final String USERS_TABLE_NAME = "teacher";
public static final class UserTableData implements BaseColumns {
public static final String TABLE_NAME = "teacher";
//Uri,外部程序需要訪問就是通過這個Uri訪問的,這個Uri必須的唯一的。
public static final Uri CONTENT_URI = Uri.parse("content://"+ AUTHORITY + "/teacher");
// 數據集的MIME類型字符串則應該以vnd.android.cursor.dir/開頭
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/hb.android.teachers";
// 單一數據的MIME類型字符串應該以vnd.android.cursor.item/開頭
public static final String CONTENT_TYPE_ITME = "vnd.android.cursor.item/hb.android.teacher";
/* 自定義匹配碼 */
public static final int TEACHERS = 1;
/* 自定義匹配碼 */
public static final int TEACHER = 2;
public static final String TITLE = "title";
public static final String NAME = "name";
public static final String DATE_ADDED = "date_added";
public static final String SEX = "SEX";
public static final String DEFAULT_SORT_ORDER = "_id desc";
public static final UriMatcher uriMatcher;
static {
// 常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 如果match()方法匹配content://hb.android.teacherProvider/teachern路徑,返回匹配碼爲TEACHERS
uriMatcher.addURI(ContentData.AUTHORITY, "teacher", TEACHERS);
// 如果match()方法匹配content://hb.android.teacherProvider/teacher/230,路徑,返回匹配碼爲TEACHER
uriMatcher.addURI(ContentData.AUTHORITY, "teacher/#", TEACHER);
}
}
}
PS:在創建UriMatcher對象uriMatcher時,我們傳給構造函數的參數爲UriMatcher.NO_MATCH,它表示當uriMatcher不能匹配指定的URI時,就返回代碼UriMatcher.NO_MATCH。接下來增加了三個匹配規則,分別是uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(ContentData.AUTHORITY, "teacher", TEACHERS); uriMatcher.addURI(ContentData.AUTHORITY, "teacher/#", TEACHER);
它們的匹配碼分別是teacher.ITEM、teacher.ITEM_ID和teacher.ITEM_POS,其中,符號#表示匹配任何數字。
2、SQLite操作類DBOpenHelper
/**
* 這個類繼承SQLiteOpenHelper抽象類,用於創建數據庫和表。創建數據庫是調用它的父類構造方法創建。
* @author HB
*/
public class DBOpenHelper extends SQLiteOpenHelper {
// 在SQLiteOepnHelper的子類當中,必須有該構造函數,用來創建一個數據庫;
public DBOpenHelper(Context context, String name, CursorFactory factory,
int version) {
// 必須通過super調用父類當中的構造函數
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
// public DBOpenHelper(Context context, String name) {
// this(context, name, VERSION);
// }
public DBOpenHelper(Context context, String name, int version) {
this(context, name, null, version);
}
/**
* 只有當數據庫執行創建 的時候,纔會執行這個方法。如果更改表名,也不會創建,只有當創建數據庫的時候,纔會創建改表名之後 的數據表
*/
@Override
public void onCreate(SQLiteDatabase db) {
System.out.println("create table");
db.execSQL("create table " + ContentData.UserTableData.TABLE_NAME
+ "(" + ContentData.UserTableData._ID
+ " INTEGER PRIMARY KEY autoincrement,"
+ ContentData.UserTableData.NAME + " varchar(20),"
+ ContentData.UserTableData.TITLE + " varchar(20),"
+ ContentData.UserTableData.DATE_ADDED + " long,"
+ ContentData.UserTableData.SEX + " boolean)" + ";");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
3、內容提供者代碼/**
* 這個類給外部程序提供訪問內部數據的一個接口
* @author HB
*
*/
public class TeacherContentProvider extends ContentProvider {
private DBOpenHelper dbOpenHelper = null;
// UriMatcher類用來匹配Uri,使用match()方法匹配路徑時返回匹配碼
/**
* 是一個回調函數,在ContentProvider創建的時候,就會運行,第二個參數爲指定數據庫名稱,如果不指定,就會找不到數據庫;
* 如果數據庫存在的情況下是不會再創建一個數據庫的。(當然首次調用 在這裏也不會生成數據庫必須調用SQLiteDatabase的 getWritableDatabase,getReadableDatabase兩個方法中的一個纔會創建數據庫)
*/
@Override
public boolean onCreate() {
//這裏會調用 DBOpenHelper的構造函數創建一個數據庫;
dbOpenHelper = new DBOpenHelper(this.getContext(), ContentData.DATABASE_NAME, ContentData.DATABASE_VERSION);
return true;
}
/**
* 當執行這個方法的時候,如果沒有數據庫,他會創建,同時也會創建表,但是如果沒有表,下面在執行insert的時候就會出錯
* 這裏的插入數據也完全可以用sql語句書寫,然後調用 db.execSQL(sql)執行。
*/
@Override
public Uri insert(Uri uri, ContentValues values){
//獲得一個可寫的數據庫引用,如果數據庫不存在,則根據onCreate的方法裏創建;
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
long id = 0;
switch (uriMatcher.match(uri)) {
case TEACHERS:
id = db.insert("teacher", null, values); // 返回的是記錄的行號,主鍵爲int,實際上就是主鍵值
return ContentUris.withAppendedId(uri, id);
case TEACHER:
id = db.insert("teacher", null, values);
String path = uri.toString();
return Uri.parse(path.substring(0, path.lastIndexOf("/"))+id); // 替換掉id
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
int count = 0;
switch (uriMatcher.match(uri)) {
case TEACHERS:
count = db.delete("teacher", selection, selectionArgs);
break;
case TEACHER:
// 下面的方法用於從URI中解析出id,對這樣的路徑content://hb.android.teacherProvider/teacher/10
// 進行解析,返回值爲10
long personid = ContentUris.parseId(uri);
String where = "_ID=" + personid; // 刪除指定id的記錄
where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : ""; // 把其它條件附加上
count = db.delete("teacher", where, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
db.close();
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
int count = 0;
switch (uriMatcher.match(uri)) {
case TEACHERS:
count = db.update("teacher", values, selection, selectionArgs);
break;
case TEACHER:
// 下面的方法用於從URI中解析出id,對這樣的路徑content://com.ljq.provider.personprovider/person/10
// 進行解析,返回值爲10
long personid = ContentUris.parseId(uri);
String where = "_ID=" + personid;// 獲取指定id的記錄
where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它條件附加上
count = db.update("teacher", values, where, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
db.close();
return count;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case TEACHERS:
return CONTENT_TYPE;
case TEACHER:
return CONTENT_TYPE_ITME;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
switch (uriMatcher.match(uri)) {
case TEACHERS:
return db.query("teacher", projection, selection, selectionArgs, null, null, sortOrder);
case TEACHER:
// 進行解析,返回值爲10
long personid = ContentUris.parseId(uri);
String where = "_ID=" + personid;// 獲取指定id的記錄
where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它條件附加上
return db.query("teacher", projection, where, selectionArgs, null, null, sortOrder);
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
}
PS:1、這裏我們在ArticlesProvider類的內部中定義了一個DBHelper類,它繼承於SQLiteOpenHelper類,它用是用輔助我們操作數據庫的。使用這個DBHelper類來輔助操作數據庫的好處是隻有當我們第一次對數據庫時行操作時,系統纔會執行打開數據庫文件的操作。拿我們這個例子來說,只有第三方應用程序第一次調用query、insert、update或者delete函數來操作數據庫時,我們纔會真正去打開相應的數據庫文件。這樣在onCreate函數裏,就不用執行打開數據庫的操作,因爲這是一個耗時的操作,而在onCreate函數中,要避免執行這些耗時的操作。
2、我們在實現自己的Content Provider時,必須繼承於ContentProvider類,並且實現以下六個函數:
onCreate()//用來執行一些初始化的工作
query(Uri, String[], String, String[], String)//用來返回數據給調用者
insert(Uri, ContentValues)/用來插入新的數據
update(Uri, ContentValues, String, String[])//用來更新已有的數據
delete(Uri, String, String[])//用來刪除數據
getType(Uri)//用來返回數據的MIME類型
4、manifestPS:
在配置Content Provider的時候,最重要的就是要指定它的authorities屬性了,只有配置了這個屬性,第三方應用程序才能通過它來找到這個Content Provider。這要需要注意的,這裏配置的authorities屬性的值是和我們前面在Articles.java文件中定義的AUTHORITY常量的值是一致的。另外一個屬性multiprocess是一個布爾值,它表示這個Content Provider是否可以在每個客戶進程中創建一個實例,這樣做的目的是爲了減少進程間通信的開銷。這裏我們爲了減少不必要的內存開銷,把屬性multiprocess的值設置爲false,使得系統只能有一個Content Provider實例存在,它運行在自己的進程中。在這個配置文件裏面,我們還可以設置這個Content Provider的訪問權限,這裏我們爲了簡單起見,就不設置權限了。
6、佈局文件
7、activity
package hb.android.contentProvider;
import java.util.Date;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/**
* 這個類用來測試ContentProvider是否可用。通過 給定的uri訪問,數據庫;
*
* @author HB
*
*/
public class TeacherActivity extends Activity {
Button insert;
Button query;
Button update;
Button delete;
Button querys;
Uri uri = Uri.parse("content://hb.android.contentProvider/teacher");
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
insert = (Button) findViewById(R.id.insert);
query = (Button) findViewById(R.id.query);
update = (Button) findViewById(R.id.update);
delete = (Button) findViewById(R.id.delete);
querys = (Button) findViewById(R.id.querys);
// 綁定監聽器的兩種方法一;
insert.setOnClickListener(new InsertListener());
query.setOnClickListener(new QueryListener());
// 方法二
update.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
ContentResolver cr = getContentResolver();
ContentValues cv = new ContentValues();
cv.put("name", "huangbiao");
cv.put("date_added", (new Date()).toString());
int uri2 = cr.update(uri, cv, "_ID=?", new String[]{"3"});
System.out.println("updated"+":"+uri2);
}
});
delete.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
ContentResolver cr = getContentResolver();
cr.delete(uri, "_ID=?", new String[]{"2"});
}
});
querys.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
ContentResolver cr = getContentResolver();
// 查找id爲1的數據
Cursor c = cr.query(uri, null, null,null, null);
System.out.println(c.getCount());
c.close();
}
});
}
class InsertListener implements OnClickListener {
public void onClick(View v) {
// TODO Auto-generated method stub
ContentResolver cr = getContentResolver();
ContentValues cv = new ContentValues();
cv.put("title", "jiaoshou");
cv.put("name", "jiaoshi");
cv.put("sex", true);
Uri uri2 = cr.insert(uri, cv);
System.out.println(uri2.toString());
}
}
class QueryListener implements OnClickListener {
public void onClick(View v) {
// TODO Auto-generated method stub
ContentResolver cr = getContentResolver();
// 查找id爲1的數據
Cursor c = cr.query(uri, null, "_ID=?", new String[] { "1" }, null);
//這裏必須要調用 c.moveToFirst將遊標移動到第一條數據,不然會出現index -1 requested , with a size of 1錯誤;cr.query返回的是一個結果集。
if (c.moveToFirst() == false) {
// 爲空的Cursor
return;
}
int name = c.getColumnIndex("name");
System.out.println(c.getString(name));
c.close();
}
}
}
最終的效果如下:組件Content Provider中的數據更新通知機制和Android系統中的廣播(Broadcast)通知機制的實現思路是相似的。
在Android的廣播機制中,首先是接收者對自己感興趣的廣播進行註冊,接着當發送者發出這些廣播時,接收者就會得到通知了。更多關於Android系統的廣播機制的知識,可以參考前面Android四大組件--Broadcast Receiver詳解這一文章。
然而,Content Provider中的數據監控機制與Android系統中的廣播機制又有三個主要的區別,
一是前者是通過URI來把通知的發送者和接收者關聯在一起的,而後者是通過Intent來關聯的,
二是前者的通知註冊中心是由ContentService服務來扮演的,而後者是由ActivityManagerService服務來扮演的,
三是前者負責接收數據更新通知的類必須要繼承ContentObserver類,而後者要繼承BroadcastReceiver類。
之所以會有這些區別,是由於Content Proivder組件的數據共享功能本身就是建立在URI的基礎之上的,因此專門針對URI來設計另外一套通知機制會更實用和方便,而Android系統的廣播機制是一種更加通用的事件通知機制,它的適用範圍會更廣泛一些。