ContentProvider 作为四大组件之一,讲道理工作中应该经常用到才对,但是做了三年android 开发却没怎么用,在之前启动相机拍照的时候有用到FileProvider 去获取图片(android7.0之后获取图片文件的权限问题)。
ContentProvider 的使用 实现原理底层也是binder
1.继承ContentProvider 实现方法,需要注意的是这几个方法返回的都是Cursor 对象,另外它的方法都是在binder 线程池中执行的需要注意数据同步的问题,以及数据变更的时候需要通知 observer
2.manifest 注册
1)AUTHORYTY 包名+名称(用来识别这个provider)
2)exported 表示别的程序是否能够访问这个provider
3)permission (可以加上自定义的读写权限)
3.使用的时候
1.注册数据更新的观察者 ContentObserver
2.getContentResolver.query(),insert,update....
3.操作的时候是异步的会挂起当前线程属于耗时操作,不要在主线程直接调用
4.Uri uri = Uri.parse("content://"+AUTHORITY+"PATH");
自定义ContentProvider的简单实现,只实现了一个查询的方法,将集合封装成cursor 返回,参照FileProvider
public class BookProvider extends ContentProvider {
/**
* ContentProvider
* 注意:onCreate 是在主线程,query update insert delete
* 都是在Binder 线程池 需要注意数据同步
* 在数据更新之后可以通过contentProviderObserver更新
*/
List<Book> books = new CopyOnWriteArrayList<>();//线程安全的集合
private static final String AUTHORITY = "com.example.contentproviderdemo.BookProvider";
private static final int BOOK_CODE = 0;
private static final UriMatcher uriMatcher;
private static final String TAG = BookProvider.class.getSimpleName();
private static final String[] COLUMS = {"bookName", "bookId"};
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "BOOK", BOOK_CODE);
}
@Override
public boolean onCreate() {
initBookList();
return false;
}
@androidx.annotation.Nullable
@Override
public Cursor query(@androidx.annotation.NonNull Uri uri, @androidx.annotation.Nullable String[] projection, @androidx.annotation.Nullable String selection, @androidx.annotation.Nullable String[] selectionArgs, @androidx.annotation.Nullable String sortOrder) {
if (uriMatcher.match(uri) == BOOK_CODE) {
//将集合封装成cursor 返回
MatrixCursor cursor = new MatrixCursor(COLUMS, 1);
for (int i = 0; i < books.size(); i++) {
Book book = books.get(i);
cursor.addRow(new Object[]{book.getBookName(), book.getBookId()});
}
return cursor;
}
Log.i(TAG, "query: projection = " + Arrays.toString(projection) + "selection = " + selection);
return null;
}
@androidx.annotation.Nullable
@Override
public String getType(@androidx.annotation.NonNull Uri uri) {
Log.i(TAG, "getType: ");
return null;
}
@androidx.annotation.Nullable
@Override
public Uri insert(@androidx.annotation.NonNull Uri uri, @androidx.annotation.Nullable ContentValues values) {
Log.i(TAG, "insert: ");
Objects.requireNonNull(getContext()).getContentResolver().notifyChange(uri, null);
return null;
}
@Override
public int delete(@androidx.annotation.NonNull Uri uri, @androidx.annotation.Nullable String selection, @androidx.annotation.Nullable String[] selectionArgs) {
Log.i(TAG, "delete: ");
Objects.requireNonNull(getContext()).getContentResolver().notifyChange(uri, null);
return 0;
}
@Override
public int update(@androidx.annotation.NonNull Uri uri, @androidx.annotation.Nullable ContentValues values, @androidx.annotation.Nullable String selection, @androidx.annotation.Nullable String[] selectionArgs) {
Log.i(TAG, "update: ");
Objects.requireNonNull(getContext()).getContentResolver().notifyChange(uri, null);
return 0;
}
private void initBookList() {
books.add(new Book("java", 0));
books.add(new Book("sqlite", 1));
books.add(new Book("kotlin", 2));
books.add(new Book("c", 3));
books.add(new Book("数据结构与算法", 4));
}
}
注册manifest
<!--自定义权限-->
<permission android:name="providerReadPermission"/>
<permission android:name="providerWritePermission"/>
<provider
android:name=".BookProvider"
android:authorities="com.example.contentproviderdemo.BookProvider"
android:exported="true"
android:readPermission="providerReadPermission"
android:writePermission="providerWritePermission"
></provider>
使用
Uri uri = Uri.parse("content://" + AUTHORITY + "/BOOK");
/**第二参数表示路径后面的更改是否通知观察者*/
getContentResolver().registerContentObserver(uri, false, new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.i(TAG, "onChange: ");
}
});
/*耗时操作,查询的时候会挂起当前线程,不建议在主线程中使用*/
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
Log.i(TAG, "book: " + cursor.getColumnName(0) + "=" + cursor.getString(0));
Log.i(TAG, "book: " + cursor.getColumnName(1) + "=" + cursor.getString(1));
}
}
cursor.close();
}
输出结果
总结:
知识点1. uriMatcher.add()->UriMatcher.match() == match_code
2.Uri uri = uri.parse("content://"+AUTHORITY+PATH);
3.manifest注册 athoryty+permission
获取手机的联系人信息
private void requestPermission() {
if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_CONTACTS)) {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.READ_CONTACTS}, 10001);
} else {
readContacts();
}
}
private void readContacts() {
Cursor cursor1 = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cursor1 != null) {
while (cursor1.moveToNext()) {
Log.i(TAG, cursor1.getColumnName(0) + " = " + cursor1.getString(0));
Log.i(TAG, cursor1.getColumnName(1) + " = " + cursor1.getString(1));
}
cursor1.close();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 10001 && resultCode == RESULT_OK) {
readContacts();
}
}
只要知道uri 可以轻松访问ContentProvider 的信息->解析Cursor
FileProvider 的使用