跨程序共享数据——探究内容提供器
内容提供器的简介
主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。目前,使用内容提供器是Android实现跨程序共享数据的标准方式。内容提供器可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。
运行时权限
Android权限机制
在我们使用任何一个软件的时候,有时就会弹出来你是否允许使用摄像头…,这就是权限。安卓当中的权限非常多,要是所有的权限都要由我们来授权就会非常麻烦,因此Android现在将所有的权限归成了两类,一类是普通权限,一类是危险权限。普通权限指的是那些不会直接威胁到用户的安全和隐私的权限,对于这部分权限申请,系统会自动帮我们进行授权,而不需要用户再去手动操作了。危险权限则表示那些可能会触及用户隐私,或者对设备安全性造成影响的权限,如获取设备联系人信息、定位设备的地理位置等,对于这部分权限申请,必须要由用户手动点击授权才可以,否则程序就无法使用相应的功能。
下面Android中所有的危险权限,一共是9组24个权限:
当你要使用这张表中的权限,那么就需要进行运行时权限处理,如果不在这张表中,那么只需要在AndroidManifest.xml文件中添加一下权限声明就可以了。
注意,表格中每个危险权限都属于一个权限组,我们在进行运行时权限处理时使用的是权限名,但是用户一旦同意授权了,那么该权限所对应的权限组中所有的其他权限也会同时被授权。
在程序运行时申请权限
在上面的表格当中会看到CALL_PHONE权限是危险权限,就根据这个来学习在程序运行时申请权限吧。
对于一个权限无非就是授权和不授权两种情况:
PackageManager.PERMISSION_GRANTED
:表示权限已经被授权PackageManager.PERMISSION_DENIED
:表示改权限处于未授权的状态
我们在程序运行时申请权限时,首先就是判断是否已经授权了,当处于已授权的状态时,就无需提示用户,直接进行操作,当没有授权我们就提示用户是否进行授权,根据用户的选择进行下一步改如何操作。下面就是代码的示例:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});Button buttonmakecall = (Button) findViewById(R.id.makecall);buttonmakecall.setOnClickListener(new View.OnClickListener() {@Overridepublic 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:17765079498"));startActivity(intent);} catch (SecurityException e) {e.printStackTrace();}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == 1) {if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {call();} else {Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();}} else {;}}
}
- 我们在点击按钮之后,先对权限进行判断是否已经被授权,借助
ContextCompat.checkSelfPermission()
方法,是一个静态方法,用于检查应用是否具有执行某个操作所需的权限,接收两个参数:
Context
:第一个参数是需要检查权限的上下文,上下文提供了一个环境,使得权限检查能够关联到正确的应用程序String permission
:第二个参数是需要检查的权限的名称
- 遇到没有授权,我们就需要向用户申请授权,借助
ActivityCompat.requestPermissions()
方法,接收三个参数:
Activity
:第一个参数是Activity
的实例,通常是当前的Activity
。这是请求权限的上下文,并且是接收权限请求结果的回调所在String[] permissions
:第二个参数是一个包含权限名称的字符串数组。这些权限是应用请求授权的权限列表int requestCode
:第三个参数是一个整数值,它是请求代码,用于唯一标识权限请求。当权限请求的结果返回时,这个值将用于确定是哪个权限请求的结果。这个值应该大于0,并且最好是一个不会与Activity
中其他请求代码冲突的值
- 在调用
requestPermissions()
方法之后,无论结果如何都会回调到onRequestPermissionsResult()
方法,此时就可以写用户不同选择带来的操作,这个方法也是三个参数,都是由调用requestPermissions()
方法时传入的:
int requestCode
: 一个整型值,用于标识请求权限的请求码String[] permissions
:一个字符串数组,包含请求的权限名称int[] grantResults
:用户的授权结果就会保存在这里面
在if
语句当中与允许授权作比较是很好理解的,前面还有一个是判断字符串的长度是否大于0,是因为数字所存放的是被处理的请求,如果不做判断,当数组长度为0直接访问会报错。
这下去运行程序当你按下按钮,就会出现让用户授权的提示:
按下允许就会执行相关操作,以上面为例就会出现拨打电话的页面:
这时能再次按下按钮就不会再次让你授权,你想要关闭授权就在设置里面关闭
访问其他程序中的数据
内容提供器的用法有两种:
- 使用现有的内容提供器来读取和操作相应程序中的数据
- 创建自己的内容提供器给我们程序的数据提供外部访问接口
ContentResolver的基本用法
对于每一个应用程序来说,如果想要访问内容提供器中共享的数据,就一定要借助ContentResolver
类,可以通过Context
中的getContentResolver()
方法获取到该类的实例,它提供了增删改查的操作,与SQLite
的方法名是相同的,但在操作上面所传参数有些许不同,不接受表名参数,而是使用一个Uri
参数代替,这个参数被称之为内容URI。
内容URI主要由两部分组成:
- authority:是用于对不同的程序做区分,一般为了避免冲突,都会采用程序包名的方式来进行命名
- path:对同一应用程序的不同表做区分的,通常加到authority的后面
例如:content://com.example.app.provider/table1
得到了字符串要先转成Uri对象,调用Uri.parse()
方法:
Uri uri = Uri.parse("content://com.example.app.provider/table1")
查询表中数据
Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
接下来就可以使用循环读取数据了。
添加表中数据
ContentValues values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);
getContentResolver().insert(uri, values);
更新表中数据
ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text", "1"});
这段代码的目的是更新那些column1
列的值为"text"且column2
列的值为"1"的记录。只有同时满足这两个条件的记录会被更新,且column1
的值会被更新为一个空字符串
删除表中数据
getContentResolver().delete(uri, "column2 = ?", new String[] {"1"});
值为"text"且column2
列的值为"1"的记录。只有同时满足这两个条件的记录会被更新,且column1
的值会被更新为一个空字符串
删除表中数据
getContentResolver().delete(uri, "column2 = ?", new String[] {"1"});
文章到这里就结束了!