欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android跨程序共享数据-探究内容提供器(一)了解Android权限机制

程序员文章站 2022-05-09 20:53:02
...

为什么会有内容提供器?

在学Android持久化技术中,有文件存储,SharedPreferences存储一级数据库存储.在这些持久化技术所保存的数据只能带当前程序使用。在以前文件存储和SharedPreferences存储中提供了MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE这两种操作模式,用于让其他程序访问当前数据,但这两种模式在Android4.2的版本中都被废弃了。Android官方不再推荐使用这种方式来实现跨程序来数据共享功能,而是使用更加安全可靠的内容提供器技术。

那么我们为什么要将程序中的数据共享给其他程序呢?

并不是所有的数据都要共享,比如账号和密码这种隐私的数据肯定不能共享给别的程序的,有的一些数据可以让其他的程序当作基础数据,还是可以分享的。比如手机的电话簿程序,它的数据库保存了很多的联系人的信息,有些第三方的程序就很需要这些数据,不然就要拉跨了。不单单手机的电话簿程序可以分享,还有短信,媒体库等程序都可以实现跨程序共享的功能,所使用的技术就是内容提供器。

内容提供其简介

内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另外一个程序中的数据,同时还能保住被访问数据的安全性。目前,使用内容提供器是Android跨程序共享数据的标准方式。
不同于文件存储和SharedPreferences存储中的两种全局可读操作方式,内容提供器可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄露的风险。
在学内容提供器之前,我们要先了解一下Android运行时权限,因为在使用内容提供器中会使用到运行时权限的功能,不仅仅是内容提供器,比如联网也要开权限,必须要掌握一下。

运行时权限

Android的权限机制并不是什么新鲜的东西,从系统的第一个版本开始就存在。但其实之前的Android的权限机制在保护用户安全和隐私等方面起到的作用比较有限,尤其是一些大家都离不开的软件。所以Android开发团队在Android6.0系统中引用了运行时权限这个功能,能更好的保护用户的隐私安全。

Android权限机制讲解

首先先看一下Android权限机制是怎么样的。
下面的代码是为了要访问系统的短信

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.mmvtc.power">
    <uses-permission android:name="android.permission.READ_SMS"/>
    ....
    </manifest>

因为访问系统的短信涉及到了用户的安全性,因此必须在AndroidManifest.xml文件中加入权限声明,否则程序就会崩溃。

在AndroidManifest.xml加入两句权限之后,有什么影响?

用户主要在一下两个方面得到了保护。
一方面,如果用户在低于6.0的系统的设备上安装该程序,会在安装界面的时候给出提示,让用户知道该程序一共申请了哪些权限,从而决定是否安装这个程序。
另一方面,用户可以在设置-应用程序中随时查看任意一个程序的权限申请情况,这样程序的所有权限就尽收眼底,保证了应用程序不会出现各种滥用权限的情况。
在这种机制下,如果用户不同意应用程序不使用某种权限,就安装不了该程序。但是那是在理想情况下,有的应用程序对你非用不可,它就一次性把所有的权限都申请。在这种情况下,Android开发团队也意识到了这个问题,于是在6.0的系统加入了运行时权限的功能,就是用户不需要一次性同意所有权限,只有当使用软件时申请某一项权限申请再进行授权,比如打开地图的时候需要定位,这时候就需要授权定位的权限,如果不打开就使用不了。
并不是所有的权限都需要一个一个的去授权。在Android中将所有的权限归分两类:1:普通权限2:危险权限,如果准确的说,有第三类特殊程序,但是用得少。
普通权限:系统会帮我自动授权,我们不需要手动操作
危险权限:那些可能为触及用户的隐私或者对设备安全性造成影响的权限,如定位,获取设备联系人信息等,对于这部分的权限申请,需要用户点击授权才行,否则程序无法使用对应的功能。
但是Android权限有上百种,我们怎么从中区分那些是普通权限,危险权限呢?其实危险权限就几种,只要区分危险权限就了。
访问https://developer.android.google.cn/reference/android/Manifest.permission.html可以查看Android系统中完整的权限列表。

在程序运行时申请权限

首先新建一个Android_power程序,在这个程序上学习运行时权限的使用方法。
我们这里以读取手机短信为例子READ_SMS。
READ_SMS这个权限是编写读取短信的功能的时候需要声明的,因为读取短信会涉及到手机的隐私问题。
修改activity_main.xml布局问题,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
   <Button
       android:id="@+id/btn_readSms"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/tv_showSms"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

这里的布局文件放一个按钮和一个显示文本的控件,当点击按钮时读取短信显示到控件上。
接着修改MainActivity中的代码,如下所示:

public class MainActivity extends AppCompatActivity {

    private Button btnReadSms;
    private TextView tvShowSms;
    private StringBuilder smss=new StringBuilder();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化控件
        initView();
        //添加按钮点击事件
        btnReadSms.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Uri parse = Uri.parse("content://sms/");
                Cursor cursor = getContentResolver().query(parse, new String[]{"_id"}, null, null, null, null);
                if(cursor!=null){
                    while(cursor.moveToNext()){
                        int anInt = cursor.getInt(0);
                        smss.append(anInt+"\n");
                    }
                }
                tvShowSms.setText(smss.toString());
            }
        });
    }
    //初始化控件
    private void initView() {
        btnReadSms = (Button) findViewById(R.id.btn_readSms);
        tvShowSms = (TextView) findViewById(R.id.tv_showSms);
    }
}

