功能描述:
通过ContentProvider实现对数据库的CRUD操作
以下文章代码中共涉及ContentProvider、ContentReslover、Uri、UriMather、ContentUris 5个类,下面分别是这几个类的简单介绍,具体操作和说明可以到文档中查询。
ContentProvider类:
ContentProvider形象的说,一个搭建在远端服务器上的一个站点,为我们暴露出来响应的接口,从而实现客户端操作。具体常用到的方法有:
方法名称 描述
onCreate() 启动组件时调用初始化provider
query(Uri, String[], String, String[], String) 根据指定Uri进行查询操作,返回Cursor
insert(Uri, ContentValues) 根据指定Uri进行插入操作,返回新插入行的Uri地址
update(Uri, ContentValues, String, String[]) 根据指定Uri进行更新操作,返回修改条数
delete(Uri, String, String[]) 根据指定Uri进行删除操作,返回被删除的行数
abstract String getType(Uri) 根据UriMatcher匹配返回相应MIME格式
public abstract boolean onCreate () 组件启动时调用,一般进行初始化操作部署 例如:实例化一个数据库操作类
public final Context getContext () 获得运行provider的Context环境,该函数只有在onCreate()启动后才能被执行,如果在构造函数中调用返回null值
在为应用开发ContentProvider功能时通常需继承并复写以上红色字体的方法。
ContentReslover类:
如果ContentProvider是服务器端,那么ContentReslover就是客户端,ContentReslover中的操作方法与ContentProvider里一一对应,常见方法如下表:
方法名称 | 描述 |
public final int delete (Uri url, String where, String[]selectionArgs) | 调用ContentProvider中的delete() |
public final int update (Uri uri, ContentValues values,String where, String[]selectionArgs)
|
调用ContentProvider中的update() |
public final Cursor query (Uri uri, String[] projection,String selection, String[] selectionArgs, StringsortOrder) | 调用ContentProvider中的query() |
public final Uri insert (Uri url, ContentValues values) | 调用ContentProvider中的insert() |
public final String getType (Uri url) | 调用ContentProvider中的getType() |
需要注意的是实例化一个ContentReslover对象,需要借助Activity的帮助,调用acticity的getContentReslover()方法从而获得一个ContentReslover对象:
// 由Activity辅助获得一个ContentResolver对象
ContentResolver contentResolver = super.getContentResolver();
Uri类:
Uri是一个类似于网址一样的定位数据的地址,其格式为:
A://B/C
A代表协议名称 ContentProvider协议在android中规定为 content://
B代表主机名或Authority 唯一标识一个ContentProvider,外部访问可以通过这个找到它,可用包名+类名标识
C代表访问路径 可以具体访问到一个主机的全部数据或某个数据抑或某个数据表中的某个字段
Uri类用来将给定字符串生成Uri类型的地址,同时可以解析Uri地址,其中withAppendedPath 用于为给定Uri添加子路径,常见方法如下:
方法名称 | 描述 |
public static String encode (String s) | 对字符串进行解码 |
public static String decode (String s) | 对字符串进行编码 |
public static Uri fromFile (File file) | 从文件中创建Uri地址 |
public static Uri withAppendedPath (UribaseUri, String pathSegment) | 在给定Uri后添加路径 |
public static Uri parse (String uriString) | 将给定字符串解析成为Uri地址 |
ContentUris类:
ContentUris类是Uri的一个辅助类,可以用来解析出Uri中给定的id值并且可以为给定路径添加ID值,具体方法如下表:
方法名称 | 描述 |
public static long parseId (Uri contentUri) | 解析出给定Uri地址的末尾path的id值 |
public static Uri withAppendedId (UricontentUri, long id) | 在给定Uri末尾path添加id值 |
需要说明的是 这里的withAppendID()可以用Uri中的withAppendedPath替换,但是其没有Uri中的前打,该方法只能添加id值,withAppendedPath却可以增加子路径包括id值,建议使用前者
UriMatcher类:
也是Uri的辅助类,起作用是做不同类型的Uri的匹配,主要用在ContenProvider中的getType()方法中,用于将不同的Uri操作绑定到不同的Uri地址上,即返回相应的MIME类型:
方法名称 | 描述 |
public static final int NO_MATCH | 常量值为-1,用于实例化一个Urimatcher对象 |
public void addURI (Stringauthority, String path, int code) | 增加一个指定的Uri地址,path中*统配任何文字 #统配数字 |
public int match (Uri uri) | 与指定Uri进行匹配,成功放回相应的Code,失败返回-1 |
public UriMatcher (int code) | 构造实例化一个UriMatcher对象 |
UriMather的实例方法 通过一个 NO_MATCH 的静态变量来实现
//UriMatcher实例化
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
以上就是开发Contentprovider要使用到的主要类,熟悉以后。下面开始正式程序操作
首先程序目录清单:
其次是界面文件清单
contacts.xml listView上单个数据的样式
main.xml 例程的主界面
ContentProvider的操作其实就是简介的操作数据库,其核心还是数据库的操作,所以我们需要一个SQLite的助手类继承SQLiteOpenHelper 如下:
package com.freshstu;
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 {
// 构造函数
public DBHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
// 俩个参数的构造函数
public DBHelper(Context context, String name) {
this(context, name, null, ContactsMetaDatabase.VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
String sql = "CREATE TABLE "+ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME+"("
+ ContactsMetaDatabase.contactPersonTableMeta._ID + " integer primary key,"
+ ContactsMetaDatabase.contactPersonTableMeta.MEMBER_NAME + " varchar(50) not null,"
+ ContactsMetaDatabase.contactPersonTableMeta.MEMBER_PHONE + " varchar(50) not null,"
+ ContactsMetaDatabase.contactPersonTableMeta.MEMBER_CREATE_DATA + " data not null)";
System.out.println(sql);
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
String sql = "DROP TABLE IF EXIST" + ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME;
db.execSQL(sql);
this.onCreate(db);
}
}
其主要是实现的功能就是获得一个SQLiteDatabase对象,然后对数据库进行操作。
其次就需要定义数据库中字段名以及表名之类的常量,如下
package com.freshstu;
/**
* 定义操作涉及的常量*/
import android.provider.BaseColumns;
import android.net.Uri;
public interface ContactsMetaDatabase {
//定义外部访问路径 content地址 content://com.freshstu.contactsprovider
public static final String AUTHORITY ="com.freshstu.contactsprovider";
//定义数据库名称
public static final String DATABASE_NAME ="contact_book";
//定义数据库版本
public static final int VERSION = 1;
public interface contactPersonTableMeta extends BaseColumns{
//定义表名称
public static final String TABLE_NAME ="contact_members";
//外部访问本表的Uri地址 在anroidManifest中与关联
public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
//取得表中所有数据的 在Contentprovider中 getType中与关联
public static final String CONTENT_LIST = "vnd.android.cursor.dir/vnd.freshstucontentprovider.contact_members";
//根据id序号取得表中的条件数据
public static final String CONTENT_ITEM = "vnd.android.cursor.item/vnd.freshstucontentprovider.contact_members";
//定义表中字段
public static final String MEMBER_NAME ="name";
public static final String MEMBER_PHONE ="phone";
public static final String MEMBER_CREATE_DATA = "create_time";
//结果逆向排序
public static final String SORT_ORDER = "_id DESC";
}
}
需要注意的时,要实现数据库中id字段的自增操作,就必须将id字段名名为“_id”形式,具体在android中有一个类封装了这个常量,可以直接继承使用BaseColumns该类中一共俩个常量 一个是记录行号的_ID 一个是记录id的总数_COUNT
完成以上操作就可以开始对ContentProvider操作了,首先要继承ContentProvider,重写其中的ContenProvider中的那几个红色标签的函数,关于这些方法的作用已经在前面的表格中说明了,就不熬述
package com.freshstu;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.content.ContentUris;
/**
* 1、转换地址 2、关联地址 3、根据地址进行操作
*/
public class contact_memberContentProvidre extends ContentProvider {
// 声明一个数据库辅助对象
private DBHelper dbHelper = null;
// 地址转换 即转换操作类型
private static UriMatcher uriMatcher = null;
// 得到全部数据 code代表码
private static final int GET_CONTACT_LIST = 1;
// 得到单个数据 code代表码
private static final int GET_CONTACT_ITEM = 2;
static { // 静态代码块
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(ContactsMetaDatabase.AUTHORITY,
ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME,
GET_CONTACT_LIST); // 取得全部匹配数据
uriMatcher.addURI(ContactsMetaDatabase.AUTHORITY,
ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME + "/#",
GET_CONTACT_ITEM);// 取得单个匹配数据
}
// 进行初始化操作
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
// 实例化一个数据库操作对象
dbHelper = new DBHelper(super.getContext(),
ContactsMetaDatabase.DATABASE_NAME);
// 操作成功
return true;
}
// 取得数据类型
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
// 数值码与序列关联
switch (uriMatcher.match(uri)) {
case GET_CONTACT_LIST:
// 将接口中定义的操作类型与之相匹配
return ContactsMetaDatabase.contactPersonTableMeta.CONTENT_LIST;
case GET_CONTACT_ITEM:
return ContactsMetaDatabase.contactPersonTableMeta.CONTENT_ITEM;
default:
throw new UnsupportedOperationException("Not Support Operation:"
+ uri);
}
}
// 『返回操作结果』!
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
// 获取数据库对象
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 记录返回新添加的行号
long id;
System.out.println("*****" + uri);
switch (uriMatcher.match(uri)) {
// 增加操作 insert操作用不上item
case GET_CONTACT_ITEM:
System.out.println("Item满足");
return null;
case GET_CONTACT_LIST:
System.out.println("List满足");
// 要处理id自动增长所以要插入默认
id = db.insert(
ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME,
ContactsMetaDatabase.contactPersonTableMeta._ID, values);
// 将新的id的uri返回去
String uriPath = uri.toString();
String path = uriPath + "/" + id;
return Uri.parse(path);
default:
throw new UnsupportedOperationException("Not Support Operation:"
+ uri);
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 返回更新结果
int result = 0;
long id = 0;
switch (uriMatcher.match(uri)) {
case GET_CONTACT_ITEM:
System.out.println("Item满足");
// ContentUris是静态方法
id = ContentUris.parseId(uri);
String where = "_id=" + id;
// id值就是selectionArgs的参数值
result = db.update(
ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME,
values, where, selectionArgs);
System.out.println("***更新的值为:" + result);
break;
case GET_CONTACT_LIST:
// 更新全表
result = db.update(
ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME,
values, null, null);
break;
default:
throw new UnsupportedOperationException("Not Support Operation:"
+ uri);
}
return result;
}
@Override
public int delete(Uri uri, String whereClause, String[] whereArgs) {
// TODO Auto-generated method stub
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 返回更新结果
int result = 0;
// where条件id
// String where ="_id="+ id;
switch (uriMatcher.match(uri)) {
case GET_CONTACT_ITEM:
System.out.println("Delete@@@Item满足" + uri);
result = db.delete(
ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME,
whereClause, whereArgs);
System.out.println("itme值为:" + result);
break;
case GET_CONTACT_LIST:
System.out.println("Delete@@@list满足");
result = db.delete(
ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME,
whereClause, whereArgs);
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) {
// TODO Auto-generated method stub
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 记录返回查询结果
Cursor result = null;
switch (uriMatcher.match(uri)) {
case GET_CONTACT_LIST:
System.out.println("Query@@@list满足");
result = db
.query(ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME,
null, null, null, null, null,
null);
break;
case GET_CONTACT_ITEM:
System.out.println("Query@@@item满足");
result = db
.query(ContactsMetaDatabase.contactPersonTableMeta.TABLE_NAME,
projection, selection, selectionArgs, null, null,
sortOrder);
System.out.println("服务端执行完毕");
break;
default:
throw new UnsupportedOperationException("Not Support Operation:"
+ uri);
}
return result;
}
}
其中关于对sql操作的参数建议,保留不要在内部写死,要留接口给客户端ContentResolver,这样可以增大程序的灵活性
完成了以上的所有操作,我们就可以在对主界面中的按钮设置监听器了,以对操作做出相应,代码如下,其中CRUD都被封装写到了函数中,这样onCreate里的程序就显得干净许多了
package com.freshstu;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
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;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
public class ContentproviderActivity extends Activity {
/** Called when the activity is first created. */
// UI控件
private Button insertBut = null;
private Button deleteBut = null;
private Button updateBut = null;
private Button queryBut = null;
private ListView listView = null;
private TextView textView = null;
// listView适配器
private SimpleAdapter simpleadapter = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
listView = (ListView) findViewById(R.id.query_list);
textView = (TextView) findViewById(R.id.title);
insertBut = (Button) findViewById(R.id.insert);
insertBut.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
// TODO Auto-generated method stub
// 增加函数
long id = 0;
id = testInsert("小明", "13307145776", new SimpleDateFormat(
"yyyy-mm-dd").format(new Date()));
Toast.makeText(ContentproviderActivity.this, "增加数据成功" + id,
Toast.LENGTH_SHORT).show();
}
});
updateBut = (Button) findViewById(R.id.update);
updateBut.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
// TODO Auto-generated method stub
long id = 0;
id = testUpdate("2", "大明", "0000000", new SimpleDateFormat(
"yyyy-mm-dd").format(new Date()));
Toast.makeText(ContentproviderActivity.this, "修改数据成功" + id,
Toast.LENGTH_SHORT).show();
}
});
deleteBut = (Button) findViewById(R.id.delete);
deleteBut.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
long id = 0;
id = testDelete(String.valueOf(1));
Toast.makeText(ContentproviderActivity.this, "删除数据成功" + id,
Toast.LENGTH_SHORT).show();
}
});
queryBut = (Button) findViewById(R.id.query);
queryBut.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
// 用于结果显示
Cursor showdata = null;
// 存放显示参数
String[] columns = {
ContactsMetaDatabase.contactPersonTableMeta.MEMBER_NAME,
ContactsMetaDatabase.contactPersonTableMeta.MEMBER_PHONE,
ContactsMetaDatabase.contactPersonTableMeta.MEMBER_CREATE_DATA };
// 查询结果
System.out.println("执行到这里了");
showdata = testQuery(null, columns);
// 结果交给系统管理
ContentproviderActivity.this.startManagingCursor(showdata);
List
完成了主界面,就可以放到虚拟机上跑一下了,看一下成果。如下图
代码下载:
http://115.com/file/e74axjll#contentprovider.zip
总结:
完成了对ContentProvider操作的自定义,整体上看就是服务端与客户端的操作,大体流程服务端CP暴露接口给客户端请求数据 之后数据交给Activity托管,请求者可以对数据做进一步操作,已满足各个需求的不同处理。其关键点就是URI与数据库数据的匹配操作,做好了这些,之后的操作就跟对数据库操作没什么俩样了。