(1)概述
这里来模拟应用是如何访问ContentProvider,所以此处的ContentProvider也是自定义的,后通过ContentResolver来访问,具体源码可访问如下:
(2)部分源码
(A)ContentProvider
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.provider">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<permission
android:name="com.demo.contentProvider"
android:label="provider permission"
android:protectionLevel="normal" />
<permission
android:name="com.demo.contentProvider.read"
android:label="provider read permission"
android:protectionLevel="normal" />
<permission
android:name="com.demo.contentProvider.write"
android:label="provider write permission"
android:protectionLevel="normal" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Provider">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name=".PersonContentProvider"
android:authorities="com.example.provider"
android:exported="true"
android:enabled="true"
android:grantUriPermissions="true"
android:permission="com.demo.contentProvider"
android:readPermission="com.demo.contentProvider.read"
android:writePermission="com.demo.contentProvider.write" />
</application>
</manifest>
package com.example.provider;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File;
public class PersonContentProvider extends ContentProvider {
private static final UriMatcher sUriMatcher;
private static final int MATCH_FIRST = 1;
private static final int MATCH_SECOND = 2;
public static final String AUTHORITY = "com.example.provider";
public static final Uri CONTENT_URI_FIRST = Uri.parse("content://" + AUTHORITY +
File.separator + DatabaseHelper.TABLE_FIRST_NAME);
public static final Uri CONTENT_URI_SECOND = Uri.parse("content://" + AUTHORITY +
File.separator + DatabaseHelper.TABLE_SECOND_NAME);
public static final String CONTENT_FIRST_TYPE = "vnd.android.cursor.dir/demo.first";
public static final String CONTENT_SECOND_TYPE = "vnd.android.cursor.item/demo.second";
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, DatabaseHelper.TABLE_FIRST_NAME, MATCH_FIRST);
sUriMatcher.addURI(AUTHORITY, DatabaseHelper.TABLE_SECOND_NAME, MATCH_SECOND);
}
private DatabaseHelper mDbHelper;
@Override
public boolean onCreate() {
mDbHelper = new DatabaseHelper(getContext());
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
switch (sUriMatcher.match(uri)) {
case MATCH_FIRST:
queryBuilder.setTables(DatabaseHelper.TABLE_FIRST_NAME);
break;
case MATCH_SECOND:
queryBuilder.setTables(DatabaseHelper.TABLE_SECOND_NAME);
break;
default:
throw new IllegalArgumentException("Unknow URI: " + uri);
}
SQLiteDatabase db = mDbHelper.getReadableDatabase();
Cursor cursor = queryBuilder.query(db, strings, s, strings1, null, null, null);
return cursor;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (sUriMatcher.match(uri)) {
case MATCH_FIRST: {
return CONTENT_FIRST_TYPE;
}
case MATCH_SECOND: {
return CONTENT_SECOND_TYPE;
}
}
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
Uri retUri = null;
long rowID = 0;
switch (sUriMatcher.match(uri)) {
case MATCH_FIRST:
rowID = db.insert(DatabaseHelper.TABLE_FIRST_NAME, null, contentValues);
if (rowID > 0) {
retUri = ContentUris.withAppendedId(uri, rowID);
}
break;
case MATCH_SECOND:
rowID = db.insert(DatabaseHelper.TABLE_SECOND_NAME, null, contentValues);
if (rowID > 0) {
retUri = ContentUris.withAppendedId(uri, rowID);
}
break;
default:
throw new IllegalArgumentException("Unknow URI: " + uri);
}
return retUri;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
int count = 0;
switch (sUriMatcher.match(uri)) {
case MATCH_FIRST:
count = db.delete(DatabaseHelper.TABLE_FIRST_NAME, s, strings);
break;
case MATCH_SECOND:
count = db.delete(DatabaseHelper.TABLE_SECOND_NAME, s, strings);
break;
default:
throw new IllegalArgumentException("Unknow URI :" + uri);
}
this.getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
int count = 0;
switch (sUriMatcher.match(uri)) {
case MATCH_FIRST:
count = db.update(DatabaseHelper.TABLE_FIRST_NAME, contentValues, s, strings);
break;
case MATCH_SECOND:
count = db.update(DatabaseHelper.TABLE_SECOND_NAME, contentValues, s, strings);
break;
default:
throw new IllegalArgumentException("Unknow URI : " + uri);
}
this.getContext().getContentResolver().notifyChange(uri, null);
return count;
}
}
(B)ContentResolver
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.resolver">
<queries>
<provider android:authorities="com.example.provider"/>
</queries>
<uses-permission android:name="com.demo.contentProvider.read" />
<uses-permission android:name="com.demo.contentProvider.write" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Resolver">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.second"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.item/demo.second" />
</intent-filter>
</activity>
</application>
</manifest>
package com.example.resolver;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TABLE_FIRST_NAME = "first";
private static final String TABLE_SECOND_NAME = "second";
public static final String AUTHORITY = "com.example.provider";
public static final Uri CONTENT_URI_FIRST = Uri.parse("content://" + AUTHORITY + File.separator + TABLE_FIRST_NAME);
public static final Uri CONTENT_URI_SECOND = Uri.parse("content://" + AUTHORITY + File.separator + TABLE_SECOND_NAME);
public Uri mCurrentURI = getUri(TABLE_FIRST_NAME);
private DataBaseObserver mObserver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mObserver = new DataBaseObserver(new Handler());
getContentResolver().registerContentObserver(getUri(null), true, mObserver);
}
@Override
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(mObserver);
}
public class DataBaseObserver extends ContentObserver {
public DataBaseObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.e("MainActivity", "selfChange = " + selfChange);
}
}
private Uri getUri(String path) {
return new Uri.Builder().authority(AUTHORITY)
.path(path)
.scheme("content")
.build();
}
private void secondPart() {
Intent intent = new Intent();
intent.setAction("com.example.second");
intent.setData(mCurrentURI);
try {
startActivity(intent);
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
private void insert() {
ContentValues values = new ContentValues();
values.put("name", "hello");
values.put("detail", "my name is demo");
Uri uri = this.getContentResolver().insert(mCurrentURI, values);
Log.e("MainActivity", "insert : " + uri.toString());
}
private void query() {
Cursor cursor = this.getContentResolver().query(mCurrentURI, null, null, null, null);
Log.e("MainActivity", "query : count = " + cursor.getCount());
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
String table = cursor.getString(cursor.getColumnIndex("table_name"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String detail = cursor.getString(cursor.getColumnIndex("detail"));
Log.e("MainActivity", "query : table_name = " + table + ", name = " + name + ", detail = " + detail);
cursor.moveToNext();
}
cursor.close();
}
private void update() {
ContentValues values = new ContentValues();
values.put("detail", "my name is update");
int count = this.getContentResolver().update(mCurrentURI, values, "_id = 1", null);
Log.e("MainActivity", "update : count = " + count);
query();
}
private void delete() {
int count = this.getContentResolver().delete(mCurrentURI, "_id = 1", null);
Log.e("MainActivity", "delete : count = " + count);
query();
}
private void switchURI(Uri uri) {
mCurrentURI = uri;
mtv.setText("switch URI = " + mCurrentURI.toString());
}
}
(3)注意事项
(A)Android R平台,将软件的TargetApi提升到30后,原来用的好好的自定义的ContentProvider找不到了,或者直接报 Unknow Uri 问题。
//需要在使用Uri的客户端的AndroidManifest.xml下加上标签
<queries>
<provider android:authorities="com.example.provider"/>
</queries>
否则在进行registerContentObserver会出现如下Error:
getContentResolver().registerContentObserver(getUri(null), true, mObserver);
//error
Failed to find provider com.demo.provider.PersonContentProvider for user 0
(B)Provider权限问题
ContentProvider:
<permission
android:name="com.demo.contentProvider"
android:label="provider permission"
android:protectionLevel="normal" />
<permission
android:name="com.demo.contentProvider.read"
android:label="provider read permission"
android:protectionLevel="normal" />
<permission
android:name="com.demo.contentProvider.write"
android:label="provider write permission"
android:protectionLevel="normal" />
<provider
android:name=".PersonContentProvider"
android:authorities="com.example.provider"
android:exported="true"
android:enabled="true"
android:grantUriPermissions="true"
android:permission="com.demo.contentProvider"
android:readPermission="com.demo.contentProvider.read"
android:writePermission="com.demo.contentProvider.write" />
ContentResolver:
<uses-permission android:name="com.demo.contentProvider.read" />
<uses-permission android:name="com.demo.contentProvider.write" />
- exported:这个属性用于指示该服务是否能被其他程序应用组件调用或跟他交互; 取值为(true | false),如果设置成true,则能够被调用或交互,否则不能;
- readPermission:使用Content Provider的查询功能所必需的权限,即使用ContentProvider里的query()函数的权限;
- writePermission:使用ContentProvider的修改功能所必须的权限,即使用ContentProvider的insert()、update()、delete()函数的权限;
- permission:客户端读、写 Content Provider 中的数据所必需的权限名称,readPermission和writePermission属性优先于本设置;