概述
今天来看看ContentProvider的使用。
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "DatabaseHelper";
//数据库名称
private static final String DATABASE_NAME = "person.db";
//数据库版本
private static final int DATABASE_VERSION = 1;
//表名称
static final String TABLE_STUDENT = "student";
static final String TABLE_TEACHER = "teacher";
//创建表结构的语句,注意primary key必须是integer类型的
private static final String SQL_CREATE_TABLE_STUDENT = "create table " + TABLE_STUDENT + "(" +
BaseColumns._ID + " integer primary key autoincrement,name varchar(20)" + ");";
private static final String SQL_CREATE_TABLE_TEACHER = "create table " + TABLE_TEACHER + "(" +
BaseColumns._ID + " integer primary key autoincrement,name varchar(20)" + ");";
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//第一次创建数据库的时候回调该方法
//当使用getReadableDatabase()方法获取数据库实例的时候, 如果数据库不存在, 就会调用这个方法;
@Override
public void onCreate(SQLiteDatabase db) {
Log.d(TAG, "onCreate: SQL_CREATE_TABLE_STUDENT" + SQL_CREATE_TABLE_STUDENT);
db.execSQL(SQL_CREATE_TABLE_STUDENT);
db.execSQL(SQL_CREATE_TABLE_TEACHER);
}
//版本号改变时会触发此方法
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
public class StudentContentProvider extends ContentProvider {
//ContentProvider唯一标识符,一般是ContentProvier的全类名,要和清单文件中的保持一致
public static final String AUTHORITY = "com.example.app.StudentContentProvider";
//路径部分,一般代表数据的集合,一般用表的名字
public static final String STUDENT = "student";
public static final String TEACHER = "teacher";
//代表特定的记录,如果没有指定,返回全部数据
public static final int MATCH_STUDENT = 1;
public static final int MATCH_TEACHER = 2;
private Uri CONTENT_URI_STUDENT = Uri.parse("content://" + AUTHORITY + "/" + STUDENT);
private Uri CONTENT_URI_TEACHER = Uri.parse("content://" + AUTHORITY + "/" + TEACHER);
//访问多条记录
public static final String CONTENT_STUDENT_TYPE = "vnd.android.cursor.dir/student";
//访问单个记录
public static final String CONTENT_TEACHER_TYPE = "vnd.android.cursor.item/teacher";
private static UriMatcher uriMatcher;
static {
//完整的匹配路径为content://com.example.app.StudentContentProvider/student/1
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//表示匹配com.example.app.StudentContentProvider/student,如果匹配成功,返回1
uriMatcher.addURI(AUTHORITY, STUDENT, MATCH_STUDENT);
uriMatcher.addURI(AUTHORITY, TEACHER, MATCH_TEACHER);
}
private DatabaseHelper mDataBaseHelper;
@Override
public boolean onCreate() {
mDataBaseHelper = new DatabaseHelper(getContext());
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
SQLiteDatabase database = mDataBaseHelper.getReadableDatabase();
switch (uriMatcher.match(uri)) {
case MATCH_STUDENT:
queryBuilder.setTables(DatabaseHelper.TABLE_STUDENT);
break;
case MATCH_TEACHER:
queryBuilder.setTables(DatabaseHelper.TABLE_TEACHER);
break;
}
return queryBuilder.query(database, projection, selection, selectionArgs, null, null, null);
}
/**
* 在query方法中返回Cursor的时候,系统要对Cursor进行分析,进而得出结论,知道该Cursor是多条还是一条记录,
* 如果我们按照谷歌的建议,手动返回了一个能识别的MIME类型,那么系统就不用自己分析,相当于提高了一点点性能
*/
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri)) {
case MATCH_STUDENT:
return CONTENT_STUDENT_TYPE;
case MATCH_TEACHER:
return CONTENT_TEACHER_TYPE;
}
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
SQLiteDatabase database = mDataBaseHelper.getReadableDatabase();
switch (uriMatcher.match(uri)) {
case MATCH_STUDENT: {
long rowId = database.insert(DatabaseHelper.TABLE_STUDENT, null, values);
if (rowId > 0) {
return ContentUris.withAppendedId(CONTENT_URI_STUDENT, rowId);
}
break;
}
case MATCH_TEACHER:
long rowId = database.insert(DatabaseHelper.TABLE_TEACHER, null, values);
if (rowId > 0) {
return ContentUris.withAppendedId(CONTENT_URI_TEACHER, rowId);
}
break;
}
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
SQLiteDatabase database = mDataBaseHelper.getWritableDatabase();
int count = 0;
switch (uriMatcher.match(uri)) {
case MATCH_STUDENT:
count = database.delete(DatabaseHelper.TABLE_STUDENT, selection, selectionArgs);
break;
case MATCH_TEACHER:
count = database.delete(DatabaseHelper.TABLE_STUDENT, selection, selectionArgs);
break;
}
return count;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
SQLiteDatabase database = mDataBaseHelper.getWritableDatabase();
int count = 0;
switch (uriMatcher.match(uri)) {
case MATCH_STUDENT:
count = database.update(DatabaseHelper.TABLE_STUDENT, values, selection, selectionArgs);
break;
case MATCH_TEACHER:
count = database.update(DatabaseHelper.TABLE_STUDENT, values, selection, selectionArgs);
break;
}
if (getContext() != null) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
}
清单文件配置
<provider
android:authorities="com.example.app.StudentContentProvider"
android:name=".StudentContentProvider"
android:exported="true" />
外部调用
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
//注意:这里的Uri不能指定特定记录
public static final Uri URI_STUDENT = Uri.parse("content://com.example.app.StudentContentProvider/student");
public static final Uri URI_TEACHER = Uri.parse("content://com.example.app.StudentContentProvider/teacher");
private ContentResolver contentResolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contentResolver = getContentResolver();
contentResolver.registerContentObserver(URI_STUDENT, false, new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
}
});
}
public void insert(View view) {
ContentValues values = new ContentValues();
values.put("name", "张三");
Uri uri = contentResolver.insert(URI_STUDENT, values);
Log.d(TAG, "insert: " + uri);
}
public void delete(View view) {
String where = "_id=2";
contentResolver.delete(URI_STUDENT, where, null);
}
public void update(View view) {
ContentValues contentValues = new ContentValues();
contentValues.put("name", "李四");
contentResolver.update(URI_STUDENT, contentValues, "_id=1", null);
}
public void query(View view) {
Cursor cursor = contentResolver.query(URI_STUDENT, null, null, null, null);
if (cursor != null && cursor.getCount() > 0) {
while (!cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID));
String name = cursor.getString(cursor.getColumnIndex("name"));
Log.d(TAG, "query: " + id + "--" + name);
}
cursor.close();
}
}
}
其他问题
如果我们想在其他应用中访问这个ContentProvider,则ContentProvider在清单文件中配置时exported属性必须为true,那么如果我们设置为了true,其他应用都可以访问,岂不是有了安全隐患,这时候可以通过增加权限,下面看怎么做
在ContentProvider所在应用的清单文件中使用android:permission配置权限
<provider
android:authorities="com.example.client.CustomerProvider"
android:name="com.example.client.CustomerProvider"
android:exported="true"
android:permission="com.example.client"
/>
然后在和application相同节点使用permission标签定义一个权限
<permission android:name="com.example.client"
android:protectionLevel="normal"
android:label="CustomerProvider"
/>
其中protectionLevel指定权限的级别,具体区别可以看下面的连接
android自定义permission android:protectionLevel说明
这样,其他应用要是想访问我们的ContentProvider,则必须声明一个权限,如下
<uses-permission android:name="com.example.client"/>