1

Android运行时权限

参考网址:在运行时请求权限

在Android6.0之前应用请求权限都是在应用安装时将所有权限清单展示给用户,当用户选择安装就默认允许应用请求的所有权限,而Android6.0(API级别23)以后google修改权限请求的方式,首先将权限分为两类:

  • 正常权限:不会给用户带来隐私风险的权限。只要在AndroidManifest.xml中声明即可,系统自动授权。
  • 危险权限:可能会访问用户隐私数据的权限。需要先在AndroidManifest.xml中声明,然后在使用之前通过用户手动授权。
关于权限的分类请参考官网文档:系统权限

新的权限管理分为以下两种情况:

  • 如果手机系统低于Android6.0或者应用的targetSdkVersion低于23,还是按照老的权限管理方式:在AndroidManifest.xml声明的所有权限都会在应用安装时以清单的形式展现给用户,如果用户同意安装就通过了所有的权限申请。
  • 如果手机系统为Android6.0以上并且应用的targetSdkVersion等于或者高于23,就会按照新的权限管理方式:在AndroidManifest.xml中声明的正常权限系统将会系统授权,而危险权限就需要应用在使用权限之前请求用户授权。

注意:当手机系统为Android6.0以上但是应用的targetSdkVersion低于23虽然采用的是安装时授权,但是用户依然可以在设置中手动关闭应用的权限,所以应用开发依然需要考虑缺失某些权限的情况

接下来看看应用是如何检查和请求用户授权的:

  1. 检查授权:使用ContextCompat.checkSelfPermission方法检查Activity是否获得授权,返回值PackageManager.PERMISSION_GRANTED表示已获得授权,PackageManager.PERMISSION_DENIED表示未获得授权,示例代码如下:

    int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.WRITE_CALENDAR);
    if (PackageManager.PERMISSION_GRANTED == permissionCheck) {
        // 已授权
    } else {
        // 未授权,需要请求用户授权
    }
  2. 申请权限:使用ActivityCompat.requestPermissions方法动态申请权限,示例代码如下:

    final static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
    
    ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
  3. 获取权限申请结果:通过ActivityonRequestPermissionsResult回调获取申请结果,示例代码如下:

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE == requestCode) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 用户授权通过
            } else {
                // 用户拒绝授权
            }
        }
    }
  4. 判断用户是否拒绝过授权:用户拒绝授权可能是因为不了解应用获取权限的目的,所以google推荐开发者在用户拒绝授权之后再次请求权限之前可以向用户说明申请权限的目的有利于用户通过授权,可以通过ActivityshouldShowRequestPermissionRationale方法得知用户是否已经拒绝过授权,示例代码如下:

    if (Build.VERSION.SDK_INT > 22 
    && shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
        // 用户已经拒绝过授权,可以想用户说明申请权限的目的
    } else {
        // 直接请求权限
    }

通过上述几个步骤已经完成了一次运行时权限申请的全部过程,还有一点requestPermissions方法请求权限的时候,系统唤起的dialog上显示的是开发者所申请权限的权限组的信息,同一个权限组的权限只需要申请一次一旦用户授权通过应用就会获取整个权限组的授权,再次请求同组的其它权限系统会自动授权,理论上来说同组权限只需要申请一次,但是goole的官方文档上依然推荐开发者为每个权限单独申请授权,因为在之后的版本升级中权限的分组可能会发生改变。

最后附上完整的运行时权限申请示例:

class MainActivity : AppCompatActivity() {
    val PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener({
            val permissionCheck = ContextCompat.checkSelfPermission(this@MainActivity,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)
            if (PackageManager.PERMISSION_GRANTED == permissionCheck) {
                Toast.makeText(this@MainActivity, "权限已授权", Toast.LENGTH_SHORT).show()
            } else {
                if (Build.VERSION.SDK_INT > 22 && shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    Toast.makeText(this@MainActivity, "授权已经被拒绝过了,可以向用户说明权限用途", Toast.LENGTH_SHORT).show()
                    AlertDialog.Builder(this@MainActivity)
                            .setMessage("权限作用说明")
                            .setPositiveButton("我知道了", { dialog, which ->
                                requsetPermission()
                            })
                            .create().show()
                } else {
                    requsetPermission()
                }
            }
        })
    }

    private fun requsetPermission() {
        ActivityCompat.requestPermissions(this@MainActivity, Array(1) { Manifest.permission.WRITE_EXTERNAL_STORAGE }, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        if (requestCode == PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this@MainActivity, "授权成功", Toast.LENGTH_SHORT).show()
            } else {
                Toast.makeText(this@MainActivity, "授权失败", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

路人甲的x个马甲
2 声望0 粉丝