如何自定义一个注解优雅的ContentProvider

博客分类:
自定义ContentProvider,根据不同的条件查询不同的数据:
public class MainActivity extends Activity implements OnClickListener {
private ListView lv_
private Button btn_city_code, btn_all_city, btn_city_name, btn_
private ContentResolver contentR
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv_list = (ListView) findViewById(R.id.lv_lsit);
btn_all_city = (Button) findViewById(R.id.btn_all_city);
btn_city_code = (Button) findViewById(R.id.btn_city_code);
btn_city_name = (Button) findViewById(R.id.btn_city_name);
btn_provinces = (Button) findViewById(R.id.btn_provinces);
contentResolver = getContentResolver();
btn_all_city.setOnClickListener(this);
btn_city_code.setOnClickListener(this);
btn_city_name.setOnClickListener(this);
btn_provinces.setOnClickListener(this);
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_all_city:
uri = Uri.parse("content://com.example.contetnprovider/cities");
case R.id.btn_city_code:
uri = Uri.parse("content://com.example.contetnprovider/code/024");
case R.id.btn_city_name:
uri = Uri.parse("content://com.example.contetnprovider/name/沈阳");
case R.id.btn_provinces:
uri = Uri.parse("content://com.example.contetnprovider/cities_in_province/辽宁");
cursor = contentResolver.query(uri, new String[] {"city_code as _id","city_name","province_code"}, null, null, null);
SimpleCursorAdapter simpleCursorAdapter = new SimpleCursorAdapter(this, R.layout.list_item, cursor, new String[]{"city_name"}, new int[] {R.id.tv_name});
lv_list.setAdapter(simpleCursorAdapter);
继承contentprovider:
public class RegionContentProvider extends ContentProvider {
private static UriMatcher uriM
private static final String AUTOHORITH = "com.example.contetnprovider";
// 下面是4个常量是返回码
private static final int CITIES = 1;
private static final int CITY_CODE = 2;
private static final int CITY_NAME = 3;
private static final int CITITE_IN_PROVINCE = 4;
private SQLiteD
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 用于查询所有城市的url
uriMatcher.addURI(AUTOHORITH, "cities", CITIES);
// 用于根据城市代码查询城市信息的url
uriMatcher.addURI(AUTOHORITH, "code/#", CITY_CODE);
// 用于根据城市名称查询城市信息
uriMatcher.addURI(AUTOHORITH, "name/*", CITY_NAME);
// 用于根据省名查询省内所有城市信息
uriMatcher.addURI(AUTOHORITH, "cities_in_province/*", CITITE_IN_PROVINCE);
private SQLiteDatabase openDatabase() {
String dataseFileName = "sdcard/region.db"; // assets目录下有一个region。db数据库,里面有一个省表,城市表,和一个视图,表中的结构就是code,name
if (!(new File(dataseFileName)).exists()) {
InputStream is = getContext().getResources().getAssets().open("region.db");
FileOutputStream fos = new FileOutputStream(dataseFileName);
byte[] buff = new byte[8192];
int count = 0;
while ((count = is.read(buff)) & 0){
fos.write(buff,0,count);
fos.close();
is.close();
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(dataseFileName, null);
} catch (Exception e) {
Log.d("error", e.getStackTrace().toString());
public boolean onCreate() {
database = openDatabase();
Log.d("good", "ok");
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor =
// 根据url获得返回码
switch (uriMatcher.match(uri)) {
case CITIES: //查询所有城市信息
cursor = database.query("v_cities_province", projection, selection, selectionArgs, null, null, sortOrder);
case CITY_CODE: // 根据城市代码查询城市信息
String citycode = uri.getPathSegments().get(1);
if (selection == null) {
selection = "city_code='"+citycode+"' ";
selection += " and (city_code='"+ citycode +"' )";
cursor = database.query("t_cities", projection, selection, selectionArgs, null, null, sortOrder);
case CITY_NAME: // 根据城市名查询所有城市信息
String cityname = uri.getPathSegments().get(1);
if (selection == null) {
selection = "city_name='" + cityname +"'";
selection += "and (city_name='" + cityname + "')";
cursor = database.query("t_cities", projection, selection, selectionArgs, null, null, sortOrder);
case CITITE_IN_PROVINCE: // 根据省名,查询省内的所有城市信息
String provinceName = uri.getPathSegments().get(1);
if (selection == null) {
selection = "province_name='" + provinceName +"'";
selection += "and (province_name='" + provinceName + "')";
cursor = database.query("v_cities_province", projection, selection, selectionArgs, null, null, sortOrder);
throw new IllegalArgumentException(uri+"的格式不正确");
public String getType(Uri uri) {
// TODO Auto-generated method stub
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
在appcation标签中加入:
&provider android:name="RegionContentProvider" android:authorities="com.example.contetnprovider"&&/provider&
然后加一个权限:
&uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&
浏览: 411481 次
为什么我在主线程中定义也会出现这个bug?
Apache2.4.x版wampserver本地php服务器如 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'最近在code review的时候发现很多人的provider定义的不是很好,写的很粗糙 以至于代码健壮性不够好,可读性也不强
但是你既然写了content provider 就是要给别人调用的,如果provider写的漏洞百出的话 &还不如不写,
要么别让别的app 对你的数据进行crud,要么就让自己的app 直接用db 来操作数据,既然要写provider,就要写的标准
优雅~~放一个provider的实例在这里,有大量注释 告诉你为什么要这么写。跟我一样有代码洁癖的人可以参考下。
1 package com.example.
3 import android.net.U
4 import android.provider.BaseC
9 public final class StudentProfile {
* 一般来说 我们的authority都是设置成 我们这个常量类的包名+类名
public static final String AUTHORITY = "com.example.providertest.StudentProfile";
* 注意这个构造函数 是私有的 目的就是让他不能被初始化
private StudentProfile() {
* 实现了这个BaseColumns接口 可以让我们少写几行代码
public static final class Students implements BaseColumns {
* 这个类同样也是不能被初始化的
private Students() {
// 定义我们的表名
public static final String TABLE_NAME = "students";
* 下面开始uri的定义
// uri的scheme部分 这个部分是固定的写法
private static final String SCHEME = "content://";
// 部分学生
private static final String PATH_STUDENTS = "/students";
// 某一个学生
private static final String PATH_STUDENTS_ID = "/students/";
* path这边的第几个值是指的位置 我们设置成第一个位置
public static final int STUDENT_ID_PATH_POSITION = 1;
// 这个表的基本的uri格式
public static final Uri CONTENT_URI = Uri.parse(SCHEME + AUTHORITY
+ PATH_STUDENTS);
// 某一条数据的基本uri格式 这个通常在自定義的provider的insert方法里面被调用
public static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME
+ AUTHORITY + PATH_STUDENTS_ID);
* 定义一下我们的mime类型 注意一下mime类型的写法
* 一般都是后面vnd.应用程序的包名.表名
// 多行的mime类型
public static final String CONTENT_TYPE = "vnd.android.cursor..example.providertest.students";
// 单行的mime类型
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/.example.providertest.students";
* 既然provider提供了查询的方法 我们肯定要设置一个默认的排序方式 这里我们就默认让他根据创建的时间 来降序排序
public static final String DEFAULT_SORT_ORDER = "created DESC";
* 下面就是表的列定义了
// 学生的名字
public static final String COLUMN_NAME_NAME = "name";
// 学生的年龄
public static final String COLUMN_NAME_AGE = "age";
// 学生的学号
public static final String COLUMN_NAME_NUMBER = "number";
// 这个学生创建的时间
public static final String COLUMN_NAME_CREATE_DATE = "created";
// 这个学生入库以后修改的时间
public static final String COLUMN_NAME_MODIFICATION_DATE = "modified";
1 package com.example.
3 import java.util.HashM
5 import android.content.ContentP
6 import android.content.ContentU
7 import android.content.ContentV
8 import android.content.C
9 import android.content.UriM
10 import android.database.C
11 import android.database.SQLE
12 import android.database.sqlite.SQLiteD
13 import android.database.sqlite.SQLiteOpenH
14 import android.database.sqlite.SQLiteQueryB
15 import android.net.U
16 import android.text.TextU
17 import android.util.L
19 public class StudentProfileProvider extends ContentProvider {
// tag 打日志用
private static final String TAG = "StudentProfileProvider";
// 数据库的名字
private static final String DATABASE_NAME = "students_info.db";
// 数据库版本号
private static final int DATABASE_VERSION = 1;
* A UriMatcher instance
private static final UriMatcher sUriM
// 匹配成功的返回值 这里代表多行匹配成功
private static final int STUDENTS = 1;
// 匹配成功的返回值 这里代表多单行匹配成功
private static final int STUDENTS_ID = 2;
* 注意看一下这个哈希表 这个哈希表实际上是主要为了SQLiteQueryBuilder这个类的 setProjectionMap这个方法使用的
* 他的值的初始化我放在静态代码块里面,这个地方实际上主要是为了多表查询而存在的
* 比如你要多表查询的时候 你有2个表 一个表A 一个表B 你join的时候 肯定需要重命名某个表的某个列
* 比如你要把表A的 name1 这个列名重命名成 a.name1 那你就可以add一个key value对,key为name1
* value 为a.name1 即可。当然咯 如果你不想重命名或者只是单表查询那就只需要吧key 和value
* 的值都写成 一样的即可
private static HashMap&String, String& sStudentsProjectionM
// 定义数据库helper.
private DatabaseHelper mOpenH
// 静态代码块执行
// 先构造urimatcher
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(StudentProfile.AUTHORITY, "students", STUDENTS);
// #代表任意数字 *一般代表任意文本
sUriMatcher.addURI(StudentProfile.AUTHORITY, "students/#", STUDENTS_ID);
// 因为我们这里是单表查询 所以这个地方key和value的值都写成固定的就可以了
sStudentsProjectionMap = new HashMap&String, String&();
sStudentsProjectionMap.put(StudentProfile.Students._ID,
StudentProfile.Students._ID);
sStudentsProjectionMap.put(StudentProfile.Students.COLUMN_NAME_AGE,
StudentProfile.Students.COLUMN_NAME_AGE);
sStudentsProjectionMap.put(
StudentProfile.Students.COLUMN_NAME_CREATE_DATE,
StudentProfile.Students.COLUMN_NAME_CREATE_DATE);
sStudentsProjectionMap.put(
StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE,
StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE);
sStudentsProjectionMap.put(StudentProfile.Students.COLUMN_NAME_NAME,
StudentProfile.Students.COLUMN_NAME_NAME);
sStudentsProjectionMap.put(StudentProfile.Students.COLUMN_NAME_NUMBER,
StudentProfile.Students.COLUMN_NAME_NUMBER);
public boolean onCreate() {
// TODO Auto-generated method stub
mOpenHelper = new DatabaseHelper(getContext());
return true;
* 对于自定义contentprovider来说CRUD的这几个方法的写法 要尽量保证 代码优美 和 容错性高
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(StudentProfile.Students.TABLE_NAME);
// 先匹配uri
switch (sUriMatcher.match(uri)) {
// 多行查询
case STUDENTS:
qb.setProjectionMap(sStudentsProjectionMap);
// 单行查询
case STUDENTS_ID:
qb.setProjectionMap(sStudentsProjectionMap);
qb.appendWhere(StudentProfile.Students._ID
+ uri.getPathSegments().get(
StudentProfile.Students.STUDENT_ID_PATH_POSITION));
throw new IllegalArgumentException("Unknown uri" + uri);
// 如果没有传orderby的值过来 那我们就使用默认的
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = StudentProfile.Students.DEFAULT_SORT_ORDER;
// 如果传过来了 就使用传来的值
orderBy = sortO
// 开始操作数据库
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null,
null, orderBy);
// 这个地方要解释一下 这句语句的作用,很多人自定义provider的时候 在query方法里面都忘记
// 写这句话,有的人写了也不知道这句话是干嘛的,实际上这句话就是给我们的cursor加了一个观察者
// 有兴趣的可以看一下sdk里面这个函数的源码,非常简单。那么他的实际作用就是如果返回的cursor
// 被用在SimpleCursorAdapter 类似的这种adapter的话,一旦uri所对应的provider数据发生了变化
// 那么这个adapter里的数据是会自己变化刷新的。这句话起的就是这个作用 有兴趣的可以自己写代码
// 验证一下 如果把这句话删除掉的话 adapter里的数据是不会再uri更新的时候 自动更新的
c.setNotificationUri(getContext().getContentResolver(), uri);
* 这个地方的返回值 一定要和manifest你配置activity的时候data 字段的值相同 不然会报错
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case STUDENTS:
return StudentProfile.Students.CONTENT_TYPE;
case STUDENTS_ID:
return StudentProfile.Students.CONTENT_ITEM_TYPE;
// 注意这个地方记得不匹配的时候抛出异常信息 这样当比人调用失败的时候会知道哪里不对
throw new IllegalArgumentException("Unknown uri" + uri);
public Uri insert(Uri uri, ContentValues initialValues) {
if (sUriMatcher.match(uri) != STUDENTS) {
throw new IllegalArgumentException("Unknown URI " + uri);
if (initialValues != null) {
values = new ContentValues(initialValues);
values = new ContentValues();
// 下面几行代码实际上就是告诉我们对于某些表而言 默认的字段的值 可以在insert里面自己写好
// 不要让调用者去手动再做重复劳动,我们应该允许调用者写入最少的字段的值 来完成db的insert
Long now = Long.valueOf(System.currentTimeMillis());
if (values.containsKey(StudentProfile.Students.COLUMN_NAME_CREATE_DATE) == false) {
values.put(StudentProfile.Students.COLUMN_NAME_CREATE_DATE, now);
if (values
.containsKey(StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE) == false) {
values.put(StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE,
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(StudentProfile.Students.TABLE_NAME,
StudentProfile.Students.COLUMN_NAME_NAME, values);
if (rowId & 0) {
Uri stuUri = ContentUris.withAppendedId(
StudentProfile.Students.CONTENT_ID_URI_BASE, rowId);
// 用于通知所有观察者数据已经改变
getContext().getContentResolver().notifyChange(stuUri, null);
return stuU
// 如果插入失败也最好抛出异常 通知调用者
throw new SQLException("Failed to insert row into " + uri);
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String finalW
switch (sUriMatcher.match(uri)) {
case STUDENTS:
count = db.delete(StudentProfile.Students.TABLE_NAME, where,
whereArgs);
case STUDENTS_ID:
finalWhere = StudentProfile.Students._ID
+ uri.getPathSegments().get(
StudentProfile.Students.STUDENT_ID_PATH_POSITION);
if (where != null) {
finalWhere = finalWhere + " AND " +
count = db.delete(StudentProfile.Students.TABLE_NAME, finalWhere,
whereArgs);
throw new IllegalArgumentException("Unknown URI " + uri);
getContext().getContentResolver().notifyChange(uri, null);
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String finalW
switch (sUriMatcher.match(uri)) {
case STUDENTS:
count = db.update(StudentProfile.Students.TABLE_NAME, values,
where, whereArgs);
case STUDENTS_ID:
finalWhere = StudentProfile.Students._ID
+ uri.getPathSegments().get(
StudentProfile.Students.STUDENT_ID_PATH_POSITION);
if (where != null) {
finalWhere = finalWhere + " AND " +
count = db.update(StudentProfile.Students.TABLE_NAME, values,
finalWhere, whereArgs);
throw new IllegalArgumentException("Unknown URI " + uri);
getContext().getContentResolver().notifyChange(uri, null);
// 自定义helper
static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("CREATE TABLE " + StudentProfile.Students.TABLE_NAME
+ " (" + StudentProfile.Students._ID
+ " INTEGER PRIMARY KEY,"
+ StudentProfile.Students.COLUMN_NAME_NAME + " TEXT,"
+ StudentProfile.Students.COLUMN_NAME_NUMBER + " TEXT,"
+ StudentProfile.Students.COLUMN_NAME_AGE + " INTEGER,"
+ StudentProfile.Students.COLUMN_NAME_CREATE_DATE
+ " INTEGER,"
+ StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE
+ " INTEGER" + ");");
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
// 数据库升级的时候 这边的代码 不写了,看各自的业务逻辑了,一般建议大家在这个地方多打一些日志
阅读(...) 评论()【Android 开发教程】自定义ContentProvider - CSDN博客
【Android 开发教程】自定义ContentProvider
本章节翻译自《Beginning-Android-4-Application-Development》,如有翻译不当的地方,敬请指出。原书购买地址在Android平台上创建ContentProvider,相对而言是很容易的。你所需做的就是继承ContentProvider这个抽象类,然后重新它里面的各种方法。下面将介绍如何创建一个ContentProvider,用来存储一些图书的信息。1. 使用Eclipse创建一个工程,ContentProviders。2. 新建一个名为BooksProvider类。3. BooksProvider.java中的代码。public class BooksProvider extends ContentProvider
static final String PROVIDER_NAME =
&net.manoel.provider.Books&;
static final Uri CONTENT_URI =
Uri.parse(&content://&+ PROVIDER_NAME + &/books&);
static final String _ID = &_id&;
static final String TITLE = &title&;
static final String ISBN = &isbn&;
static final int BOOKS = 1;
static final int BOOK_ID = 2;
private static final UriMatcher uriM
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, &books&, BOOKS);
uriMatcher.addURI(PROVIDER_NAME, &books/#&, BOOK_ID);
//---for database use---
SQLiteDatabase booksDB;
static final String DATABASE_NAME = &Books&;
static final String DATABASE_TABLE = &titles&;
static final int DATABASE_VERSION = 1;
static final String DATABASE_CREATE =
&create table & + DATABASE_TABLE +
& (_id integer primary key autoincrement, &
+ &title text not null, isbn text not null);&;
private static class DatabaseHelper extends SQLiteOpenHelper
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
public void onCreate(SQLiteDatabase db)
db.execSQL(DATABASE_CREATE);
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion) {
Log.w(&Content provider database&,
&Upgrading database from version & +
oldVersion + & to & + newVersion +
&, which will destroy all old data&);
db.execSQL(&DROP TABLE IF EXISTS titles&);
onCreate(db);
public int delete(Uri arg0, String arg1, String[] arg2) {
// arg0 = uri
// arg1 = selection
// arg2 = selectionArgs
int count=0;
switch (uriMatcher.match(arg0)){
case BOOKS:
count = booksDB.delete(
DATABASE_TABLE,
case BOOK_ID:
String id = arg0.getPathSegments().get(1);
count = booksDB.delete(
DATABASE_TABLE,
_ID + & = & + id +
(!TextUtils.isEmpty(arg1) ? & AND (& +
arg1 + ')' : &&),
default: throw new IllegalArgumentException(&Unknown URI & + arg0);
getContext().getContentResolver().notifyChange(arg0, null);
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
//---get all books---
case BOOKS:
return &vnd.android.cursor.dir/vnd.manoel.books &;
//---get a particular book---
case BOOK_ID:
return &vnd.android.cursor.item/vnd.manoel.books &;
throw new IllegalArgumentException(&Unsupported URI: & + uri);
public Uri insert(Uri uri, ContentValues values) {
//---add a new book---
long rowID = booksDB.insert(
DATABASE_TABLE,
//---if added successfully---
if (rowID&0)
Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
throw new SQLException(&Failed to insert row into & + uri);
public boolean onCreate() {
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
booksDB = dbHelper.getWritableDatabase();
return (booksDB == null)? false:
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
sqlBuilder.setTables(DATABASE_TABLE);
if (uriMatcher.match(uri) == BOOK_ID)
//---if getting a particular book---
sqlBuilder.appendWhere(
_ID + & = & + uri.getPathSegments().get(1));
if (sortOrder==null || sortOrder==&&)
sortOrder = TITLE;
Cursor c = sqlBuilder.query(
projection,
selection,
selectionArgs,
sortOrder);
//---register to watch a content URI for changes---
c.setNotificationUri(getContext().getContentResolver(), uri);
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)){
case BOOKS:
count = booksDB.update(
DATABASE_TABLE,
selection,
selectionArgs);
case BOOK_ID:
count = booksDB.update(
DATABASE_TABLE,
_ID + & = & + uri.getPathSegments().get(1) +
(!TextUtils.isEmpty(selection) ? & AND (& +
selection + ')' : &&),
selectionArgs);
default: throw new IllegalArgumentException(&Unknown URI & + uri);
getContext().getContentResolver().notifyChange(uri, null);
}4. 在AndroidManifest.xml中添加声明。&?xml version=&1.0& encoding=&utf-8&?&
&manifest xmlns:android=&/apk/res/android&
package=&net.manoel.ContentProviders&
android:versionCode=&1&
android:versionName=&1.0& &
&uses-sdk android:minSdkVersion=&14& /&
&application
android:icon=&@drawable/ic_launcher&
android:label=&@string/app_name& &
android:label=&@string/app_name&
android:name=&.ContentProvidersActivity& &
&intent-filter &
&action android:name=&android.intent.action.MAIN& /&
&category android:name=&android.intent.category.LAUNCHER& /&
&/intent-filter&
&/activity&
&provider android:name=&BooksProvider&
android:authorities=&net.manoel.provider.Books&&
&/provider&
&/application&
&/manifest&在这个例子中,首先创建一个ContentProvider的子类,名字叫BooksProvider。需要重写一些方法:getTpye() - 返回给定的URI数据的MIME类型onCreate() - 当provider启动的时候被调用query() - 接收客户的请求。返回一个Cursor对象。insert() - 向provider中插入一条新记录delete() - 在provider中删除一条已存在的记录update() - 在provider中更新一条已存在的记录在content provider中,可以任意选择要存储的数据 - 传统的文件,XML,数据库,甚至web service。接下来,在BooksProvider.java类中定义一些常量: static final String PROVIDER_NAME =
&net.manoel.provider.Books&;
static final Uri CONTENT_URI =
Uri.parse(&content://&+ PROVIDER_NAME + &/books&);
static final String _ID = &_id&;
static final String TITLE = &title&;
static final String ISBN = &isbn&;
static final int BOOKS = 1;
static final int BOOK_ID = 2;
private static final UriMatcher uriM
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, &books&, BOOKS);
uriMatcher.addURI(PROVIDER_NAME, &books/#&, BOOK_ID);
}观察上面的代码,使用的一个UriMatcher对象去转换URI,这个URI将会作为一个参数传递给ContentResolver。举个例子,下面的URI代表了请求所有的图书:content://net.manoel.provider.Books/books下面的URI代表了请求指定_id 5为的图书:content://net.manoel.provider.Books/books/5content provider 使用一个SQlite数据库去存储图书。注意一点,这里使用SQLiteOpenHelper辅助类去操纵数据库:private static class DatabaseHelper extends SQLiteOpenHelper
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
public void onCreate(SQLiteDatabase db)
db.execSQL(DATABASE_CREATE);
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion) {
Log.w(&Content provider database&,
&Upgrading database from version & +
oldVersion + & to & + newVersion +
&, which will destroy all old data&);
db.execSQL(&DROP TABLE IF EXISTS titles&);
onCreate(db);
}接下来,重写getType()方法,唯一可以描述content provider的数据类型。使用UriMatcher对象,返回vnd.android.cursor.item/vnd.manoel.books代表单独的一本书,vnd.android.cursor.dir/vnd.android.cursor.item/vnd.manoel.bools 代表多本书: @Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
//---get all books---
case BOOKS:
return &vnd.android.cursor.dir/vnd.manoel.books &;
//---get a particular book---
case BOOK_ID:
return &vnd.android.cursor.item/vnd.manoel.books &;
throw new IllegalArgumentException(&Unsupported URI: & + uri);
}然后,重写onCreate()方法,当provider被开启的时候,和数据库建立建立链接: @Override
public boolean onCreate() {
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
booksDB = dbHelper.getWritableDatabase();
return (booksDB == null)? false:
}然后,重新query()方法: @Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
sqlBuilder.setTables(DATABASE_TABLE);
if (uriMatcher.match(uri) == BOOK_ID)
//---if getting a particular book---
sqlBuilder.appendWhere(
_ID + & = & + uri.getPathSegments().get(1));
if (sortOrder==null || sortOrder==&&)
sortOrder = TITLE;
Cursor c = sqlBuilder.query(
projection,
selection,
selectionArgs,
sortOrder);
//---register to watch a content URI for changes---
c.setNotificationUri(getContext().getContentResolver(), uri);
}查询的结果是一个Cursor对象。然后,重写insert()方法: @Override
public Uri insert(Uri uri, ContentValues values) {
//---add a new book---
long rowID = booksDB.insert(
DATABASE_TABLE,
//---if added successfully---
if (rowID&0)
Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
throw new SQLException(&Failed to insert row into & + uri);
}一旦新的记录插入成功,调用ContentResolver的nofityChande()方法。这就通知观察者一个新的记录被更新了。然后,重新delete()方法: @Override
public int delete(Uri arg0, String arg1, String[] arg2) {
// arg0 = uri
// arg1 = selection
// arg2 = selectionArgs
int count=0;
switch (uriMatcher.match(arg0)){
case BOOKS:
count = booksDB.delete(
DATABASE_TABLE,
case BOOK_ID:
String id = arg0.getPathSegments().get(1);
count = booksDB.delete(
DATABASE_TABLE,
_ID + & = & + id +
(!TextUtils.isEmpty(arg1) ? & AND (& +
arg1 + ')' : &&),
default: throw new IllegalArgumentException(&Unknown URI & + arg0);
getContext().getContentResolver().notifyChange(arg0, null);
}最后,重写update()方法: @Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)){
case BOOKS:
count = booksDB.update(
DATABASE_TABLE,
selection,
selectionArgs);
case BOOK_ID:
count = booksDB.update(
DATABASE_TABLE,
_ID + & = & + uri.getPathSegments().get(1) +
(!TextUtils.isEmpty(selection) ? & AND (& +
selection + ')' : &&),
selectionArgs);
default: throw new IllegalArgumentException(&Unknown URI & + uri);
getContext().getContentResolver().notifyChange(uri, null);
}最后的最后,别忘了在AndroidManifest.xml中声明这个ContentProvider。
本文已收录于以下专栏:
相关文章推荐
苦心人天不负卧薪尝胆三千越甲可吞吴,有志者天不负釜底抽薪百二秦川终属楚。
Android允许我们定义自己的的ContentProvider对象来共享数据,练练手,简单来实现一下。
要使用ContentProvider来操作数据,必须要有保存数据的场所。可以使用文件或SQL...
Android 中ContentProvider的使用,自定义ContentProvider的用法,使用URIMatcher进行匹配:uriMatcher.addURI();uriMatcher.ma...
1.   适用范围
对于什么情况下才会用到自定义的ContentProvider,官方文档的Dev Guide是这样描述的:
如果你想要提供以下的一种或几种特性的时候你才需要构造一个Content...
自定义ContentProvider是要继承ContentProvider,那么在自定义之前 ,需要做一些准备工作
1、定义合同类(Contract Class)
这个名字是我自己取的,可能翻译不...
接上一篇:程序员必知的8大排序(二)-------简单选择排序,堆排序(java实现)
5.冒泡排序
(1)基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依...
是否需要覆盖ContentProvider的getType方法?      不少关于android的书籍都谈到了ContentProvider,而且给了例子,但是对ContentProvider的ge...
程序入口点
类似于win32程序里的WinMain函数,Android自然也有它的程序入口点。它通过在AndroidManifest.xml文件中配置来指明,可以看到名为NotesList的...
在上篇博客中我们介绍了自定义ContentProvider,但是遗漏掉了一个方法,那就是getType,自定义ContentProvider一般用不上getType方法,但我们还是一起来探究下这个方法...
找了几篇文章,全都写得云里雾里的
他的最新文章
讲师:宋宝华
讲师:何宇健
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 如何自定义一个异常类 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信