今天来看看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; } }
清单文件配置
外部调用
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配置权限
然后在和application相同节点使用permission标签定义一个权限
其中protectionLevel指定权限的级别,具体区别可以看下面的连接
android自定义permission android:protectionLevel说明
这样,其他应用要是想访问我们的ContentProvider,则必须声明一个权限,如下