1 内容提供器简介
Content Provider 主要用于在不同应用程序间实现数据共享。
不同于文件存储和SharedPreferences存储中的两种全局可读写操作模式,Content Provider可以选择只对那一部分数据进行共享,从而保证我们的程序中的隐私数据不会由泄露的风险。
2 运行时权限
2.1 权限分类
危险权限9组24项需要进行运行时权限处理。
权限组名 | 权限名 |
CALENDAR | READ_CALENAR WRITE_CALENDAR |
CAMERA | CAMERA |
CONTACTS | READ_CONTACETS WRITE_CONTACTS GET_ACCOUNTS |
LOCATION | ACCESS_FINE_LOCATION ACCESS_COARSE_LOCATION |
MICROPHONE | RECORD_AUDIO |
PHONE | READ_PHONE_STATE CALL_PHONE READ_CALL_LOG WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS |
SENSORS | BODY_SENSORS |
SMS | SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS |
STORAGE | READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE |
普通权限,只需要在AndroidManifest.xml文件中添加权限声明就可以了。
2.2 在程序运行时申请权限
举例,申请打电话权限 Manifest.permission.CALL_PHONE
1.先进行判断是否已经获得权限,有则直接打电话,无则申请权限
ContextCompat.checkSelfPermission(MainActivity.this, Manifest.
permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED
2.申请权限
ActivityCompat.requestPermissions(MainActivity.this, new
String[] {Manifest.permission.CALL_PHONE}, 1)
3.申请权限后,系统会弹出一个权限申请框,不管如何操作,最终会回调
onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResult)
4.授权结果会封装在grantResult中,做最后判断是否授权成功。
int[0] grantResult == PackageManager.PERMISSION_GRANTED
3 访问其他程序中的数据
Content Provider的用法由两种:
- 1.使用现有的Content Provider来读取和操作相应程序中的数据;
- 2.创建自己的Content Provider给我们程序的数据提供外部访问接口。
3.1 ContentResolver的基本用法
通过Context 中的getCOntentResolver()方法获得ContentResolver类的实例。
提供了方法用于对数据进行CRUD操作。(增加(Create)、读取(Read)、更新(Update)和删除(Delete))
- insert()方法用于添加数据
- update()方法用于更新数据
- delete()方法用于删除数据
- query()方法用于查询数据
这些方法使用Uri参数,即内容URI,给Content Provider中的数据建立唯一标识符,两部分组成:authority和path。
content头部协议声明
authority是用于对不同的应用程序做区分,一般使用程序包名来命名。
path则是用于对统一应用中不同的表做区分,通常会添加在authority后面。
标准写法例子
content://com.example.app.provider/table1
内容URI字符串解析成Uri对象后,作为参数传入。
Uri uri = Uri.parse("content://com.example.app.provider/table1")
查询table1表数据,代码
Cursor cursor = getContentResolver().query(
uri,//指定查询某个应用程序下的某一张表
projection,//指定查询的列名
selection,//指定where的约束条件
selectionArgs,//为where的占位符提供具体符
sortOrder);//指定查询结果的排序方式
将数据从Cursor对象中逐个读取处理。通过移动游标来遍历Cursor所有行,在取出每一行相应列的数据。
if (cursor != null && cursor.moveToFirst()) {
do {
Stirng colum1 = cursor.getString(cursor.getColumnIndex("column1"));//根据 name的名称获得它的列索引
int colum2 = cursor.getInt(cursor.getColumnIndex("column2"));
}while(cursor.moveToNext());
cursor.close();
}
添加数据,先将待添加的数据组装子啊ContentValues 中,再调用ContentResolver的insert()方法。
ContentValues values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);
getContentResolver().insert(uri, values);
更新这条新添加的数据,把column1的值清空
ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?",
new String[] {"text", "1"});
删除数据
getContentResolver().delete(uri, "column2 = ?", new Stirng[] { "1" }
3.2 读取系统联系人
判断权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new
String[] {Manifest.permission.READ_CONTACTS}, 2);
} else {
readContacts();
}
回调返回值
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
case 2:
if (grantResults.length > 0 && grantResults[0] ==
PackageManager.PERMISSION_GRANTED) {
readContacts();
} else {
Toast.makeText(this, "You denied the permssion",
Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
读取联系人
private void readContacts() {
Cursor cursor = null;
try {
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.
Phone.CONTENT_URI, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String displayName = cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactsList.add(displayName + "\n" + number);
}
adapter.notifyDataSetChanged();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
AndroidManifest.xml声明权限
<uses-permission android:name="android.permission.READ_CONTACTS"/>
4 创建自己的内容提供器
4.1 创建内容提供器步骤
1.onCreate()
初始化ContentProvider时调用,完成对数据库的创建和升级。
只有当存在ContentResolver尝试访问我们程序中的数据时,ContentProvider才会被初始化。
2.query()
从ContentProvider中查询数据。查询结果存放在Cursor对象中返回。
- Uri参数:确定查询哪一张表;
- projection参数:确定查询哪些列;
- selection和selectionArgs参数:用于约束查询哪些行;
- sortOrder参数:用于对结果进行排序。
3.insert()
向ContentProvider添加一条数据。使用uri参数确定要添加到的表,待添加的数据保持在values参数中。添加完成后,返回一个用于表示这条新纪录的URI。
4.update()
更新ContentProvider已有的数据。使用uri参数确定要更新到的表,新数据保持在values参数中,selection和selectionArgs参数用于约束更新哪些行,受影响的行数将作为返回值返回。
5.delete()
从ContentProvider中删除数据。使用uri参数确定要删除的表,selection和selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。
6.getType()
根据传入的内容URI来返回相应的MIME类型。
标准的内容URI写法扩展:
原本:
content://com.example.app.provider/table1
内容URI后面添加id
content://com.example.app.provider/table1/1
表示访问/com.example.app这个应用的table1表中id为1的数据
通配符:
- *:表示匹配任意长度的任意字符;
- #:表示匹配任意长度的数字。
使用UriMatcher类匹配内容URI
该类提供addURI()方法,接受三个参数,分别把authority、path和一个自定义代码传进去。
该类提供match()方法,传入一个Uri对象,返回值是某个能够匹配这个URI对象的自定义代码。通过这个代码,可以判断期望访问哪一个表。
使用该类可以判断使用哪一个表,然后再进行CRUD操作。
getType()方法,用于获取Uri对象所对应的MIME类型。一个内容URI所对应的MIME字符串主要三部分组成。
- 必须以vnd开头
- 如果内容URI以路径结尾,则vnd后面接android.cursor.dir/,如果以id结尾,则vnd后面接android.cursor.item/。
- 最后接上vnd..
tip:不使用”/",用"."代替。
例子:
内容URI:content://com.example.app.provider/table1
MIME:vnd.android.cursor.dir/vnd.com.example.app.provider.table1
内容URI:content://com.example.app.provider/table1/1
MIME:vnd.android.cursor.item/vnd.com.example.app.provider.table
tip:id没了
4.2 实现跨程序数据共享
<provider
android:name=".DatabaseProvider"
android:name="com.example.databasetest.provider"
android:enabled="true"
android:exported="true">
</provider>