ContentProvider主要负责存储和共享数据,底层是由Binder实现的,所以能够跨进程通信,在不同的应用程序之间进行数据共享,它还可以选择对哪一部分数据进行共享,从而保证程序中的隐私数据不会有泄露风险。
ContentProvider有两种形式:可以使用现有的内容提供者来操作和读取相应程序中的数据,也可以创建自己的内容提供者给这个程序的数据提供外部访问接口。
- 访问系统ContentProvider
- 自定义ContentProvider
一.访问系统ContentProvider
实际上ContentProvider是对SQLiteOpenHelper的进一步封装,所以可以通过ContentProvider对应用中的数据进行增删改查操作,其它应用是通过ContentResolver来访问ContentProvider提供的数据,而ContentResolver是通过uri来定位自己要访问的数据。
1.URI
uri(Uniform Resource Identifier)即统一资源标识符,是用于标识某一互联网资源名称的字符串,以联系人Contacts的Uri为例:
schema:协议申明,形式为content://
authority:权限,ContentProvider的唯一标识
path:数据表的表名
id:数据表中数据的标识,可选字段
2.ContentResolver
ContentResolver类中提供了一系列对数据增删改查操作的方法,类似于数据库操作,insert()插入数据,update()更新数据,delete()删除数据,query()查询数据,下面以访问系统通讯录为例:
(1)添加权限,Android6.0之上要动态获取权限。
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
int contactsPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS);
if (contactsPermission != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},REQUEST_CODE_PERMISSIONS);
}
}
(2)查询系统联系人,获取联系人名称和所有的电话号码。
Uri uri = ContactsContract.Contacts.CONTENT_URI;
Cursor cursor = getContentResolver().query(uri, null, null, null, null);//查询系统联系人
if (cursor != null) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));//联系人名称
int id = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID));//联系人唯一标识id
Cursor cursor1 = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER},
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + id, null, null);//通过id查询联系人所有号码
if (cursor1 != null) {
while (cursor1.moveToNext()) {
String phone = cursor1.getString(cursor1.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Log.d(TAG,name+":"+phone);//打印联系人名称和电话号码
}
}
cursor1.close();
}
}
cursor.close();
}
二.自定义ContentProvider
ContentProvider的使用可以分为以下几个步骤:
- 创建自己的数据列表
- 自定义ContentProvider,实现相关的抽象方法
- 在配置文件中注册以及定义相关的访问权限
- 通过ContentResolver根据uri进行增删改查操作
- ContentObserver监听ContentProvider的变化
1.创建数据列表
public class DBOpenHelper extends SQLiteOpenHelper {
public final static String DATABASE_NAME = "com.czy.contentproviderdemo.provider";
public final static String DATABASE_STUDENT_TABLE_NAME = "student";
private final static int DATABASE_VERSION = 1;
private Context mContext;
private final static String CREATE_STUDENT_TABLE = "CREATE TABLE IF NOT EXISTS "
+ DATABASE_STUDENT_TABLE_NAME
+ "(id INTEGER PRIMARY KEY,"
+ "name TEXT VARCHAR(20) NOT NULL,"
+ "gender BIT DEFAULT(1),"
+ "number TEXT VARCHAR(12) NOT NULL,"
+ "score INTEGER CHECK(score >= 0 and score <= 100))";
public DBOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
}
public DBOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, DATABASE_NAME, factory, DATABASE_VERSION);
mContext = context;
}
public DBOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version, @Nullable DatabaseErrorHandler errorHandler) {
super(context, DATABASE_NAME, factory, DATABASE_VERSION, errorHandler);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_STUDENT_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2.自定义ContentProvider
public class StudentContentProvider extends ContentProvider {
private final static String AUTHORITY = "com.czy.contentproviderdemo.provider";
private final static int STUDENT_URI_CODE = 0;
/*
UriMatcher类是帮助匹配ContentProvider中的uri
它有两个方法addURI()和match()
addURI()方法用来把uri和uri_code相关联
match()方法能够根据传递的uri匹配到相应的uri_code
* */
private final static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY,"student",STUDENT_URI_CODE);
}
private Context mContext;
private SQLiteDatabase mDataBase;
@Override
public boolean onCreate() {
mContext = getContext();
mDataBase = new DBOpenHelper(mContext).getWritableDatabase();
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
int uriType = uriMatcher.match(uri);
Cursor cursor = null;
switch (uriType) {
case STUDENT_URI_CODE:
cursor = mDataBase.query(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder, null);
break;
default:
new IllegalArgumentException("unSupport Uri :" +uri);
break;
}
return cursor;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {//获得数据的MIME类型
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
int uriType = uriMatcher.match(uri);
long row = 0;
switch (uriType) {
case STUDENT_URI_CODE:
row = mDataBase.insert(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME, null, values);
break;
default:
new IllegalArgumentException("unSupport Uri :" +uri);
break;
}
if (row > 0) {
mContext.getContentResolver().notifyChange(uri,null);
}
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
int uriType = uriMatcher.match(uri);
int rowDelete = 0;
switch (uriType) {
case STUDENT_URI_CODE:
rowDelete = mDataBase.delete(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME, selection, selectionArgs);
default:
new IllegalArgumentException("unSupport Uri :" +uri);
break;
}
if (rowDelete > 0) {
mContext.getContentResolver().notifyChange(uri,null);
}
return rowDelete;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
int uriType = uriMatcher.match(uri);
int rowUpdate = 0;
switch (uriType) {
case STUDENT_URI_CODE:
rowUpdate = mDataBase.update(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME, values,selection, selectionArgs);
default:
new IllegalArgumentException("unSupport Uri :" +uri);
break;
}
if (rowUpdate > 0) {
mContext.getContentResolver().notifyChange(uri,null);
}
return rowUpdate;
}
}
注意:
(1)onCreate()方法由系统回调运行在主线程中,其它五个方法由外界回调,运行在binder线程池中,因此在onCreate()方法中不能做耗时操作。
(2)增删改查操作存在多线程并发问题,因此在方法的内部要做好线程的同步。
(3)insert方法的返回值是插入数据所在的行号,update和delete方法的返回值代表是此次操作影响到的行数。
3.配置文件注册
<provider
android:authorities="com.czy.contentproviderdemo.provider"
android:name="com.czy.contentproviderdemo.StudentContentProvider"
android:process=":provider"
android:exported="true"/>
authorities:provider的权限,形式是包名.provider
name:provider全名,形式是包名.类名
process:开启一个新进程
exported:是否可以被其它程序访问
4.ContentResolver的增删改查操作
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity==";
private final static String AUTHORITY = "com.czy.contentproviderdemo.provider";
private final static Uri STUDENT_URI = Uri.parse("content://" + AUTHORITY + "/student");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//删除数据
public void deleteValue(View view) {
getContentResolver().delete(STUDENT_URI, "name = ?", new String[]{"update"});
}
//更新数据
public void updateValue(View view) {
ContentValues contentValues = new ContentValues();
contentValues.put("id", 0);
contentValues.put("name", "update");
contentValues.put("gender", 1);
contentValues.put("number", "201804111048");
contentValues.put("score", "90");
getContentResolver().update(STUDENT_URI, contentValues, "id = ?", new String[]{"0"});
}
//查询数据
public void queryValue(View view) {
Cursor cursor = getContentResolver().query(STUDENT_URI, new String[]{"id", "name", "gender", "number", "score"}, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
Student student = new Student();
student.id = cursor.getInt(cursor.getColumnIndex("id"));
student.name = cursor.getString(cursor.getColumnIndex("name"));
student.gender = cursor.getInt(cursor.getColumnIndex("gender"));
student.number = cursor.getString(cursor.getColumnIndex("number"));
student.score = cursor.getInt(cursor.getColumnIndex("score"));
Log.d(TAG, "student = " + student.toString());
}
}
cursor.close();
}
//插入数据
public void insertValue(View view) {
ContentValues contentValues = new ContentValues();
contentValues.put("id", 0);
contentValues.put("name", "peter");
contentValues.put("gender", 0);
contentValues.put("number", "201804081705");
contentValues.put("score", "100");
getContentResolver().insert(STUDENT_URI, contentValues);
}
//student数据封装类
public class Student {
private final static String TAG = "Student";
public Integer id;
public String name;
public Integer gender;
public String number;
public Integer score;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gender=" + gender +
", number='" + number + '\'' +
", score=" + score +
'}';
}
}
}
5.ContentObserver监听ContentProvider变化
observerHandler = new ObserverHandler();
contentObserver = new ContentObserver(observerHandler){
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
}
};
getContentResolver().registerContentObserver(STUDENT_URI,true, contentObserver);//注册监听
private class ObserverHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(contentObserver);//注销监听
}