分享

Content Provider

 dddTTLee 2011-04-16



Android程序的主要4部分:

1、Activiyt

2、Broadcast Intent Receiver

3、Service

4、Content Provider

一个ContentProvider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此ContentProvider的各种数据类型。

下面列举一些常用的接口:

1、query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor.

2、insert(Uri uri,ContentValues values):将一组数据插入到Uri指定的地方。

3、update(Uri uri,ContentValues values,String where,String[] selectionArgs):更新Uri指定位置的数据。

4、delete(Uri uri,String where,String[] selectionArgs):删除指定Uri并且符合一定条件的数据。

52、ContentResolver

外界程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用ContentResolver实例。其提供的接口与ContentProvider提供的接口对应:

1、query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor.

2、insert(Uri uri,ContentValues values):将一组数据插入到Uri指定的地方。

3、update(Uri uri,ContentValues values,String where,String[] selectionArgs):更新Uri指定位置的数据。

4、delete(Uri uri,String where,String[] selectionArgs):删除指定Uri并且符合一定条件的数据。

【实例】

提供ContentProvider的程序:TestSQLite_2_ContentProvider

对其进行访问访问的程序:TestContentProvider_useStudentData

下面进行分析:

【1】要为当前应用程序的私有数据定义URI,就需要专门定义个继承自ContentProvider的类,然后实现各个不同的操作所调用的方法。

首先在该应用程序的某个类中定义所有与数据库操作有关的静态字段,以便打包成jar文件供其他应用程序调用(本例中引用打包好的jar包后会有“找不到类”的错误,故直接将该类,连同包名一同复制到另一个程序中,如)

此例子没有新定义一个专门的类来存放这些字段,而是在继承了SQLiteOpenHelper的类StudentData中定义:

package com.shutao.testsqlite2;

public class StudentData extends SQLiteOpenHelper {

/*

 * 分别定义了数据库和表的名称、表中各个字段的名称、数据库的版本号

 */

public final static String DB_NAME = "student";

public final static String TABLE_NAME = "hero";

public final static String SNAME = "name";

public final static String SID = "_id";

public final static int DB_VERSION = 1;

/*

 * AUTHORITY:定义了标识ContentProvider的字符串 ;

 * ITEM和ITEM_ID分别用于UriMatcher(资源标识符匹配器)中对路径item和item/id的匹配号码

 * CONTENT_TYPE和CONTENT_ITEM_TYPE定义了数据的MIME类型。需要注意的是: 单一数据的MIME 类型字符串应该以

 * vnd.android.cursor.item/开头,数据集的MIME类型字符串应该以vnd.android.cursor.dir开头

 * CONTENT_URI定义的是查询当前表数据的content://样式URI

 */

public static final String AUTHORITY = "com.shutao.testsqlite2.provider.studentdata";

public static final int ITEM = 1;

public static final int ITEM_ID = 2;


public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.shutao.testsqlite2.studentdata";

public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.shutao.testsqlite2.studentdata";


public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY

+ "/item");


public StudentData(Context context) {

super(context, DB_NAME, null, DB_VERSION);

}


@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL("CREATE TABLE " + TABLE_NAME + "(" + SID

+ " VARCHAR PRIMARY KEY," + SNAME + " VARCHAR NOT NULL);");

}


@Override

public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {

db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);

}

}


【2】接下来定义一个继承自ContentProvider的类:MyProvider.java,来实现对数据进行操作的各个方法。这里将用到StudentData来辅助得到SQLiteDatabase对象:

package com.shutao.testsqlite2;

