一般我们很少自己实现ContentProvider,都是用的Android内置的Contentprovider。
下面的这个例子说明如何实现自己的ContentProvider,并且如何使用它。
数据存储采用的是SQLite。
实现ContentProvider涉及的东西很多,不一一概述,代码中有相应注释。
MyContentPorviderMetaData.java:
这个类中封装了实现自己的ContentProvider用到的常量。
import android.net.Uri;
import android.provider.BaseColumns;
public final class MyContentProviderMetaData {
// 数据库名称
public static final String DATABASE_NAME = "lucky_star.db";
// 数据库版本
public static final int DATABASE_VERSION = 1;
// ContentProvider的完全限定名
public static final String AUTHOIRTY = "com.and.test.contentprovider.MyContentProvider";
public static final class UserTableMetaData implements BaseColumns{
// BaseColumns中已经有_ID和_COUNT2个字段了。
// 访问该ContentProvider的URI
// CONTENT_URI必须是一个唯一的值,推荐的方式使用ContentProvider的完全限定名。
public static final Uri CONTENT_URI =
Uri.parse("content://"+AUTHOIRTY+"/user");
// 该ContentProvider返回的数据类型的定义。
// 下面这种方式是参考Android联系人里面定义的方式。
// 访问所有记录的数据类型
public static final String CONTENT_TYPE_ITMES = "vnd.android.cursor.dir/vnd.mycontentprovider.user";
// 访问某条记录的数据类型
public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.mycontentprovider.user";
// 表名
public static final String TABLE_NAME = "user";
// 字段名
public static final String USER_NAME = "name";
public static final String USER_AGE = "age";
public static final String USER_ADDRESS = "address";
// 默认排序规则,按name升序排列。
public static final String DEFAULT_ORDER = "name asc";
}
}
MyContentProvider.java:
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import com.and.test.DatabaseHelper;
/**
* 要实现自己的ContentProvider,需要继承ContentProvider,并实现onCreate(),insert(),query(),update(),delete(),getType()这些方法。<br>
* onCreate()方法在初始化ContentProvider时被调用。
* @author babylove
*
*/
public class MyContentProvider extends ContentProvider{
public static UriMatcher uriMatcher = null;
// 1:表示访问所有的记录。
public static final int FETCH_ALL_ROWS = 1;
// 2:表示访问1条记录。
public static final int FETCH_ONE_ROW = 2;
private SQLiteOpenHelper soh = null;
private static Map<String, String> columnMap = new HashMap<String, String>();
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// user:表示访问user表的所有记录
uriMatcher.addURI(MyContentProviderMetaData.AUTHOIRTY, "user", FETCH_ALL_ROWS);
// user/#:表示访问user表的某条记录。
uriMatcher.addURI(MyContentProviderMetaData.AUTHOIRTY, "user/#", FETCH_ONE_ROW);
}
// 给查询字段取的别名。在用SQLiteQueryBuilder是用到。
static {
columnMap.put(MyContentProviderMetaData.UserTableMetaData._ID, MyContentProviderMetaData.UserTableMetaData._ID);
columnMap.put(MyContentProviderMetaData.UserTableMetaData.USER_NAME, MyContentProviderMetaData.UserTableMetaData.USER_NAME);
columnMap.put(MyContentProviderMetaData.UserTableMetaData.USER_AGE, MyContentProviderMetaData.UserTableMetaData.USER_AGE);
columnMap.put(MyContentProviderMetaData.UserTableMetaData.USER_ADDRESS, MyContentProviderMetaData.UserTableMetaData.USER_ADDRESS);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase sdb = soh.getWritableDatabase();
int count = sdb.delete(MyContentProviderMetaData.UserTableMetaData.TABLE_NAME, selection, selectionArgs);
// 通知监听器,数据已更改
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
// 根据传入的URI,返回该URI表示的数据类型。
@Override
public String getType(Uri uri) {
// UriMatcher中通过addURI()方法将一个URI跟一个数字绑定
// 这里通过UriMatcher的match()方法,如果有匹配的URI,则将该URI对应的数字返回。
switch (uriMatcher.match(uri)) {
case FETCH_ALL_ROWS:
return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITMES;
case FETCH_ONE_ROW:
return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM;
default:
throw new IllegalArgumentException("Unknown Uri " + uri);
}
}
// 返回成功插入数据的对应的URi。
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase sdb = soh.getWritableDatabase();
// 返回的是成功插入数据库后的生成的rowId,如果插入失败返回-1.
long rowId = sdb.insert(MyContentProviderMetaData.UserTableMetaData.TABLE_NAME, null, values);
if (rowId > 0) {
// 刚刚插入数据库的记录对应的URI
Uri insertedUri = ContentUris.withAppendedId(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, rowId);
System.out.println(insertedUri.toString());
// 通知监听器,数据已更改。
getContext().getContentResolver().notifyChange(insertedUri, null);
return insertedUri;
}
throw new SQLException("Failed insert data into " + uri);
}
// 这是一个回调方法,在ContentProvider创建时调用。
@Override
public boolean onCreate() {
// 这里在初始化ContentProvider时打开数据库
soh = new DatabaseHelper(getContext(), MyContentProviderMetaData.DATABASE_NAME);
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
// 设置要查询的表名
builder.setTables(MyContentProviderMetaData.UserTableMetaData.TABLE_NAME);
// 设置查询的字段别名
builder.setProjectionMap(columnMap);
switch (uriMatcher.match(uri)) {
case FETCH_ALL_ROWS: // 查询所有记录
break;
case FETCH_ONE_ROW: // 查询某条记录
// uri.getPathSegments():decoded path segments, each without a leading or trailing '/'
// 如:content://com.and.test.contentprovider/user/23返回的是:user,23。那么get(1)得到的就是23,这个就是要查询的记录的_ID。
List<String> list = uri.getPathSegments();
for (int i=0;i<list.size();i++) {
Log.d(MyContentProvider.class.getName(),"path[" + "]:" + list.get(i));
}
builder.appendWhere(MyContentProviderMetaData.UserTableMetaData._ID + " = " + uri.getPathSegments().get(1));
default:
throw new SQLException("Unknown Uri for query:" + uri);
}
// 排序规则,如果调用者传入了orderBy,则使用调用者的orderBy;否则使用默认的排序规则。
String orderBy = null;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = MyContentProviderMetaData.UserTableMetaData.DEFAULT_ORDER;
}
else {
orderBy = sortOrder;
}
SQLiteDatabase sdb = soh.getReadableDatabase();
Cursor cursor = builder.query(sdb, projection, selection, selectionArgs, null, null, orderBy);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase sdb = soh.getWritableDatabase();
// 返回受影响的行数。
int count = sdb.update(MyContentProviderMetaData.UserTableMetaData.TABLE_NAME, values, selection, selectionArgs);
// 通知监听器,数据已改变。
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
}
ContentProviderActivity.java:
这是使用我们自己实现的ContentProvider的一个例子
import android.app.Activity;
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.Toast;
import com.and.test.R;
public class ContentProviderActivity extends Activity {
private Button insertBtn = null;
private Button queryBtn = null;
private Button updateBtn = null;
private Button deleteBtn = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.contentprovider);
insertBtn = (Button) findViewById(R.id.contentprovider_insert_btn);
queryBtn = (Button) findViewById(R.id.contentprovider_query_btn);
updateBtn = (Button) findViewById(R.id.contentprovider_update_btn);
deleteBtn = (Button) findViewById(R.id.contentprovider_delete_btn);
insertBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ContentValues values = new ContentValues();
values.put(MyContentProviderMetaData.UserTableMetaData.USER_NAME, "content provider");
values.put(MyContentProviderMetaData.UserTableMetaData.USER_AGE, 25);
values.put(MyContentProviderMetaData.UserTableMetaData.USER_ADDRESS,"china");
Uri uri = getContentResolver().insert(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, values);
System.out.println("数据插入成功,对应的Uri:" + uri);
}
});
queryBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Cursor c = getContentResolver().query(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, null, null, null, null);
while (c.moveToNext()) {
// _ID
int _id = c.getInt(c.getColumnIndex(MyContentProviderMetaData.UserTableMetaData._ID));
// name
String name = c.getString(c.getColumnIndex(MyContentProviderMetaData.UserTableMetaData.USER_NAME));
// age
int age = c.getInt(c.getColumnIndex(MyContentProviderMetaData.UserTableMetaData.USER_AGE));
// address
String address = c.getString(c.getColumnIndex(MyContentProviderMetaData.UserTableMetaData.USER_ADDRESS));
String data = ",_id:" + _id + ",name:" + name + ",age:" + age + ",address:" + address;
System.out.println(data);
Toast.makeText(ContentProviderActivity.this, data, Toast.LENGTH_SHORT).show();
}
}
});
updateBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ContentValues values = new ContentValues();
values.put(MyContentProviderMetaData.UserTableMetaData.USER_NAME, "updated_name");
values.put(MyContentProviderMetaData.UserTableMetaData.USER_AGE, 22);
int count = getContentResolver().update(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, values, MyContentProviderMetaData.UserTableMetaData.USER_NAME+"=?", new String[]{"content provider"});
if (count > 0) {
Toast.makeText(ContentProviderActivity.this, "数据已修改!", Toast.LENGTH_SHORT).show();
}
}
});
deleteBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
getContentResolver().delete(MyContentProviderMetaData.UserTableMetaData.CONTENT_URI, MyContentProviderMetaData.UserTableMetaData.USER_NAME+"=?", new String[]{"content provider"});
Toast.makeText(ContentProviderActivity.this, "数据删除!", Toast.LENGTH_SHORT).show();
}
});
}
}
DatabaseHelper.java:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
/**
* SQLiteOpenHelper是一个抽象类,不能直接使用,需要自己写一个类继承它。
* 然后可以通过getReadableDatabase()获取一个可读的database;<br>
* 通过getWritableDatabase()方法获取一个可读写的Database。<br>
* 如果只有查询操作,就用getReadableDatabase()就可以了。
* @author lucky star
*
*/
public class DatabaseHelper extends SQLiteOpenHelper {
// 数据库版本号,从1开始,必须是大于0的整数。
public final static int VERSION = 1;
// 需要复写SQLiteOpenHelper的这个构造方法。
public DatabaseHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
public DatabaseHelper(Context context,String name,int version) {
this(context,name,null,version);
}
public DatabaseHelper(Context context,String name) {
this(context,name,null,VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.d("DatabaseHelper", "onCreate()");
// 创建数据时就创建好表结构。
db.execSQL("create table user(_id integer primary key autoincrement,name varchar(32),age int,address varchar(100))");
Log.d("DatabaseHelper","create table user.");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d("DatabaseHelper","onUpgrade()");
}
}
使用的布局文件contentprovider.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/contentprovider_insert_btn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/insertData"
/>
<Button
android:id="@+id/contentprovider_query_btn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/queryData"
/>
<Button
android:id="@+id/contentprovider_update_btn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/updateData"
/>
<Button
android:id="@+id/contentprovider_delete_btn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/deleteData"
/>
</LinearLayout>
最后在AndroidManifest.xml中注册时这个ContentProvider。
<provider android:name="com.and.test.contentprovider.MyContentProvider"
android:authorities="com.and.test.contentprovider.MyContentProvider">
</provider>