Android第七章(内容提供器)

7.1 内容提供器简介
1、内容提供器主要用于在不同的应用程序之间实现数据共享的功能,他提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性
2、内容提供器可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险
7.2 运行时权限
7.2.1 Android权限机制详解
权限机制:用于保护用户设备的安全性
运行时权限:用户不需要在安装软件时一次性授权所有申请的权限,而是可以在软件的使用过程中再对某一项权限申请进行授权
运行时权限的核心:在程序运行过程中由用户授权我们去执行某些危险操作,程序是不可以擅自做主去执行这些危险操作的
Android将所有权限归为两类:①普通权限——系统自动帮我们授权;②危险权限——必须用户手动点击授权才行
危险权限种类:9组24个权限
在这里插入图片描述
7.2.2 在程序运行时申请权限
这里我们以申请打电话的运行时权限:
首先,写一个按钮控件:

  <Button
        android:id="@+id/make_call"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Make Call" />

接着修改MainActivity中的代码:
1、借助ContextCompat.checkSelfPermission()方法判断用户是不是已经给过我们授权
2、如果授权了就打电话
3、若没有授权,则调用ActivityCompat.requestPermissions()方法向用户申请授权
4、不论我们是选择同意或拒绝权限申请,最终都会回调到onRequestPermissionsResult()方法中

public class MainActivity extends AppCompatActivity {
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button)findViewById(R.id.make_call);
        button.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
            	//借助ContextCompat.checkSelfPermission()方法判断用户是不是已经给过我们授权,该方法传入的第二个参数就是比较权限是否授权
				if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE != PackageManager.PERMISSION_GRANTED)){
    
    
				//若没有授权,则调用ActivityCompat.requestPermissions()方法向用户申请授权,这里传入三个参数,第一个是Activity的实例,第二个是一个String数组,我们把要申请的权限名放在数组中即可,第三个参数是请求码
                    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, @NonNull String[] permissions, @NonNull int[] grantResults) {
    
    
        switch (requestCode){
    
    
            case 1:
            //判断是否授权成功
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
    
    
                    call();
                }else {
    
    
                    Toast.makeText(this,"You denied the permisson",Toast.LENGTH_SHORT).show();
                }
                break;
                default:
                    break;
        }
    }
}

7.3 访问其他程序中的数据
内容提供器的用法一般有两种:
①使用现有的内容提供器来读取和操作相应程序中的数据;
②创建自己的内容提供器给我们的程序的数据提供外部访问的接口。
若一个应用程序通过内容提供器对其数据提供了外部访问接口,那么任何其他的应用程序就都可以对这部分数据进行访问,如电话簿、短信、媒体库等程序都提供了类似的访问接口。
7.3.1 ContentResolver的基本用法
1、若应用程序想要访问内容提供器中共享的数据,就一定要借助ContentResolver类
2、ContentResolver中提供了一系列的方法用于对数据进行CRUD操作,其中insert()用于添加数据、update()用于更新数据、delete()用于删除数据、query()用于查找数据
3、不同于SQLiteDatabase,ContentResolver中的增删改查方法都是不接受表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI
4、内容URI给内容提供器中的数据建立了唯一的标识符,它主要由authority和path两部分组成。
–1)authority是用于对不同的应用程序做区分的,一般采用程序包名的方式来进行命名
–2)path是用于对同一应用程序中不同的表做区分的,通常添加到authority的后面
–3)一个URI最标准的格式写法如下:content://com.example.app.provider/table1,其中content:为协议声com.example.app为程序包名table1为表名
5、得到内容URI字符串之后,还需要将它解析成Uri对象才可以作为参数传入,解析的代码如下:

	Uri uri = Uri.parse("content://com.example.app.provider/table1");

7.3.2 读取系统联系人
1、首先创建一个ContactsTest项目,修改activity_main中的代码

 <ListView
        android:id="@+id/contacts_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </ListView>

2、修改MainActivity中的代码:
这里我们用一个ListView+运行时权限来完成对联系人的读取

