内容提供器(Content Provider)用于在不同的应用程序之间实现数据共享的功能,同时还能保证被访数据的安全性。目前,使用内容提供器是Android实现跨程序共享数据的标准方式。
运行时权限
安卓将权限分成普通权限和危险权限。区别在是否威胁到用户的安全和隐私。
涉及到普通权限的操作系统会自动授权,涉及到危险权限的操作需要向安卓系统申请权限许可。
在安卓6.0之前,涉及到危险的权限,直接在androidmanifest.xml里面写userpermission就可以实现。但是在更高版本的安卓中不可以。
高版本安卓需要申请运行时权限。
public void onCreate(){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);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();}}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {switch (requestCode){case 1:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {readContacts();}else {Toast.makeText(this, "you denied the permission", Toast.LENGTH_SHORT).show();}break;default:}}
第一步就是要先判断用户是不是已经给过我们授权了,借助的是ContextCompat.checkSelfPermission()方法。ContextCompat.checkSelfPermission()接受2个参数,第一个参数是context,然后是想要查询的权限。
调用ActivityCompat. requestPermissions()方法来向用户申请授权。
requestPermissions()方法接收3个参数,第一个参数要求是Activity的实例,第二个参数是一个String数组,我们把要申请的权限名放在数组中即可,第三个参数是请求码,只要是唯一值就可以了,这里传入了1。调用完了requestPermissions()方法之后,系统会弹出一个权限申请的对话框,然后用户可以选择同意或拒绝我们的权限申请,不论是哪种结果,最终都会回调到onRequest-PermissionsResult()方法中,而授权的结果则会封装在grantResults参数当中。这里我们只需要判断一下最后的授权结果,如果用户同意的话就调用call()方法来拨打电话,如果用户拒绝的话我们只能放弃操作,并且弹出一条失败提示。
private boolean isStoragePermissionGranted() {if (Build.VERSION.SDK_INT >= 23) {final Context context = getApplicationContext();int readPermissionCheck = ContextCompat.checkSelfPermission(context,Manifest.permission.READ_EXTERNAL_STORAGE);int writePermissionCheck = ContextCompat.checkSelfPermission(context,Manifest.permission.WRITE_EXTERNAL_STORAGE);if (readPermissionCheck == PackageManager.PERMISSION_GRANTED&& writePermissionCheck == PackageManager.PERMISSION_GRANTED) {Log.v("juno", "Permission is granted");return true;} else {Log.v("juno", "Permission is revoked");ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);return false;}} else { //permission is automatically granted on sdk<23 upon installationLog.v(TAG, "Permission is granted");return true;}}@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);Log.v("juno", "onRequestPermissionsResult requestCode : " + requestCode+ " Permission: " + permissions[0] + " was " + grantResults[0]+ " Permission: " + permissions[1] + " was " + grantResults[1]);
访问其他程序的数据
使用现有的Content provider
使用简介
要获取设备上的文件,比如多媒体。可以通过2种方式。一种是遍历磁盘目录上的所有文件,通过后缀名找到。一种是通过安卓系统提供的 Media Provider 提出查询请求。当然最简单的方式是使用 Media Provider .
Content Provider可以为其他程序提供内容。私有的:只能为所在应用程序提供数据访问请求. exported = "false" 。公用的:为所有程序提供数据访问请求。exported = "true"。使用前注意在AndroidManifest.xml文件中注册。
借助context.getContentResolver()方法获取到ContentResolver实例,定义有insert(), update(), delete(), query()方法。
增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI。内容URI给内容提供器中的数据建立了唯一标识符,它主要由两部分组成:authority和path。authority是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式来进行命名。比如某个程序的包名是com.example. app,那么该程序对应的authority就可以命名为com.example.app. provider。path则是用于对同一应用程序中不同的表做区分的,通常都会添加到authority的后面。比如某个程序的数据库里存在两张表:table1和table2,这时就可以将path分别命名为/table1和/table2,然后把authority和path进行组合,内容URI就变成了com.example.app.provider/table1和com.example.app.provider/table2。不过,目前还很难辨认出这两个字符串就是两个内容URI,我们还需要在字符串的头部加上协议声明。因此,内容URI最标准的格式写法如下:
传入了URL字符串,需要解析成URI对象,uri 

ContentProvider还有notifyChange()方法,提醒数据变动。
https://blog.csdn.net/hb8676086/article/details/50164947
Cursor简介
Cursor是游标,储存数据,是行的集合。
在你理解和使用 Android Cursor 的时候你必须先知道关于 Cursor 的几件事情:
- Cursor 是每行的集合。
- 使用 moveToFirst() 定位第一行。
- 你必须知道每一列的名称。
- 你必须知道每一列的数据类型。
- Cursor 是一个随机的数据源。
- 所有的数据都是通过下标取得。
从cursor对象中取出数据:
向table1中添加数据:先添加数据到values里面,然后通过insert()方法,传入uri(之前已经绑定为table1)和values
项目实现:读取联系人信息
//实现内容接收:ListView contactsView = (ListView) findViewById(R.id.contacts_view);adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contactsList);//使用ArrayAdapter实现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);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();}}}
创建自己的Content provider
步骤
新建一个类继承ContentProvider(),重写6大方法,分别是:
- onCreate()
- query()
- delete()
- insert()
- update()
- getType()
项目实现
没必要继续看了。我不是专门写应用程序的,因此不需要知道具体的实现。
