android字节转m,一篇搞定Android M运行时权限

从 Android 6.0(API 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。这种权限机制可以让用户更好的管理应用的权限,保障用户隐私。

系统权限分为两类:

正常权限不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。

危险权限会授予应用访问用户机密数据的权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。

d6b3e16cc1d9?from=timeline&isappinstalled=0

危险权限及权限组

需要注意的是:

在 Android 5.1(API 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。(沿用之前的权限系统)

即使在安装时已经授予应用所有权限,在Android 6.0之后依然可以通过 "Setting" 来关闭已经授予的权限。

在请求权限时,系统只告诉用户应用需要的权限组,而不告知具体权限。

如果在未检查授权的情况下,直接使用危险权限,会导致程序Crash。

使用 v4 包中的 ContextCompat 处理权限(v13 包中的FragmentCompat),不需要考虑版本问题。

相关API

int checkSelfPermission()

检查应用是否有指定权限。返回值为 PackageManager.PERMISSION_GRANTED 表示有权限, PackageManager.PERMISSION_DENIED 表示无权限。

void requestPermissions()

请求指定权限,可以是多个,以数组的方式。

boolean shouldShowRequestPermissionRationale()

如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。

void onRequestPermissionsResult()

请求权限的结果回调。

使用原生API

因为以上列举的相关API都是在 API 23 才有的,为了适配低版本,官方提供了 v4 v13 兼容包。我们可以直接使用兼容包中的方法进行权限处理。

步骤(以拨打电话为例)

还是和以前一样,先在清单文件中申请所需要的权限。

在使用到拨打电话的地方,进行权限检查

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)

!= PackageManager.PERMISSION_GRANTED) {

// 应用没有授予拨打电话权限,请求权限

requestCameraPermission();

} else {

// 应用被授予拨打电话权限 PackageManager.PERMISSION_GRANTED

makeCall();

}

如果有权限,直接拨打电话,至此结束。

如果没有权限,则请求权限

ActivityCompat.requestPermissions(this,

new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALLPHONE);

在请求权限过程中可以使用shouldShowRequestPermissionRationale()检查是否被拒绝过,如果被拒绝过,可以给用户一个详细解释。

if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {

// 向用户详细解释申请该权限的原因

new AlertDialog.Builder(this)

.setCancelable(false)

.setMessage("拨打电话需要使用电话权限,如果不授予权限会导致该功能无法正常使用")

.setPositiveButton("好的", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

ActivityCompat.requestPermissions(

OriginalActivity.this,

new String[]{Manifest.permission.CALL_PHONE},

REQUEST_CALLPHONE

);

}

})

.setNegativeButton("不给", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

dialog.dismiss();

}

})

.show();

}

处理授权结果回调

@Override

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,

@NonNull int[] grantResults) {

if (requestCode == REQUEST_CALLPHONE) {

if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// 授予权限,拨打电话

makeCall();

} else {

Toast.makeText(this, "请求权限被拒绝", Toast.LENGTH_SHORT).show();

}

} else {

super.onRequestPermissionsResult(requestCode, permissions, grantResults);

}

}

使用轮子

在处理运行时权限的时候,虽然官方提供了兼容包不再需要做版本检查,但处理起来依然使代码很杂乱。现在已经出现了很多处理运行时权限的开源库,这里给大家推荐 PermissionsDispatcher。该库在GitHub同比获得 star 最多。而且使用 apt 技术,在编译时期动态生成xxxxPermissionsDispatcher模板代码,效率很高!

API 简介

该库使用 apt 技术,自然使用的就是注解。

注解

是否必须

作用

@RuntimePermissions

标记Activity/Fragment,则注解解释器会生成对应类的代码

@NeedsPermission

标记需要授权才能执行的方法

@OnShowRationale

对应shouldShowRequestPermissionRationale(),当应用之前请求过此权限但用户拒绝了请求,再次请求时调用

@OnPermissionDenied

当请求权限遭拒绝时调用

@OnNeverAskAgain

当用户勾选不再提示,并拒绝权限时,再次请求时调用

步骤(以使用相机为例)

还是在清单文件中声明使用的权限

配置依赖 PermissionsDispatcher,这里不再赘述

代码示例

@RuntimePermissions

public class PermissionsDispatcherActivity extends AppCompatActivity {

private ImageView imageView;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

imageView = (ImageView) findViewById(R.id.imageView);

findViewById(R.id.btn_camera).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

PermissionsDispatcherActivityPermissionsDispatcher.takePhotoWithCheck(PermissionsDispatcherActivity.this);

}

});

}

@NeedsPermission(Manifest.permission.CAMERA)

void takePhoto() {

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// 启动系统相机

startActivityForResult(intent, 100);

}

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_OK) { // 如果返回数据

if (requestCode == 100) { // 判断请求码是否为REQUEST_CAMERA,如果是代表是这个页面传过去的,需要进行获取

Bundle bundle = data.getExtras(); // 从data中取出传递回来缩略图的信息,图片质量差,适合传递小图片

Bitmap bitmap = (Bitmap) bundle.get("data"); // 将data中的信息流解析为Bitmap类型

imageView.setImageBitmap(bitmap);// 显示图片

}

}

}

@OnShowRationale(Manifest.permission.CAMERA)

void showRationaleForRecord(final PermissionRequest request) {

new AlertDialog.Builder(this)

.setPositiveButton("好的", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

request.proceed();

}

})

.setNegativeButton("不给", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

request.cancel();

}

})

.setCancelable(false)

.setMessage("拍照需要相机权限,应用将要申请使用相机权限")

.show();

}

@OnPermissionDenied(Manifest.permission.CAMERA)

void showCameraDenied() {

Toast.makeText(getApplicationContext(), "权限被拒绝", Toast.LENGTH_LONG).show();

}

@OnNeverAskAgain(Manifest.permission.CAMERA)

void onRCameraNeverAskAgain() {

new AlertDialog.Builder(this)

.setPositiveButton("好的", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

// 打开系统应用设置

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);

intent.setData(Uri.parse("package:" + getPackageName()));

intent.addCategory(Intent.CATEGORY_DEFAULT);

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(intent);

dialog.cancel();

}

})

.setNegativeButton("取消", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

dialog.cancel();

}

})

.setCancelable(false)

.setMessage("您已经禁止了相机权限,是否现在去开启")

.show();

}

@Override

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

super.onRequestPermissionsResult(requestCode, permissions, grantResults);

PermissionsDispatcherActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);

}

}

使用注意

注解的方法不能是private

在同一 Activity/Fragment 中可以多次使用以上注解,但是同一组权限处理中注解的value的值应该相同。

AS 中可以配合 PermissionsDispatcher plugin 插件一起使用。

总结与建议

请求权限显示的是标准Android对话框,我们不能自定义。

targetSdkVersion 设置为 22 或更低版本只是权宜之计。作为App开发者,需要尽快适配新权限机制。

在某个功能模块严重依赖某些权限的情况下,为了减少程序中出现过多权限检查,可以在该模块入口处统一检查,如果没有授予相应权限,则不提供该模块使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值