可以看到,我们在按钮的点击事件中,我们去读取手机短信的一个信息,具体代码是干嘛是我们先不急着去了解,反正就点击按钮然后获取系统短信的内容,然后设置到文本控件上。
然后修改AndroidManifest.xml文件,添加如下权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.mmvtc.power">
    <uses-permission android:name="android.permission.READ_SMS"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
       ......
        </application>
</manifest>

这样我们读取短信的功能就实现了,并且低于Android6.0的系统的手机上都是可以正常运行的,但是如果我们在6.0或者更高的版本的手机上运行,点击按钮程序就会关闭,查看logcat中打印的日志,会看到如下信息:

 java.lang.SecurityException: Permission Denial: reading com.android.providers.telephony.SmsProvider uri content://sms/ from pid=16490, uid=10124 requires android.permission.READ_SMS, or grantUriPermission()
        at android.os.Parcel.createException(Parcel.java:1950)
        at android.os.Parcel.readException(Parcel.java:1918)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
        at android.content.ContentProviderProxy.query(ContentProviderNative.java:418)
        at android.content.ContentResolver.query(ContentResolver.java:802)
        at android.content.ContentResolver.query(ContentResolver.java:752)
        at cn.mmvtc.power.MainActivity$1.onClick(MainActivity.java:29)

在错误的信息中,看到"Permission Denial",是由于权限被禁止所导致的,因为6.0及以上系统在使用危险权限时必须进行权限处理。
下面我们来对权限进行处理,修改MainActivity中的代码,如下所示:

public class MainActivity extends AppCompatActivity {

    private Button btnReadSms;
    private TextView tvShowSms;
    private StringBuilder smss=new StringBuilder();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化控件
        initView();
        //添加按钮点击事件
        btnReadSms.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
            if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_SMS)!= PackageManager.PERMISSION_GRANTED){
                ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.READ_SMS},1);
            }else{
                readSms();
            }
            }
        });
    }

    //读取手机短信
    private void readSms(){
        Uri parse = Uri.parse("content://sms/");
        Cursor cursor = getContentResolver().query(parse, new String[]{"_id"}, null, null, null, null);
        if(cursor!=null){
            while(cursor.moveToNext()){
                int anInt = cursor.getInt(0);
                smss.append(anInt+"\n");
            }
        }
        tvShowSms.setText(smss.toString());
    }
    //对权限进行处理

    @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){
                    readSms();
                }
                break;
            default:
                break;
        }
    }

    //初始化控件
    private void initView() {
        btnReadSms = (Button) findViewById(R.id.btn_readSms);
        tvShowSms = (TextView) findViewById(R.id.tv_showSms);
    }
}

上面的代码将运行时的权限的完整流程都走了一遍。
运行时权限的核心就是在程序运行过程中由用户授权我们去执行某些危险操作,程序是不可以擅自做主去执行那些危险操作的。所以,第一步先判读用户是否已经授权,借助的ContextCompat.checkSelfPermission()方法.checkSelfPermission方法接收2个参数,第一个参数是上下文Context,第二个参数是具体的权限名,如果是打电话的话,权限就改成,Manifest.permission.CALL_PHONE,然后我们使用方法的返回值和PackageManager.PERMISSION_GRANTED作比较,相等就说明用户已经授权,不等就表示用户没有授权。
如果授权了就直接读取短信,把读取短信的逻辑封装在readSms()方法中。如果没有,则需要调用ActivityCompat.requestPermissions()方法来向用户申请权限,requestPermissions()方法接收机三个参数,第一个参数要求是Activity的实例,第二个参数是一个String数组,我们把要申请的权限名放在数组中即可,第三个参数的请求码,只要是唯一值就可以,这里输入1.
调用完requestPermissions()方法吼,系统会弹出一个权限申请的对话框,然后用户可以选择同意或者拒绝我们的权限申请,无论那种结果,都会返回到onRequetPermissionsResult()方法中,而授权的结果则会封装在grantResults参数当中。这里我们只需要判断一下最后的授权结果,如果用户同意就readSms()读取短信,拒绝的话就放弃操作,并且弹出一条失败提示。
现在重新运行一下程序,并点击读取手机短信信息,下图 1 所示。
由于用户还没有授权过我们读取短信的权限,因此第一个运行会弹出这样一个权限申请的对话框,用户可以选择同意和拒绝,比如我这里点了DENY,结果如图 2所示
Android跨程序共享数据-探究内容提供器(一)了解Android权限机制
1.申请电话权限对话框
Android跨程序共享数据-探究内容提供器(一)了解Android权限机制
2 用户拒绝权限申请

由于用户没有同意授权,我们自能弹出一个操作失败的提示,下次我们再次点击读取手机短信信息,仍然会弹出权限申请的对话框
,这次点击ALLOW结果如图 3 所示:
Android跨程序共享数据-探究内容提供器(一)了解Android权限机制

这里我们只读取了短信的id。
关于运行时权限的内容就到这。下一篇探究内容提供器(二)将进入正题-内容提供器

相关标签: Android四大组件