使用ContentProvider存储数据
1 简介
ContentProvider是android数据存储实现方式之一,它向应用程序之间的数据共享提供了一种机制,它会把数据以表格数据组织的方式对外提供。它具有以下特性:
1)、ContentProvider为存储和获取数据提供了统一的接口。ContentProvide对数据进行封装,不用关心数据存储的细节。使用表的形式来组织数据。
2)、使用ContentProvider可以在不同的应用程序之间共享数据。
3)、Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。
2 Uri
Uri是系统为资源提供的标识,android为每个ContentProvider提供了一个公共的URI,通过这个URI来与ContentProvider提供的数据进行交互。
一个URI的例子如下
content://com.example.test2/member/1
这个URI包括4个部分:
1. content://ContentProvider的scheme,表示由ContentProvider控制数据,由android系统规定。
2. com.example.test2:ContentProvider的authority,用于唯一标识ContentProvider,外部应用通过此标识与对应的ContentProvider数据进行交互,为了保证它的唯一性,通常我们把它写成和应用程序一样的包名。
3. member:ContentProvider的Path,表示我们要操作的具体数据,通常是一张表的名字。
4. 1:这个也是ContentProvider的Path,标识我们要操作具体的Item,通常表示一张表的哪条数据。如果Uri没有包含这个标识,则表示要操作全部数据。
3 Uri解析
通常我们解析Uri使用android系统提供的操作Uri工具类:UriMatcher。UriMatcher类用来匹配Uri,对匹配结果返回对应的匹配码。
UriMatcher有个addURI方法,用于注册需要匹配的Uri。
它的原型是:
public voidaddURI(String authority, String path, int code)
注册完成之后可以使用UriMatcher类的match()方法匹配Uri,如果匹配成功就返回相应的匹配码,匹配码是使用addURI()注册Uri时传入的第3个参数,注册时传入的匹配码需要唯一。
操作Uri的工具类还有一个ContentUris类,用于操作Uri路径后面的Id,没有深入研究过。。
4 ContentProvider的getType()方法
ContentProvider里面有一个getType()方法,它通过传入的Uri返回一个字符串,具体说明可以参看getType()的注释:
* Implement this to handle requests forthe MIME type of the data at the
* given URI. The returned MIME type should start with
* <code>vnd.android.cursor.item</code>for a single record,
* or <code>vnd.android.cursor.dir/</code>for multiple items.
大概意思是他能够根据给定的Uri返回MIME type类型的数据,当单条记录时,必须以vnd.android.cursor.item开始,当是多条记录是必须以
vnd.android.cursor.dir/开始。
我的理解是:MIME类型本质是字符串,只不过是增加了特别的限制,例如我们要操作单条记录时,必须以vnd.android.cursor.item开始,当我们要操作多条记录时,必须以
vnd.android.cursor.dir/开始,后面可以增加自己定义的字符串,这样做系统可以通过前面部分就知道我们所要做的操作。
5 ContentProvider主要方法
public boolean onCreate():
ContentProvider创建时调用
public String getType(Uri uri):
返回给定Uri对应的MIME类型字符串
public Uri insert(Uri uri, ContentValuesvalues)
供外部应用往ContentProvider插入数据
public int update(Uri uri, ContentValuesvalues, String selection,String[] selectionArgs):
供外部应用更新ContentProvider数据
public int delete(Uri uri, Stringselection, String[] selectionArgs):
供外部应用删除ContentProvider数据
public Cursor query(Uri uri, String[]projection, String selection,String[] selectionArgs, String sortOrder):
供外部应用查询ContentProvider数据
6 ContentProvider操作
外部应用可以通过ContentResolver类来完成对ContentProvider的数据操作,这些数据操作包括insert,delete,update,update,分别对应上述ContentProvider提供的4个数据操作方法。
public final Uri insert(Uri url,ContentValues values):
往ContentProvider插入数据
public final int update(Uri uri,ContentValues values, String where,String[] selectionArgs):
更新ContentProvider数据
public final int delete(Uri url, Stringwhere, String[] selectionArgs):
删除ContentProvider数据
public final Cursor query(Uri uri,String[] projection,String selection, String[] selectionArgs, String sortOrder)
查询ContentProvider数据
public final Cursor query(final Uriuri, String[] projection,String selection, String[] selectionArgs, StringsortOrder,CancellationSignal cancellationSignal)
查询ContentProvider数据,最后一个参数代表取消信号监听器
7 ContentProvider使用方法
1. 定义CONTENT_URI常量,用来表示ContentProvider的访问标识符。
2. 定义ContentProvider的子类。
3. 定义UriMatcher实例,添加需要匹配的Uri
4. 实现getType()方法,返回对应Uri的MIME字符串。
5. 实现增删改查的方法
6. 在AndroidManifest.xml对自定义的ContentProvider进行注册。
7. 使用ContentResolver实例进行增删改查操作
8 测试代码
常量类:
public interface Database {
public static final String AUTHORITY = "com.example.test2";
// 表示member表的元数据定义,直接继承_ID和_COUNT静态常量
public static interface MemberTableMetaData extends BaseColumns {
// 数据表的名称
public static final String TABLE_NAME = "member";
public static final Uri CONTENT_URI = Uri.parse("content://"
+ AUTHORITY + "/" + TABLE_NAME);
// 取得member表中的所有数据
public static final String CONTACT_LIST = "vnd.android.cursor.dir/member";
// 取得一个member信息,相当于是按照ID查询
public static final String CONTACT_ITEM = "vnd.android.cursor.item/member";
// 表示member.name字段名称
public static final String MEMBER_NAME = "name";
// 表示member.age字段名称
public static final String MEMBER_AGE = "age";
// 表示member.birthday字段名称
public static final String MEMBER_BIRTHDAY = "birthday";
// 显示时的排序字段
public static final String SORT_ORDER = "_id DESC";
}
}
SQLite类
public class Helper extends SQLiteOpenHelper { // 继承SQLiteOpenHelper类
private static final String DATABASENAME = "contentprovider.db" ; // 数据库名称
private static final int DATABASEVERSION = 5 ; // 数据库名称
private static final String TABLENAME = "member" ; // 数据表名称
public Helper(Context context) {
super(context, DATABASENAME, null, DATABASEVERSION); // 调用父类构造
}
@Override
public void onCreate(SQLiteDatabase db) { // 创建数据表
String sql = "CREATE TABLE " + TABLENAME + " ("
+ Database.MemberTableMetaData._ID
+ " INTEGER PRIMARY KEY ,"
+ Database.MemberTableMetaData.MEMBER_NAME
+ " VARCHAR(50) NOT NULL ,"
+ Database.MemberTableMetaData.MEMBER_AGE
+ " INTEGER NOT NULL ,"
+ Database.MemberTableMetaData.MEMBER_BIRTHDAY
+ " DATE NOT NULL)"; // SQL语句
db.execSQL(sql) ; // 执行SQL语句
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "DROP TABLE IF EXISTS " + TABLENAME ; // SQL语句
db.execSQL(sql); // 执行SQL语句
this.onCreate(db); // 创建表
}
}
ContentProvider
public class Member extends ContentProvider { // 继承ContentProvider
private static UriMatcher uriMatcher = null; // 定义UriMatcher对象
private static final int GET_MEMBER_LIST = 1; // 查询全部的常量标记
private static final int GET_MEMBER_ITEM = 2; // 根据ID查询的常量标记
private Helper helper = null; // 数据库操作类对象
static { // 静态代码块
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); // 实例化UriMatcher
uriMatcher.addURI(Database.AUTHORITY, "member",
GET_MEMBER_LIST); // 增加匹配地址
uriMatcher.addURI(Database.AUTHORITY, "member/#",
GET_MEMBER_ITEM); // 增加匹配地址
}
@Override
public boolean onCreate() {
System.out.println("------------> onCreate");
this.helper = new Helper(super.getContext()); // 实例化DatabaseHelper
return false; // 操作成功
}
@Override
public String getType(Uri uri) { // 得到MIME
System.out.println("------------> getType");
switch (uriMatcher.match(uri)) { // 匹配传入的URI
case GET_MEMBER_LIST: // 满足条件
return Database.MemberTableMetaData.
CONTACT_LIST; // 返回所有member信息
case GET_MEMBER_ITEM: { // 满足条件
return Database.MemberTableMetaData.
CONTACT_ITEM; // 返回一个member信息
}
default: // 不匹配时返回默认
throw new UnsupportedOperationException("Not Support Operation:" + uri); // 抛出异常
}
}
@Override
public Uri insert(Uri uri, ContentValues values) { // 数据增加
System.out.println("------------> insert");
SQLiteDatabase db = this.helper.getWritableDatabase(); // 取得数据库操作对象
long id = 0; // 增加之后的id
switch (uriMatcher.match(uri)) { // 匹配传入的UR
case GET_MEMBER_LIST: // 满足条件
id = db.insert(Database.MemberTableMetaData.TABLE_NAME,
Database.MemberTableMetaData._ID,
values); // 执行插入
return ContentUris.withAppendedId(uri, id); // 返回Uri后面追加ID
case GET_MEMBER_ITEM: // 满足条件
id = db.insert(Database.MemberTableMetaData.TABLE_NAME,
Database.MemberTableMetaData._ID,
values); // 执行插入
String uriPath = uri.toString(); // 取出地址
String path = uriPath.substring(0,
uriPath.lastIndexOf("/")) + id; // 建立新的Uri地址
return Uri.parse(path); // 返回一个member信息
default: // 不匹配时返回默认
throw new UnsupportedOperationException("Not Support Operation:" + uri); // 抛出异常
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) { // 更新操作
System.out.println("------------> update");
SQLiteDatabase db = this.helper.getWritableDatabase(); // 取得数据库操作对象
int result = 0; // 操作结果
switch (uriMatcher.match(uri)) { // 匹配传入的URI
case GET_MEMBER_LIST: // 满足条件
result = db.update(
Database.MemberTableMetaData.TABLE_NAME,
values, null, null); // 更新记录
break;
case GET_MEMBER_ITEM: // 满足条件
long id = ContentUris.parseId(uri); // 取出传过来的id
String where = "_id=" + id; // 更新条件
result = db.update(
Database.MemberTableMetaData.TABLE_NAME,
values, where, selectionArgs); // 执行更新操作
break;
default: // 不匹配时返回默认
throw new UnsupportedOperationException("Not Support Operation:"
+ uri); // 抛出异常
}
return result; // 返回更新的行数
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
System.out.println("------------> delete");
SQLiteDatabase db = this.helper.getWritableDatabase(); // 取得数据库操作对象
int result = 0; // 操作结果
switch (uriMatcher.match(uri)) { // 匹配传入的UR
case GET_MEMBER_LIST: // 满足条件
result = db.delete(
Database.MemberTableMetaData.TABLE_NAME,
selection, selectionArgs); // 删除数据
break;
case GET_MEMBER_ITEM: // 满足条件
long id = ContentUris.parseId(uri); // 取得传入的id
String where = "_id=" + id; // 删除语句
result = db.delete(
Database.MemberTableMetaData.TABLE_NAME, where,
selectionArgs); // 删除数据
break;
default: // 不匹配时返回默认
throw new UnsupportedOperationException("Not Support Operation:"
+ uri); // 抛出异常
}
return result; // 删除的行数
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) { // 查询操作
System.out.println("------------> query");
SQLiteDatabase db = this.helper.getWritableDatabase(); // 取得数据库操作对象
switch (uriMatcher.match(uri)) { // 匹配传入的UR
case GET_MEMBER_LIST: // 满足条件
return db
.query(Database.MemberTableMetaData.TABLE_NAME,
projection, selection, selectionArgs, null, null,
sortOrder); // 查询
case GET_MEMBER_ITEM: // 满足条件
long id = ContentUris.parseId(uri); // 取出传入ID
String where = "_id=" + id; // 查询条件
return db.query(
Database.MemberTableMetaData.TABLE_NAME,
projection, where, selectionArgs, null, null,
sortOrder); // 查询操作
default: // 不匹配时返回默认
throw new UnsupportedOperationException("Not Support Operation:"
+ uri); // 抛出异常
}
}
}
Activity:
public class Contentprovider extends Activity {
private Button insertBut,updateBut,deleteBut,queryBut ; // 定义按钮组件
private ListView membersList ; // 定义ListView
private TextView mainInfo = null; // 操作提示
public long testInsert(String name, int age, String birthday)
throws Exception { // 测试增加操作
ContentResolver contentResolver = null ; // 定义ContentResolver
contentResolver = super.getContentResolver(); // 取得ContentResolver
ContentValues values = new ContentValues(); // 设置内容
values.put(Database.MemberTableMetaData.MEMBER_NAME, name); // 设置name字段内容
values.put(Database.MemberTableMetaData.MEMBER_AGE, age); // 设置age字段内容
values.put(Database.MemberTableMetaData.MEMBER_BIRTHDAY, birthday); // 设置birthday字段内容
Uri resultUri = contentResolver.insert(Database.MemberTableMetaData.CONTENT_URI, values); // 执行增加操作
return ContentUris.parseId(resultUri) ; // 解析ID返回
}
public long testUpdate(String _id, String name, int age, String birthday)
throws Exception { // 测试修改操作
long result = 0 ; // 保存返回结果
ContentResolver contentResolver = null ; // 定义ContentResolver
contentResolver = super.getContentResolver(); // 取得ContentResolver
ContentValues values = new ContentValues(); // 设置内容
values.put(Database.MemberTableMetaData.MEMBER_NAME, name); // 设置name字段内容
values.put(Database.MemberTableMetaData.MEMBER_AGE, age); // 设置age字段内容
values.put(Database.MemberTableMetaData.MEMBER_BIRTHDAY, birthday); // 设置birthday字段内容
if(_id == null || "".equals(_id)) { // 根据id更新
result = contentResolver.update(Database.MemberTableMetaData.CONTENT_URI, values, null, null); // 更新操作
} else { // 更新全部
result = contentResolver.update(Uri.withAppendedPath(Database.MemberTableMetaData.CONTENT_URI, _id),values, null, null); // 更新操作
}
return result; // 返回更新结果
}
public long testDelete(String _id) throws Exception { // 测试删除操作
ContentResolver contentResolver = null ; // 定义ContentResolver
contentResolver = super.getContentResolver(); // 取得ContentResolver
long result = 0 ; // 更新记录数
if (_id == null || "".equals(_id)) { // 删除全部数据
result = contentResolver.delete(Database.MemberTableMetaData.CONTENT_URI, null, null); // 执行删除操作
} else {
result = contentResolver.delete(Uri.withAppendedPath(Database.MemberTableMetaData.CONTENT_URI, _id),null, null); // 执行删除操作
}
return result; // 返回更新记录数
}
public Cursor testQuery(String id) throws Exception { // 测试增加操作
if(id==null || "".equals(id)){ // 查询全部
return super.getContentResolver().query(
Database.
MemberTableMetaData.CONTENT_URI, null, null, null,
Database.
MemberTableMetaData.SORT_ORDER); // 执行查询操作
} else { // 根据id查询
return super
.getContentResolver()
.query(Uri.withAppendedPath(
Database.MemberTableMetaData.CONTENT_URI,
id), null, null, null,
Database.
MemberTableMetaData.SORT_ORDER); // 执行查询操作
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.a130912_activity_contentprovider_1); // 指定布局管理器
// 依次取得Button、TextView、ListView组件的实例
this.membersList = (ListView) super.findViewById(R.id.membersList);
this.insertBut = (Button) super.findViewById(R.id.insertBut) ;
this.updateBut = (Button) super.findViewById(R.id.updateBut) ;
this.deleteBut = (Button) super.findViewById(R.id.deleteBut) ;
this.queryBut = (Button) super.findViewById(R.id.queryBut) ;
this.mainInfo = (TextView) super.findViewById(R.id.mainInfo) ;
this.insertBut.setOnClickListener(new InsertOnClickListener()) ;// 单击事件操作
this.updateBut.setOnClickListener(new UpdateOnClickListener()) ;// 单击事件操作
this.deleteBut.setOnClickListener(new DeleteOnClickListener()) ;// 单击事件操作
this.queryBut.setOnClickListener(new QueryOnClickListener()) ; // 单击事件操作
}
private class InsertOnClickListener implements OnClickListener {// 增加按钮的单击事件
@Override
public void onClick(View view) {
Contentprovider.this.mainInfo.
setText("执行的是增加操作..."); // 设置显示文字
long id = 0 ; // 保存接收ID
try {
id = Contentprovider.this.testInsert("李晓峰",30,
new SimpleDateFormat("yyyy-MM-dd").
format(new Date())) ; // 增加数据
} catch (Exception e) {
e.printStackTrace();
}
Toast.makeText(Contentprovider.this, "数据增加成功,ID为:" + id,
Toast.LENGTH_LONG).show(); // 信息提示
}
}
private class UpdateOnClickListener implements OnClickListener {
@Override
public void onClick(View view) {
Contentprovider.this.mainInfo.
setText("执行的是修改操作..."); // 显示文字
long result = 0 ; // 更新记录
try {
result = Contentprovider.this.testUpdate("13", "李小明", 18,
"1989-09-19"); // 更新数据
} catch (Exception e) {
e.printStackTrace();
}
Toast.makeText(Contentprovider.this, "更新了" + result + "条记录!",
Toast.LENGTH_LONG).show(); // 信息提示
}
}
private class DeleteOnClickListener implements OnClickListener {
@Override
public void onClick(View view) {
Contentprovider.this.mainInfo.
setText("执行的是删除操作..."); // 显示文字
long result = 0 ; // 更新记录
try {
result = Contentprovider.
this.testDelete("4") ; // 删除数据
} catch (Exception e) {
e.printStackTrace();
}
Toast.makeText(Contentprovider.this, "删除了" + result + "条记录!",
Toast.LENGTH_LONG).show(); // 信息提示
}
}
private class QueryOnClickListener implements OnClickListener {
@Override
public void onClick(View arg0) {
Contentprovider.this.mainInfo.
setText("执行的是查询操作..."); // 显示文字
Cursor result = null ; // 结果集
try {
result = Contentprovider.
this.testQuery(null); // 查询全部数据
} catch (Exception e) {
e.printStackTrace();
}
Log.v("test", "result = " + (result == null));
Contentprovider.this.
startManagingCursor(result) ; // Cursor交由系统管理
List<Map<String, Object>> members = null ; // 用于设置SimpleAdapter
members = new ArrayList<Map<String, Object>>();// 实例化List集合
for (result.moveToFirst(); !result.isAfterLast(); result
.moveToNext()) { // 循环取数据
Map<String, Object> member = new HashMap<String, Object>();
member.put("_id", result.getInt(0)) ; // 设置一行的_id内容
member.put("name", result.getString(1)) ; // 设置一行的name内容
member.put("age",result.getInt(2)) ; // 设置一行的age内容
member.put("birthday", result.getString(3));// 设置一行的birthday内容
members.add(member) ; // 保存Map
}
Contentprovider.this.membersList
.setAdapter(new SimpleAdapter(
Contentprovider.this, // 将数据包装
members, // 数据集合
R.layout.a130912_activity_contentprovider_1_member, // 显示的布局管理器
new String[] { "_id", "name",
"age", "birthday" }, // 匹配的Map集合的KEY
new int[] { R.id._id, R.id.name, R.id.age,
R.id.birthday })); // 显示数据
Toast.makeText(Contentprovider.this, "数据查询成功!",
Toast.LENGTH_LONG).show(); // 信息提示
}
}
}
AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity android:name="com.example.test2.contentProvider.Contentprovider"/>
<provider android:name="com.example.test2.contentProvider.Member"
android:authorities="com.example.test2"
android:exported="true"
/>
</application>