public class MyProvider extends ContentProvider {

StudentData sd;

private static final UriMatcher sMatcher;

static {

// 传入匹配码如果大于0表示匹配根路径或传入-1,即常量UriMatcher.NO_MATCH表示不匹配根路径

// addURI()方法是用来增加其他URI匹配路径的:

// 第一个参数代表传入标识ContentProvider的AUTHORITY字符串

// 第二个参数是要匹配的路径,#代表任意数字,另外还可以用*来匹配任意文本

// 第三个参数必须传入一个大于零的匹配码,用于match()方法对相匹配的URI返回相对应的匹配码

sMatcher = new UriMatcher(UriMatcher.NO_MATCH);

sMatcher.addURI(StudentData.AUTHORITY, "item", StudentData.ITEM);

sMatcher.addURI(StudentData.AUTHORITY, "item/#", StudentData.ITEM_ID);


}


/*

 * 每当ContentProvider启动时都会回调onCreate()方法。此方法主要执行一些ContentProvider初始化

 * 的工作,返回true表示初始化成功,返回false则初始化失败。

 */

@Override

public boolean onCreate() {

sd = new StudentData(this.getContext());

return true;

}


// getType()是用来返回数据的MIME类型的方法。使用sMatcher对URI进行匹配,并返回相应的MIME类型字符串

@Override

public String getType(Uri uri) {

switch (sMatcher.match(uri)) {

case StudentData.ITEM:

return StudentData.CONTENT_TYPE;

case StudentData.ITEM_ID:

return StudentData.CONTENT_ITEM_TYPE;

default:

throw new IllegalArgumentException("Unknown URI " + uri);

}

}


/*

 * 插入数据,返回新插入数据的URI,只接受数据集的URI,即指向表的URI

 */

@Override

public Uri insert(Uri uri, ContentValues values) {

SQLiteDatabase sdb = sd.getWritableDatabase();

long rowId;

if (sMatcher.match(uri) != StudentData.ITEM) {

throw new IllegalArgumentException("Unknow URI " + uri);

}

rowId = sdb.insert(StudentData.TABLE_NAME, StudentData.SID, values);

if (rowId > 0) {

Uri noteUri = ContentUris.withAppendedId(StudentData.CONTENT_URI,

rowId);

this.getContext().getContentResolver().notifyChange(noteUri, null);

return noteUri;

}

throw new SQLException("Failed to insert row into " + uri);

}


/*

 * 用于数据的删除,返回的是所影响数据的数目,首先利用数据库辅助对象获取一个SQLiteDatabase对象

 * 然后根据传入Uri用sMatcher进行匹配,对单个数据或数据集进行删除或修改。notifyChange()方法

 * 用来通知注册在次URI上的观察者(observer)数据发生了改变。

 */


@Override

public int delete(Uri uri, String selection, String[] selectionArgs) {

SQLiteDatabase sdb = sd.getWritableDatabase();

int count;

switch (sMatcher.match(uri)) {

case StudentData.ITEM:

count = sdb.delete(StudentData.DB_NAME, selection, selectionArgs);

break;

case StudentData.ITEM_ID:

String id = uri.getPathSegments().get(1);

count = sdb.delete(StudentData.DB_NAME, StudentData.SID

+ "="

+ id

+ (!TextUtils.isEmpty(selection) ? " AND (" + selection

+ ")" : ""), selectionArgs);

break;

default:

throw new IllegalArgumentException("Unknown URI " + uri);

}

this.getContext().getContentResolver().notifyChange(uri, null);

return count;

}


/*

 * 查询数据,将数据装入一个Cursor对象并返回

 */

@Override

public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder) {

SQLiteDatabase sdb = sd.getReadableDatabase();

Cursor c;

switch (sMatcher.match(uri)) {


case StudentData.ITEM:

c = sdb.query(StudentData.TABLE_NAME, projection, selection,

selectionArgs, null, null, sortOrder);

break;

case StudentData.ITEM_ID:

String id = uri.getPathSegments().get(1);

c = sdb.query(StudentData.TABLE_NAME, projection, StudentData.SID

+ "="

+ id

+ (!TextUtils.isEmpty(selection) ? " AND (" + selection

+ ")" : ""), selectionArgs, null, null, sortOrder);

break;

default:

Log.i("sMatcher.match(uri)", String.valueOf(sMatcher.match(uri)));

throw new IllegalArgumentException("Query with unknown URI: " + uri);

}

c.setNotificationUri(getContext().getContentResolver(), uri);

return c;

}


// 更新,与删除类方法类似

@Override

public int update(Uri uri, ContentValues values, String selection,

String[] selectionArgs) {

SQLiteDatabase sdb = sd.getWritableDatabase();

int count;

switch (sMatcher.match(uri)) {

case StudentData.ITEM:

count = sdb.update(StudentData.TABLE_NAME, values, selection,

selectionArgs);

break;

case StudentData.ITEM_ID:

String id = uri.getPathSegments().get(1);

count = sdb.update(StudentData.DB_NAME, values, StudentData.SID

+ "="

+ id

+ (!TextUtils.isEmpty(selection) ? " AND (" + selection

+ ")" : ""), selectionArgs);

break;

default:

throw new IllegalArgumentException("Unknown URI " + uri);

}

this.getContext().getContentResolver().notifyChange(uri, null);

return count;

}

}

