内容提供器实现了不同程序之间实现数据共享的功能。
7.2 运行时权限
安卓6.0版本后引入了运行时权限
每个权限都属于一个组,授权了其中一个,一个组内的权限都将会被授权。
测试代码
// AndroidManifest.xml中加入以下代码
<uses-permission android:name="android.permission.CALL_PHONE"/>
// 主函数是下列代码
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button makeCall = (Button) findViewById(R.id.make_call);
makeCall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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 onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, 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 对比,相等说明授权了,不等相当于没授权。
授权的话直接执行打电话逻辑,没授权的话使用
requestPermissions()方法申请授权:第一个参数是Activity的实例,第二个是要申请的权限列表数组String、,第三个是请求码,唯一即可。
调用完之后都会回调到函数 onRequestPermissionsResult 中,授权的结果在 grantResults 参数中
7.3 访问其他程序中的数据
7.3.1 ContentResolver的基本用法
要想访问提供器中共享的数据,就一定要借助 ContentResolver 类,可以通过 Context 中的 getContentResolver()方法得到实例,其中提供了 update、delete、query、insert等方法用于CRUD操作。
和数据库操作不同,ContentResolver中增删改查不接收表明参数,但用Uri参数代替,这个参数被称为内容URI,由:authority和path组成。authority是用于区分不同应用程序的,一般都是报名加provider。path 是用于对同一应用程序的不同表做区分的,一般都会加在 authority 的后面,组合后的URI示例:com.example.app.provider/label1 还需要在前面加上协议:content://com.example.app.provider/label1。URI需要解析后才可以使用:
Uri uri = uri.parse("content://com.example.app.provider/label1")
,下面就可以使用Uri对象来查询table表中的数据了:Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
之后可以逐个从返回的Cursor对象中都出来了:
if(cursor != null){
while ( cursor.moveToNest() ){
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
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"});
7.3.2 读取系统联系人
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button makeCall = (Button) findViewById(R.id.make_call);
makeCall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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 onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, 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:
}
}
}
布局代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/make_call"
android:text="Make Call"/>
</LinearLayout>
还有一条权限语句:
<uses-permission android:name="android.permission.CALL_PHONE"/>
7.4 创建自己的内容提供器
7.4.1 创建内容提供器的步骤
要想自己定制,就需要实现一个java类并重载以下方法:
public class MyProvider extends ContentProvider {
@Override
// 创建函数,在这里通常实现数据库升级和创建操作
public boolean onCreate(){
return false;
}
@Override
// 查询结果在Cursor对象中返回
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder){
return null;
}
@Override
// 添加完成后返回一个用于表示新纪录的URI
public Uri insert(Uri uri, ContentValues values){
return null;
}
@Override
// 返回受影响的行数
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs){
return 0;
}
@Override
// 返回受影响的行数
public int delete(Uri uri, String selection, String[] selectionArgs){
return 0;
}
@Override
// 根据传入的内容URI 返回相应的MIME类型
public String getType(Uri uri){
return null;
}
}
URI后面可以加id,表示要找table1表中id为1的数据
content://com.example.app.provider/table/1
在URI中也可以使用通配符,如:
content://com.example.app.provider/*
,匹配任意表,
content://com.example.app.provider/table/#
,匹配table表中任意一行。
使用 UriMatcher 可以匹配内容URI,使用方法如下:
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", TABLE2_DIR);
uriMatcher.addURI("com.example.app.provider", "table2/#", TABLE2_ITEM);
}
@Override
public boolean onCreate(){
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder){
switch (uriMatcher.match(uri)){
case TABLE1_DIR:
// 查询table1中数据
break;
case TABLE1_ITEM:
// 查询单条数据
break;
default:
break;
}
return null;
}
感觉主要是实现了个指令匹配的过程
getType函数的 MIME 类型需要介绍下,一个URI对应的MIME字符串主要有三部分:
- 必须以vnd开头
- 如果内容URI以路径结尾,则后跟 android.cursor.dir/ , 如果以id结尾,则跟 android.cursor.item/ 。
- 最后接上 vnd.<authority>.<path>
则 content://com.example.app.provider/tabel
这个内容 URI 对应的 MIME 可以写成
vnd.android.cursor.dir/vnd.com.example.app.provider.table
content://com.example.app.provider/tabel1/1
对应于vnd.android.cursor.item/vnd.com.example.app.provider.table1