内容提供器
- 主要用于在不同的应用程序之间实现数据共享的功能
- 可以选择只对哪一部分数据进行共享
- 使用现有的内容提供器来读取和操作相应程序中的数据
- 或创建自己的内容提供器给我们程序的数据提供外部访问接口
运行时权限
机制
- 普通权限系统自动进行授权
- 危险权限(权限组名 | 权限名)
- CALENDAR: READ_CALENDAR / WRITE_CALENDAR
- CAMERA: CAMERA
- CONTACTS: READ_CONTACTS / WRITE_CONTACTS / GET_CONTACTS
- 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 / REACEIVE_MMS
- STORAGE: READ_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGE
- 同意授权某个危险权限后,该权限对应的权限组中所有的其他权限也会同时被授权
运行时申请权限
- 在AndroidManifest.xml文件中声明
<uses-permission android:name="android.permission.CALL_PHONE" />
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button makeCall = findViewById(R.id.make_call);
makeCall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ContextCompat.checkSelfPermission((MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[] { Manifest.permission.CALL_PHONE }, 1);
} else {
call();
}
}
});
}
private void call() {
try {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
} catch (SecurityException e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
call();
} else {
Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
ContextCompat.checkSelfPermission:先判断用户是否已经授权
第一个参数是Context
第二个参数是具体的权限名
用返回值和PackageManager.PERMISSION_GRANTED比较,相等则已经授权,不等则没有授权
- 如果没有授权,则调用
ActivityCompat.requestPermissions()向用户申请授权
第一个参数要求Acitivity的实例
第二个参数是一个String数组,把要申请的权限名放在数组中
第三个参数是请求码,只要是唯一值即可
- 调用完
ActivityCompat.requestPermissions(),系统会弹出一个权限申请的对话框
用户选择同意或拒绝后,回调到onRequestPermissionResult方法中
授权的结果封装在grantResults参数中
- 拨打电话
- 构建一个隐式Intent
action指定为Intent.ACTION_CALL,是系统内置的打电话动作
data部分指定了协议是tel,号码是10086
- Intent.ACTION_DIAL表示打开拨号界面,不需要声明权限
Intent.ACTION_CALL直接拨打电话,必须声明权限
访问其他程序中的数据
ContentResolver的基础用法
- 通过Context中的
getContentResolver()方法获取该类的实例
- 提供了一系列方法用于对数据进行CRUD操作
insert() update() delete() query()
- 内容URI:给内容提供器中的数据建立了唯一标识符
authority:区分不同的应用程序,一般采用<package_name>.provider的方式命名
path:对同一应用程序中不同的表做区分,通常添加到authority后面
- 在字符串头部加上协议声明
- E.g.
content://com.example.app.provider/table1
- 解析为
Uri对象
Uri uri = Uri.parse(content://com.example.app.provider/table1);
- 查询
query():
Cursor cursor = getContentResolver().query(
uri,
projection,
selection,
selectionArgs,
sortOrder);
uri :指定查询某个应用程序下的某张表
projection:指定查询的列名
selection:指定where的约束条件
selectionArgs:为where中的占位符提供具体的值
sortOrder:指定查询结果的排序方式
if (cursor != null) {
while (cursor.moveToNext()) {
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cusor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close()
}
- 添加
insert()
ContentValue values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);
getContentResolver().insert(uri, values);
- 修改
update()
ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text", "1"});
- 删除
delete()
getContentResolver().delete(uri, "column2 = ?", new String[] { "1" });
创建自己的内容提供器
创建步骤
- 新建一个类继承
ContentProvider
重写6个抽象方法
boolean onCreate()
- 初始化内容提供器时调用,通常会完成对数据库的创建和升级等操作
- 只有当存在ContentResolver尝试访问程序中的数据时才会被初始化
Cursor query(Uri uri, String[] projection, String selection, String[] SelectionArgs, String sortOrder)
Uri insert(Uri uri, ContentValues values)
int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
int delete(Uri uri, String selection, String[] selectionArgs)
String getType(Uri uri)
- 返回相应的
MIME类型
MIME字符串主要由3部分组成
- 必须以
vnd开头
- 如果内容URI以路径结尾,则后接
android.cursor.dir/
如果内容URI以id结尾,则后接android.cursor.item/
- 最后接上
vnd.<authority>.<path>
- E.g.
vnd.android.cursor.dir/vnd.com.example.app.provide.table1
内容URI
- 给内容提供器中的数据建立了唯一标识符
authority:区分不同的应用程序,一般采用<package_name>.provider的方式命名
path:对同一应用程序中不同的表做区分,通常添加到authority后面
- 在字符串头部加上协议声明
- 可以在内容URI后面加上id
content://com.example.app.provider/table1/1
- 表示想访问com.example.app这个应用的table1表中id为1的数据
- 可以使用通配符匹配
*表示匹配任意长度的任意字符
#表示匹配任意长度的数字
- E.g.
content://com.example.app.provider/*匹配任意表的内容
content://com.example.app.provider/table1/#匹配table1表中任意一行数据
UriMatcher类
- 提供
addURI()方法
接收3个参数,可以分别传入authority、path和一个子弟能够以代码
- 调用
match()方法是可以将一个Uri对象传入,返回某个能匹配这个Uri对象所对应的自定义代码
通过这个代码判断需要访问哪张表中的数据
- 所有的CRUD操作都一定要匹配到相应内容的URI格式才能进行,防止隐私数据泄露
public class MyProvider extends ContentProvider {
public static final int TABLE1_DIR = 0;
public static final int TABLE1_ITEM = 1;
public static final int TABLE2_DIR = 2;
public static final int TABLE2_ITEM = 3;
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider", "table1/#", TABLE1_ITEM);
uriMatcher.addURI("com.example.app.provider", "table2", TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider", "table2/#", TABLE2_ITEM);
}
...
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
break;
case TABLE1_ITEM:
break;
case TABLE2_DIR:
break;
case TABLE2_ITEM:
break;
default:
break;
}
...
}
@Override
public String getType(Uri uri) {
switch (uriMathcer.match(uri)) {
case TABLE1_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provide.table1";
case TABLE1_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provide.table1";
case TABLE2_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provide.table2";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provide.table2";
default:
break;
}
return null;
}
...
}