ContentProvider实现举例
Author: nex3z
2016-01-10
ContentProvider将数据封装起来,对外提供统一的接口,使得对数据的访问独立于数据的存储方式(文件、数据库等)。ContentProvider还提供跨应用的数据访问能力,并可以通过Search Framework实现自定义的搜索建议。可以参考这里来决定是否使用ContentProvider。
下面例子在RecyclerView使用方法举例(1)的基础上,展示ContentProvider的一个简单实现。
1. 设计数据存储方式
ContentProvider是访问数据的接口,首先要决定数据的具体存储方式。这里使用SQLite存储从TMDb上取得的一系列电影信息。
1.1. 设计Contract
Contract定义了数据的具体结构,以及用于访问数据的URI的结构。参考这里的从TMDb取得JSON文件样例,电影信息应包含如下几个字段:
public static final String COLUMN_TITLE = "title";
public static final String COLUMN_POSTER_PATH = "poster_path";
public static final String COLUMN_RELEASE_DATE = "release_date";
public static final String COLUMN_OVERVIEW = "overview";
public static final String COLUMN_BACKDROP_PATH = "backdrop_path";
public static final String COLUMN_POPULARITY = "popularity";
public static final String COLUMN_VOTE_AVERAGE = "vote_average";
public static final String COLUMN_VOTE_COUNT = "vote_count";
public static final String COLUMN_ORIGINAL_TITLE = "original_title";
public static final String COLUMN_ORIGINAL_LANGUAGE = "original_language";
public static final String COLUMN_ADULT = "adult";
public static final String COLUMN_VIDEO = "video";
public static final String COLUMN_GENRE_IDS = "genre_ids";
public static final String COLUMN_ID = "id";
public static final String COLUMN_TITLE = "title";
public static final String COLUMN_POSTER_PATH = "poster_path";
public static final String COLUMN_RELEASE_DATE = "release_date";
public static final String COLUMN_OVERVIEW = "overview";
public static final String COLUMN_BACKDROP_PATH = "backdrop_path";
public static final String COLUMN_POPULARITY = "popularity";
public static final String COLUMN_VOTE_AVERAGE = "vote_average";
public static final String COLUMN_VOTE_COUNT = "vote_count";
public static final String COLUMN_ORIGINAL_TITLE = "original_title";
public static final String COLUMN_ORIGINAL_LANGUAGE = "original_language";
public static final String COLUMN_ADULT = "adult";
public static final String COLUMN_VIDEO = "video";
public static final String COLUMN_GENRE_IDS = "genre_ids";
public static final String COLUMN_ID = "id";
public static final String COLUMN_TITLE = "title";
public static final String COLUMN_POSTER_PATH = "poster_path";
public static final String COLUMN_RELEASE_DATE = "release_date";
public static final String COLUMN_OVERVIEW = "overview";
public static final String COLUMN_BACKDROP_PATH = "backdrop_path";
public static final String COLUMN_POPULARITY = "popularity";
public static final String COLUMN_VOTE_AVERAGE = "vote_average";
public static final String COLUMN_VOTE_COUNT = "vote_count";
public static final String COLUMN_ORIGINAL_TITLE = "original_title";
public static final String COLUMN_ORIGINAL_LANGUAGE = "original_language";
public static final String COLUMN_ADULT = "adult";
public static final String COLUMN_VIDEO = "video";
public static final String COLUMN_GENRE_IDS = "genre_ids";
public static final String COLUMN_ID = "id";
定义Provider的名称,即Authority:
public static final String CONTENT_AUTHORITY = "com.nex3z.examples.contentprovider";
public static final String CONTENT_AUTHORITY = "com.nex3z.examples.contentprovider";
public static final String CONTENT_AUTHORITY = "com.nex3z.examples.contentprovider";
在此基础上,定义URI的格式:
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
public static final String PATH_MOVIE = "movie";
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_MOVIE).build();
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
public static final String PATH_MOVIE = "movie";
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_MOVIE).build();
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
public static final String PATH_MOVIE = "movie";
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_MOVIE).build();
定义Content Type:
public static final String CONTENT_TYPE =
ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_MOVIE;
public static final String CONTENT_ITEM_TYPE =
ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_MOVIE;
public static final String CONTENT_TYPE =
ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_MOVIE;
public static final String CONTENT_ITEM_TYPE =
ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_MOVIE;
public static final String CONTENT_TYPE =
ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_MOVIE;
public static final String CONTENT_ITEM_TYPE =
ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_MOVIE;
这里CURSOR_DIR_BASE_TYPE 指示URI包含的Cursor具有零个或多个元素,CONTENT_ITEM_TYPE 指示URI包含的Cursor只具有一个元素。
【完整代码】
1.2. 实现数据库存储
Android提供了SQLiteOpenHelper用于数据库的创建和版本管理,创建其子类来实现电影数据库的创建:
public class MovieDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 4;
static final String DATABASE_NAME = "movie.db";
public MovieDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
public void onCreate(SQLiteDatabase db) {
final String SQL_CREATE_MOVIE_TABLE = "CREATE TABLE " + MovieEntry.TABLE_NAME + " (" +
MovieEntry._ID + " INTEGER PRIMARY KEY," +
MovieEntry.COLUMN_TITLE + " TEXT NOT NULL, " +
MovieEntry.COLUMN_POSTER_PATH + " TEXT NOT NULL, " +
MovieEntry.COLUMN_RELEASE_DATE + " DATE NOT NULL, " +
MovieEntry.COLUMN_OVERVIEW + " TEXT NOT NULL, " +
MovieEntry.COLUMN_BACKDROP_PATH + " TEXT NOT NULL, " +
MovieEntry.COLUMN_POPULARITY + " REAL NOT NULL, " +
MovieEntry.COLUMN_VOTE_AVERAGE + " REAL NOT NULL, " +
MovieEntry.COLUMN_VOTE_COUNT + " INTEGER NOT NULL, " +
MovieEntry.COLUMN_ORIGINAL_TITLE + " TEXT NOT NULL, " +
MovieEntry.COLUMN_ORIGINAL_LANGUAGE + " TEXT NOT NULL, " +
MovieEntry.COLUMN_ADULT + " BOOLEAN NOT NULL, " +
MovieEntry.COLUMN_VIDEO + " BOOLEAN NOT NULL, " +
MovieEntry.COLUMN_GENRE_IDS + " TEXT NOT NULL, " +
MovieEntry.COLUMN_ID + " INTEGER UNIQUE NOT NULL " +
db.execSQL(SQL_CREATE_MOVIE_TABLE);
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + MovieEntry.TABLE_NAME);
public class MovieDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 4;
static final String DATABASE_NAME = "movie.db";
public MovieDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
final String SQL_CREATE_MOVIE_TABLE = "CREATE TABLE " + MovieEntry.TABLE_NAME + " (" +
MovieEntry._ID + " INTEGER PRIMARY KEY," +
MovieEntry.COLUMN_TITLE + " TEXT NOT NULL, " +
MovieEntry.COLUMN_POSTER_PATH + " TEXT NOT NULL, " +
MovieEntry.COLUMN_RELEASE_DATE + " DATE NOT NULL, " +
MovieEntry.COLUMN_OVERVIEW + " TEXT NOT NULL, " +
MovieEntry.COLUMN_BACKDROP_PATH + " TEXT NOT NULL, " +
MovieEntry.COLUMN_POPULARITY + " REAL NOT NULL, " +
MovieEntry.COLUMN_VOTE_AVERAGE + " REAL NOT NULL, " +
MovieEntry.COLUMN_VOTE_COUNT + " INTEGER NOT NULL, " +
MovieEntry.COLUMN_ORIGINAL_TITLE + " TEXT NOT NULL, " +
MovieEntry.COLUMN_ORIGINAL_LANGUAGE + " TEXT NOT NULL, " +
MovieEntry.COLUMN_ADULT + " BOOLEAN NOT NULL, " +
MovieEntry.COLUMN_VIDEO + " BOOLEAN NOT NULL, " +
MovieEntry.COLUMN_GENRE_IDS + " TEXT NOT NULL, " +
MovieEntry.COLUMN_ID + " INTEGER UNIQUE NOT NULL " +
" );";
db.execSQL(SQL_CREATE_MOVIE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + MovieEntry.TABLE_NAME);
onCreate(db);
}
}
public class MovieDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 4;
static final String DATABASE_NAME = "movie.db";
public MovieDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
final String SQL_CREATE_MOVIE_TABLE = "CREATE TABLE " + MovieEntry.TABLE_NAME + " (" +
MovieEntry._ID + " INTEGER PRIMARY KEY," +
MovieEntry.COLUMN_TITLE + " TEXT NOT NULL, " +
MovieEntry.COLUMN_POSTER_PATH + " TEXT NOT NULL, " +
MovieEntry.COLUMN_RELEASE_DATE + " DATE NOT NULL, " +
MovieEntry.COLUMN_OVERVIEW + " TEXT NOT NULL, " +
MovieEntry.COLUMN_BACKDROP_PATH + " TEXT NOT NULL, " +
MovieEntry.COLUMN_POPULARITY + " REAL NOT NULL, " +
MovieEntry.COLUMN_VOTE_AVERAGE + " REAL NOT NULL, " +
MovieEntry.COLUMN_VOTE_COUNT + " INTEGER NOT NULL, " +
MovieEntry.COLUMN_ORIGINAL_TITLE + " TEXT NOT NULL, " +
MovieEntry.COLUMN_ORIGINAL_LANGUAGE + " TEXT NOT NULL, " +
MovieEntry.COLUMN_ADULT + " BOOLEAN NOT NULL, " +
MovieEntry.COLUMN_VIDEO + " BOOLEAN NOT NULL, " +
MovieEntry.COLUMN_GENRE_IDS + " TEXT NOT NULL, " +
MovieEntry.COLUMN_ID + " INTEGER UNIQUE NOT NULL " +
" );";
db.execSQL(SQL_CREATE_MOVIE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + MovieEntry.TABLE_NAME);
onCreate(db);
}
}
在onCreate() 中根据Contract中的定义,构造创建数据库的SQL语句并执行,由此创建数据库。DATABASE_VERSION 指示数据库版本,onUpgrade() 用来处理数据库版本升级,这里只是将数据库删除重新创建,因为例子中使用的数据都可以从TMDb获取。
【完整代码】
1.3. 实现Content Provider
实现Content Provider首先要继承ContentProvider,主要工作是实现query() 、insert() 、update() 、delete() 四个方法。另外还要实现一个UriMatcher,用于获取访问数据的URI的类型,根据不同的URI类型,前面的四个方法可能需要进行不同的处理。
1.3.1. 实现UriMatcher
首先定义URI类型,这里只有两种:
static final int MOVIE = 100;
static final int MOVIE_WITH_ID = 101;
static final int MOVIE = 100;
static final int MOVIE_WITH_ID = 101;
static final int MOVIE = 100;
static final int MOVIE_WITH_ID = 101;
MOVIE 类型对应content://com.nex3z.examples.contentprovider/movie ,用于获取所有电影数据;MOVIE_WITH_ID 类型对应形如content://com.nex3z.examples.contentprovider/movie/# 的URI,这里的# 表示任意数字,用于获取id为# 的一个电影。此外还可以用* 表示任意有效字符,这里没有使用。
然后实现UriMatcher:
private static final UriMatcher sUriMatcher = buildUriMatcher();
static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = MovieContract.CONTENT_AUTHORITY;
matcher.addURI(authority, MovieContract.PATH_MOVIE, MOVIE);
matcher.addURI(authority, MovieContract.PATH_MOVIE + "/#", MOVIE_WITH_ID);
private static final UriMatcher sUriMatcher = buildUriMatcher();
static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = MovieContract.CONTENT_AUTHORITY;
matcher.addURI(authority, MovieContract.PATH_MOVIE, MOVIE);
matcher.addURI(authority, MovieContract.PATH_MOVIE + "/#", MOVIE_WITH_ID);
return matcher;
}
private static final UriMatcher sUriMatcher = buildUriMatcher();
static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = MovieContract.CONTENT_AUTHORITY;
matcher.addURI(authority, MovieContract.PATH_MOVIE, MOVIE);
matcher.addURI(authority, MovieContract.PATH_MOVIE + "/#", MOVIE_WITH_ID);
return matcher;
}
1.3.2. 实现query()
query()用于请求数据。针对MOVIE_WITH_ID 类型的URI,只向数据库请求指定id的电影;针对MOVIE 类型的URI,向数据库请求所有电影。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
switch (sUriMatcher.match(uri)) {
retCursor = getMovieById(uri, projection, sortOrder);
retCursor = mOpenHelper.getReadableDatabase().query(
MovieContract.MovieEntry.TABLE_NAME,
throw new UnsupportedOperationException("Unknown uri: " + uri);
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor retCursor;
switch (sUriMatcher.match(uri)) {
case MOVIE_WITH_ID: {
retCursor = getMovieById(uri, projection, sortOrder);
break;
}
case MOVIE: {
retCursor = mOpenHelper.getReadableDatabase().query(
MovieContract.MovieEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor retCursor;
switch (sUriMatcher.match(uri)) {
case MOVIE_WITH_ID: {
retCursor = getMovieById(uri, projection, sortOrder);
break;
}
case MOVIE: {
retCursor = mOpenHelper.getReadableDatabase().query(
MovieContract.MovieEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
这里getMovieById() 实现如下:
private Cursor getMovieById(Uri uri, String[] projection, String sortOrder) {
int movieId = MovieContract.MovieEntry.getMovieIdFromUri(uri);
String[] selectionArgs = new String[]{Integer.toString(movieId)};
String selection = sMovieIdSelection;
return sMovieQueryBuilder.query(mOpenHelper.getReadableDatabase(),
private Cursor getMovieById(Uri uri, String[] projection, String sortOrder) {
int movieId = MovieContract.MovieEntry.getMovieIdFromUri(uri);
String[] selectionArgs = new String[]{Integer.toString(movieId)};
String selection = sMovieIdSelection;
return sMovieQueryBuilder.query(mOpenHelper.getReadableDatabase(),
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
}
private Cursor getMovieById(Uri uri, String[] projection, String sortOrder) {
int movieId = MovieContract.MovieEntry.getMovieIdFromUri(uri);
String[] selectionArgs = new String[]{Integer.toString(movieId)};
String selection = sMovieIdSelection;
return sMovieQueryBuilder.query(mOpenHelper.getReadableDatabase(),
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
}
其中sMovieIdSelection 和sMovieQueryBuilder 分别为:
private static final String sMovieIdSelection = MovieContract.MovieEntry.TABLE_NAME + "." +
MovieContract.MovieEntry.COLUMN_ID + " = ?";
private static final String sMovieIdSelection = MovieContract.MovieEntry.TABLE_NAME + "." +
MovieContract.MovieEntry.COLUMN_ID + " = ?";
private static final String sMovieIdSelection = MovieContract.MovieEntry.TABLE_NAME + "." +
MovieContract.MovieEntry.COLUMN_ID + " = ?";
private static final SQLiteQueryBuilder sMovieQueryBuilder;
sMovieQueryBuilder = new SQLiteQueryBuilder();
sMovieQueryBuilder.setTables(MovieContract.MovieEntry.TABLE_NAME);
private static final SQLiteQueryBuilder sMovieQueryBuilder;
static{
sMovieQueryBuilder = new SQLiteQueryBuilder();
sMovieQueryBuilder.setTables(MovieContract.MovieEntry.TABLE_NAME);
}
private static final SQLiteQueryBuilder sMovieQueryBuilder;
static{
sMovieQueryBuilder = new SQLiteQueryBuilder();
sMovieQueryBuilder.setTables(MovieContract.MovieEntry.TABLE_NAME);
}
这里sMovieIdSelection 用问号?代替了参数,sMovieQueryBuilder.query() 通过分别指定selection 和selectionArgs 分离了查询和参数,可以防止数据库注入。
1.3.3. 实现insert()
insert()用于插入数据,向数据库插入数据后,使用notifyChange() 通知数据变化,生成对应的URI并返回:
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
long _id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, values);
returnUri = MovieContract.MovieEntry.buildMovieUri(_id);
throw new android.database.SQLException("Failed to insert row into " + uri);
throw new UnsupportedOperationException("Unknown uri: " + uri);
getContext().getContentResolver().notifyChange(uri, null);
@Override
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
Uri returnUri;
switch (match) {
case MOVIE: {
long _id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, values);
if ( _id > 0 )
returnUri = MovieContract.MovieEntry.buildMovieUri(_id);
else
throw new android.database.SQLException("Failed to insert row into " + uri);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return returnUri;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
Uri returnUri;
switch (match) {
case MOVIE: {
long _id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, values);
if ( _id > 0 )
returnUri = MovieContract.MovieEntry.buildMovieUri(_id);
else
throw new android.database.SQLException("Failed to insert row into " + uri);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return returnUri;
}
1.3.4. 实现update()
update()用于更新已有数据,成功更新后,使用notifyChange() 通知数据变化:
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
rowsUpdated = db.update(MovieContract.MovieEntry.TABLE_NAME, values, selection,
throw new UnsupportedOperationException("Unknown uri: " + uri);
getContext().getContentResolver().notifyChange(uri, null);
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int rowsUpdated;
switch (match) {
case MOVIE: {
rowsUpdated = db.update(MovieContract.MovieEntry.TABLE_NAME, values, selection,
selectionArgs);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if (rowsUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsUpdated;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int rowsUpdated;
switch (match) {
case MOVIE: {
rowsUpdated = db.update(MovieContract.MovieEntry.TABLE_NAME, values, selection,
selectionArgs);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if (rowsUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsUpdated;
}
1.3.5. 实现delete()
delete()用于删除数据,成功删除后,同样使用notifyChange() 通知数据变化:
public int delete(Uri uri, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
if ( null == selection ) selection = "1";
MovieContract.MovieEntry.TABLE_NAME, selection, selectionArgs);
throw new UnsupportedOperationException("Unknown uri: " + uri);
getContext().getContentResolver().notifyChange(uri, null);
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int rowsDeleted;
if ( null == selection ) selection = "1";
switch (match) {
case MOVIE: {
rowsDeleted = db.delete(
MovieContract.MovieEntry.TABLE_NAME, selection, selectionArgs);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if (rowsDeleted != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsDeleted;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int rowsDeleted;
if ( null == selection ) selection = "1";
switch (match) {
case MOVIE: {
rowsDeleted = db.delete(
MovieContract.MovieEntry.TABLE_NAME, selection, selectionArgs);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if (rowsDeleted != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsDeleted;
}
1.3.6. 实现bulkInsert()
如果数据条目较多,单独使用insert()效率较低,bulkInsert()提供了打包插入数据的方法,更加高效。
public int bulkInsert(Uri uri, ContentValues[] values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
for (ContentValues value : values) {
long _id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, value);
db.setTransactionSuccessful();
getContext().getContentResolver().notifyChange(uri, null);
return super.bulkInsert(uri, values);
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
switch (match) {
case MOVIE: {
db.beginTransaction();
int returnCount = 0;
try {
for (ContentValues value : values) {
long _id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, value);
if (_id != -1) {
returnCount++;
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
getContext().getContentResolver().notifyChange(uri, null);
return returnCount;
}
default:
return super.bulkInsert(uri, values);
}
}
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
switch (match) {
case MOVIE: {
db.beginTransaction();
int returnCount = 0;
try {
for (ContentValues value : values) {
long _id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, value);
if (_id != -1) {
returnCount++;
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
getContext().getContentResolver().notifyChange(uri, null);
return returnCount;
}
default:
return super.bulkInsert(uri, values);
}
}
这里先使用beginTransaction() 以EXCLUSIVE模式开始一个Transaction,然后依次插入数据,此时数据并没有被真正插入。之后调用setTransactionSuccessful() 标记当前Transaction为成功,最后通过endTransaction() 应用修改。
【完整代码】
2. 使用ContentProvider
2.1. 声明Provider
使用ContentProvider前,首先要在AndroidManifest.xml中声明所使用的Provider:
android:name=".provider.MovieProvider"
android:authorities="com.nex3z.examples.contentprovider" />
<application
...>
<activity
...
</activity>
<provider
android:name=".provider.MovieProvider"
android:authorities="com.nex3z.examples.contentprovider" />
</application>
<application
...>
<activity
...
</activity>
<provider
android:name=".provider.MovieProvider"
android:authorities="com.nex3z.examples.contentprovider" />
</application>
【完整代码】
2.2. 插入数据
从Retorfit获取到Movie后,首先转换为ContentValue,然后就可以通过bulkInsert() 插入到数据库了。
public static void addToDatabase(Context context, List<Movie> movies) {
Vector<ContentValues> cVVector = new Vector<ContentValues>(movies.size());
for (int i = 0; i < movies.size(); i++) {
ContentValues movieValues = buildContentValues(movies.get(i));
cVVector.add(movieValues);
if (cVVector.size() > 0) {
ContentValues[] cvArray = new ContentValues[cVVector.size()];
cVVector.toArray(cvArray);
context.getContentResolver()
.bulkInsert(MovieContract.MovieEntry.CONTENT_URI, cvArray);
Log.d(LOG_TAG, "addToDatabase(): Write database Complete, "
+ cVVector.size() + " Inserted.");
public static void addToDatabase(Context context, List<Movie> movies) {
Vector<ContentValues> cVVector = new Vector<ContentValues>(movies.size());
for (int i = 0; i < movies.size(); i++) {
ContentValues movieValues = buildContentValues(movies.get(i));
cVVector.add(movieValues);
}
if (cVVector.size() > 0) {
ContentValues[] cvArray = new ContentValues[cVVector.size()];
cVVector.toArray(cvArray);
context.getContentResolver()
.bulkInsert(MovieContract.MovieEntry.CONTENT_URI, cvArray);
}
Log.d(LOG_TAG, "addToDatabase(): Write database Complete, "
+ cVVector.size() + " Inserted.");
}
public static void addToDatabase(Context context, List<Movie> movies) {
Vector<ContentValues> cVVector = new Vector<ContentValues>(movies.size());
for (int i = 0; i < movies.size(); i++) {
ContentValues movieValues = buildContentValues(movies.get(i));
cVVector.add(movieValues);
}
if (cVVector.size() > 0) {
ContentValues[] cvArray = new ContentValues[cVVector.size()];
cVVector.toArray(cvArray);
context.getContentResolver()
.bulkInsert(MovieContract.MovieEntry.CONTENT_URI, cvArray);
}
Log.d(LOG_TAG, "addToDatabase(): Write database Complete, "
+ cVVector.size() + " Inserted.");
}
这里getContentResolver() 会根据所使用的URI获取对应的Content Provider,不需要直接指定Content Provider。
【完整代码】
2.3. 请求数据
向Content Provider请求数据的方法可以参考使用CursorLoader及更新RecyclerView。
3. 完整代码
完整代码可以参考这里。