ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行添删改查。
ContentProvider的优点:如果采用文件操作模式对外共享数据,数据的访问方式会因数据存储的方式而不同,导致数据的访问方式无法统一,如:采用xml文件对外共享数据,需要xml解析才能读取数据;用sharedpreferences共享数据,需要使用sharedpreferences API读取数据。使用ContentProvider对外共享数据的好处是统一了数据的访问方式。
package com.test;
import java.util.HashMap;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
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;
public class MyProvider extends ContentProvider {
//如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,
public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir";
//如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,
public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item";
public static final String MIME_ITEM = "vnd.msi.people";
public static final String MIME_TYPE_SINGLE = MIME_ITEM_PREFIX + "/" + MIME_ITEM;
public static final String MIME_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MIME_ITEM;
/*
* 主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它
* 和androidmanifest.xml中声明的一样
*/
public static final String AUTHORITY = "com.test.provider";
public static final String PATH_SINGLE = "people/#";
public static final String PATH_MULTIPLE = "people";
/*
* Uri代表了要操作的数据,Uri主要包含了两部分信息:1.需要操作的ContentProvider
* 2.对ContentProvider中的什么数据进行操作,一个Uri由scheme,Authority,path三部分组成
* 其中scheme由Android所规定, scheme为:content://
* Authority用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。
* 和androidmanifest.xml中声明的一样
* path可以用来表示我们要操作的数据,路径的构建应根据如下规定:
* 1.要操作person表中id为10的记录,可以构建这样的路径:/person/10
* 2.要操作person表中id为10的记录的name字段, person/10/name
* 3. 要操作person表中的所有记录,可以构建这样的路径:/person
* 4.要操作xxx表中的记录,可以构建这样的路径:/xxx
* 5.当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:
* 6.要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name
* 7.如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
*/
public static final Uri content_URI = Uri.parse("content://" + AUTHORITY + "/" + PATH_MULTIPLE);
//指定查询结果按升序排列
public static final String DEFAULT_SORT_ORDER = "name DESC";
public static final int PEOPLE = 1;
public static final int PEOPLES = 2;
private static UriMatcher URI_MATCHER;
private static HashMap<String, String> PROJECTION_MAP;
public static String DB_NAME = "peopledb";
public static String DB_TABLE_NAME = "people";
SQLiteDatabase db;
DBOpenHelper dbOpenHelper;
/*
* 内部类DBOpenHelper
*/
class DBOpenHelper extends SQLiteOpenHelper {
private final String DB_CREATE = "CREATE TABLE "
+ DB_TABLE_NAME
+ " (_id INTEGER PRIMARY KEY,name TEXT UNIQUE NOT NULL,"
+ "phone TEXT,age INTEGER);";
public DBOpenHelper(Context context, String dbName, int version) {
super(context, dbName, null, version);
}
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL(DB_CREATE);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
/*
* 静态块
*/
static {
URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
/*
* 如果match()方法匹配content://com.test.provider/people/# 路径(其中“#”号是通配符)
* 返回匹配码为1
*/
URI_MATCHER.addURI(AUTHORITY, PATH_SINGLE, PEOPLE);
//如果match()方法匹配content://com.test.provider/people路径,返回匹配码为2
URI_MATCHER.addURI(AUTHORITY, PATH_MULTIPLE, PEOPLES);
PROJECTION_MAP = new HashMap<String, String>();
PROJECTION_MAP.put("ID", "_id");
PROJECTION_MAP.put("NAME", "name");
PROJECTION_MAP.put("PHONE", "phone");
PROJECTION_MAP.put("AGE", "age");
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
switch (URI_MATCHER.match(uri)) {
case PEOPLES:
count = db.delete(DB_TABLE_NAME, selection, selectionArgs);
break;
case PEOPLE:
String segment = uri.getPathSegments().get(1);
String where = "";
if (!TextUtils.isEmpty(selection)) {
where = " AND (" + selection + ")";
}
//删除具体某一条
count = db.delete(DB_TABLE_NAME, "_id=" + segment + where,
selectionArgs);
break;
default:
break;
}
/*
* 如果ContentProvider的访问者需要知道ContentProvider中的数据发生变化,
* 可以在ContentProvider发生数据变化时调
* 用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者
* 如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对
* 数 据(数据采用uri描述)进行监听,当监听到数据变化通知时,
* 系统就会调用ContentObserver的onChange()方法,具体实现如下:
* getContentResolver().registerContentObserver(Uri.parse("content://com.test
* .provider/people"), true, new PeopleObserver(new Handler()));
public class PeopleObserver extends ContentObserver{
public PersonObserver(Handler handler) {
super(handler);
}
public void onChange(boolean selfChange) {
//此处可以进行相应的业务处理
}
}
*/
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
switch (URI_MATCHER.match(uri)) {
case PEOPLES:
return MIME_TYPE_MULTIPLE;
case PEOPLE:
return MIME_TYPE_SINGLE;
default:
throw new IllegalArgumentException("Unkown URI " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
long rowId = 0L;
if (URI_MATCHER.match(uri) != PEOPLES) {
throw new IllegalArgumentException("Unkown URI" + uri);
}
rowId = db.insert(DB_TABLE_NAME, null, values);
if (rowId > 0) {
/*
* withAppendedId(content_URI, id)用于为路径加上ID部分
* 生成后的Uri为:content://com.test.provider/people/rowId
*/
Uri result = ContentUris.withAppendedId(content_URI, rowId);
//通知数据变化
getContext().getContentResolver().notifyChange(result, null);
return result;
} else
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public boolean onCreate() {
dbOpenHelper = new DBOpenHelper(this.getContext(), DB_NAME, 1);
db = dbOpenHelper.getWritableDatabase();
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
/*
* SQLiteQueryBuilder:SQL查询的辅助类
* 这个查询类似于一个标准的SQL查询,但是这个查询是SQLiteQueryBuilder 来发起的,而不是
* SQLiteDatabase 直接发起的,所以在参数方面略有不同。
* 这个函数为 query(SQLiteDatabase db, String[] projectionIn, String selection,
* String[] selectionArgs, String groupBy, String having, String sortOrder,
* String limit)下边将各个参数介绍一下。
* 第一个参数为要查询的数据库实例。
* 第二个参数是一个字符串数组,里边的每一项代表了需要返回的列名。
* 第三个参数相当于SQL语句中的where部分。
* 第四个参数是一个字符串数组,里边的每一项依次替代在第三个参数中出现的问号(?)。
* 第五个参数相当于SQL语句当中的groupby部分。
* 第六个参数相当于SQL语句当中的having部分。
* 第七个参数描述是怎么进行排序。
* 第八个参数相当于SQL当中的limit部分,控制返回的数据的个数。
*/
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(DB_TABLE_NAME);
queryBuilder.setProjectionMap(PROJECTION_MAP);
switch (URI_MATCHER.match(uri)) {
case PEOPLES:
break;
case PEOPLE:
queryBuilder.appendWhere("_id=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unkonw URI" + uri);
}
String orderBy = null;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = DEFAULT_SORT_ORDER;
} else
orderBy = sortOrder;
Cursor c = queryBuilder.query(db, projection, selection, selectionArgs,
null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int count = 0;
switch (URI_MATCHER.match(uri)) {
case PEOPLES:
count = db.update(DB_TABLE_NAME, values, selection, selectionArgs);
break;
case PEOPLE:
String segment = uri.getPathSegments().get(1);
String where = "";
if (!TextUtils.isEmpty(selection)) {
where = " AND (" + selection + ")";
}
count = db.update(DB_TABLE_NAME, values, "_id=" + segment + where,
selectionArgs);
break;
default:
throw new IllegalArgumentException("Unkonw URI" + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test"
android:versionCode="1"
android:versionName="1.0" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name="com.test.Hello"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="MyProvider"
android:authorities="com.test.provider"
android:syncable="true" >
</provider>
</application>
<uses-sdk android:minSdkVersion="7" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CONTACTS" >
</uses-permission>
</manifest>
可以通过如下代码:调用MyProvider中的方法:
ContentValues values = new ContentValues();
values.put("name", “tester”);
values.put("phone", "10086");
values.put("age", 23);
Uri uri = getContentResolver().insert(MyProvider.content_URI,values);