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 的使用