【3】完成以上方法以后,还要AndroidManifest.xml中对这个ContentProvider声明:

<provider

android:name="MyProvider"

android:authorities="com.shutao.testsqlite2.provider.studentdata"/>

 <!-- 其中 android:name必须跟定义的ContentProvider的类名一样

 android:authorities则指定了content://样式的URI中标识这个ContentProvider的字符串-->


【4】接下来在另一个程序中使用ContentResolver来操作数据:

package com.shutao.contentprovider;

public class TestContentProvider extends Activity {

private EditText stu_name;

private EditText stu_sid;

Button commit;

ListView studList;

ContentResolver resolver;

// 由于在AlertDialog中用到,所以定义为全局变量。

String sid = "";

private final int DIALOG_IN_USED = 1;


/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

stu_name = (EditText) this.findViewById(R.id.edit_name);

stu_sid = (EditText) this.findViewById(R.id.edit_sid);

commit = (Button) this.findViewById(R.id.butt_add);

studList = (ListView) this.findViewById(R.id.show_stud);

//【重要】得到ContentResolver()

resolver = this.getContentResolver();

String[] projection = { StudentData.SNAME, StudentData.SID };

Cursor c = resolver.query(StudentData.CONTENT_URI, null, null, null,

StudentData.SID);

CursorAdapter adapter = new SimpleCursorAdapter(this,

android.R.layout.simple_list_item_2, c, projection, new int[] {

android.R.id.text1, android.R.id.text2 });

studList.setAdapter(adapter);


commit.setOnClickListener(new OnClickListener() {


@Override

public void onClick(View v) {

if (!(stu_name.getText().length() > 0)) {

Toast.makeText(TestContentProvider.this,

"Please input the student's name!",

Toast.LENGTH_LONG).show();

} else if (!(stu_sid.getText().length() > 0)) {

Toast

.makeText(TestContentProvider.this,

"Please input the student's ID!",

Toast.LENGTH_LONG).show();

} else {

String name = stu_name.getText().toString();

sid = stu_sid.getText().toString();

Uri quri = Uri.parse(StudentData.CONTENT_URI + "/" + sid);

Cursor c = resolver.query(quri, null, null, null, null);

if (c.moveToFirst()) {

// 采用Toast方式提醒

/*

 * Toast.makeText(TestContentProvider.this,

 * "Fail to add!This ID is already in used!",

 * Toast.LENGTH_LONG).show();

 */

// 采用AlertDialog方式提醒

TestContentProvider.this.showDialog(DIALOG_IN_USED);

} else {

ContentValues values = new ContentValues();

values.put(StudentData.SNAME, name);

values.put(StudentData.SID, sid);

resolver.insert(StudentData.CONTENT_URI, values);

}

}

}


});

}

//如果插入的Id已经存在则用提示框进行提示

@Override

protected Dialog onCreateDialog(int id, Bundle args) {

// // TODO Auto-generated method stub

// return super.onCreateDialog(id, args);

switch (id) {

case DIALOG_IN_USED:

return new AlertDialog.Builder(this).setTitle("Fail to add!")

.setIcon(R.drawable.fail).setMessage(

"This ID:" + sid + " is already in used!")

.setPositiveButton("确定",

// 特别注意:必须为:new DialogInterface.OnClickListener()

// 不能为new

// OnClickListener(),否则会跟View.OnClickListener冲突。

new DialogInterface.OnClickListener() {


@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

}


}).create();

default:

return super.onCreateDialog(id, args);

}

}

}

53、ContentProvider与ContentResolver中用到的Uri

【注】:在ContentProvider与ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据,如:

1、content://contacts/people/:这个Uri指定的就是全部联系人的数据。

2、content://contacts/people/1:这个Uri指定的就是ID为1的联系人的数据。

Uri一般由3部分组成:

1、"content://"

2、要获得数据的一个字符串片段

3、最后就是ID,如果没有ID则返回全部数据。

但因为URI通常比较长,容易出错,所以在Android当中定义了一些辅助类,并定义了一些常量来代替这些这些字符串的使用,如:

Contacts.People.CONTENT_URI:联系人的URI.

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多