public class MainActivity extends AppCompatActivity {
    
    
    ArrayAdapter<String> adapter;
    List<String> contactsList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
		//获取ListView实例
        ListView contactsView = (ListView)findViewById(R.id.contacts_view);
        //设置好适配器
        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,contactsList);
        contactsView.setAdapter(adapter);
        //运行时权限判断
        if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){
    
    
            ActivityCompat.requestPermissions(this,new String[]{
    
    Manifest.permission.READ_CONTACTS},1);
        }else {
    
    
            readContacts();
        }
    }

    private void readContacts(){
    
    
        Cursor cursor = null;
        try {
    
    
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
            //这里ContactsContract.CommonDataKinds.Phone.CONTENT_URI帮我们做好了对内容字符串URI的封装,CONTENT_URI就是Uri.pause()方法解析出来的结果
            if(cursor != null){
    
    
            //遍历,将联系人依次输出,并存入到集合contactsList中
                while (cursor.moveToNext()){
    
    
                    String diaplayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                    String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                    contactsList.add(diaplayName+"\n"+number);
                }
                adapter.notifyDataSetChanged();
            }
        }catch (SecurityException e){
    
    
            e.printStackTrace();
        }finally {
    
    
            if(cursor != null)
                cursor.close();
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    
    
        switch (requestCode){
    
    
            case 1:
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
    
    
                    readContacts();
                }else {
    
    
                    Toast.makeText(this,"You denied the permisson",Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
    }
}

3、注意声明权限:

  <uses-permission android:name="android.permission.READ_CONTACTS"/>

7.4 创建自己的内容提供器
首先我们可以通过新建一个类去继承ContentProvider的方式来创建一个自己的内容提供器,代码如下:

public class MyProvider extends ContentProvider {
    
    
	//初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作
    @Override
    public boolean onCreate() {
    
    
        return false;
    }
	//从内容提供器中查询数据。使用uri参数确定查询那张表,projection用于确定查询那些列, selection和selectionArgs用于查询那些行, sortOrder用于对结果进行排序
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    
    
        return null;
    }

    @Override
    public String getType(Uri uri) {
    
    
        return null;
    }
	//添加数据。uri参数确定要添加到的表,待添加的数据保存在values参数中
    @Override
    public Uri insert(Uri uri, ContentValues values) {
    
    
        return null;
    }
	//删除数据,uri参数确定要删除哪个表的数据,selection和selectionArgs用于约束删除哪些行
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
    
    
        return 0;
    }
	//更新数据,uri参数确定要更新哪个表的数据,values保存新数据,selection和selectionArgs用于约束更新哪些行
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    
    
        return 0;
    }
}

我们知道,一个标准的内容URI写法如下:content://com.example.app.provider/table1;
另一种格式,在后面加路径:
①现在若内容后面加数字,表示期望访问该表中拥有相应id的数据,如content://com.example.app.provider/table1/1 表示访问id为1的数据;
②现在若内容后面加 * ,表示能够匹配任意长度的任意字符,如content://com.example.app.provider/ * ;
③现在若内容后面加 # ,表示能够匹配任意长度的数字,如content://com.example.app.provider/table1/ # ;
然后我们再借助UriMatcher这个类就可以实现匹配内容URI的功能。该类中提供了一个addURI()方法,该方法接收authority、path和一个自定义代码,这样调用match方法时就可以将一个Uri对象传入,返回值是某个能够匹配这个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.provide","table1",TABLE1_DIR);
        uriMatcher.addURI("com.example.app.provide","table1/#",TABLE1_ITEM);
        uriMatcher.addURI("com.example.app.provide","table2",TABLE2_DIR);
        uriMatcher.addURI("com.example.app.provide","table2/#",TABLE2_ITEM);
    }
    @Override
    public Cursor query(Uri uri, String[] projection, Bundle queryArgs, CancellationSignal cancellationSignal) {
    
    
        switch (uriMatcher.match(uri)) {
    
    
            case TABLE1_DIR:
                //查询table1表中的所有数据
                break;
            case TABLE1_ITEM:
                //查询table1表中的单条数据
                break;
            case TABLE2_DIR:
                //查询table2表中的所有数据
                break;
            case TABLE2_ITEM:
                //查询table2表中的单条数据
                break;
            default:
                break;
        }
		return (要求返回的数据);
    }

上述方法只是以query()方法为例做了个示范,其余的几个方法的实现都差不多
对于getType()方法,它是所有的内容提供器都必须提供的一个方法,用于获取Uri对象所对应的MIME类型,一个内容URI所对应的MIME字符串主要由3部分组成,Android对这三部分做出了如下格式规定:
①必须以vnd开头;
②如果内容URI以路径结尾,则后面接android.cursor.dir/ ,如果内容URI以id结尾,则后面接android.cursor.item/;
③最后接上vnd."<“authority”>"."<“path”>"
如 content://com.example.app.provider/table1 对应的MIME类型是vnd.android.cursor.dir/vnd.com.example.app.provider.table1;
content://com.example.app.provider/table1 对应的MIME类型是vnd.android.cursor.item/vnd.com.example.app.provider.table1;
现在我们来完善getType()方法中的逻辑:

 @Override
    public String getType(Uri uri) {
    
    
        switch (uriMatcher.match(uri)) {
    
    
            case TABLE1_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
            case TABLE1_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
            case TABLE2_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
            case TABLE2_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
            default:
                break;
        }
        return null;
    }

猜你喜欢

转载自blog.csdn.net/Cristiano_san/article/details/108504505