不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。
例如,设置时区的权限就是正常权限。如果应用声明其需要正常权限,系统会自动向应用授予该权限。
会授予应用访问用户机密数据的权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。
首先要在清单文件中申请所需要的权限。
将硬件声明为可选(uses-feature)以及要确定硬件可用性
3. 按API级别申明权限
当目标sdk大于等于29的时候配置android.permission.WRITE_EXTERNAL_STORAGE
会出现下面的警报:
WRITE_EXTERNAL_STORAGE no longer provides write access when targeting Android 10+
这时候需要设置根据Api级别申明权限android:maxSdkVersion
,该属性表示设备搭载的系统版本高于 maxSdkVersion
时不需要特定权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" tools:ignore="ScopedStorage"/>
请求应用权限,其逻辑应该如下图所示
首先检查是否已经授予了某个权限,使用 ContextCompat.checkSelfPermission()
此方法会返回 PERMISSION_GRANTED
或 PERMISSION_DENIED
。如果 ContextCompat.checkSelfPermission()
方法返回 PERMISSION_DENIED
,请调用 shouldShowRequestPermissionRationale()
。如果此方法返回 true
,请向用户显示指导界面,在此界面中说明用户希望启用的功能为何需要特定权限。(返回true说明应用之前请求过此权限但用户拒绝了请求,此方法将返回 true),之后再调用requestPermissions
函数进行权限的请求,这个函数会引起onRequestPermissionsResult
的回调。
private void callPhone(){ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { // 应用没有授予拨打电话权限,请求权限 requestPhoneCallPermission(); } else { // 应用被授予拨打电话权限 PackageManager.PERMISSION_GRANTED makeCall(); } }
private void requestPhoneCallPermission() { // 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true 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( MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALLPHONE//注意这个REQUEST_CALLPHONE会传给回调函数的requestCode ); } }) .setNegativeButton("拒绝", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .show(); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALLPHONE); } }
下面是回调函数:
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); } }
当需要打电话的时候第一次请求:
该工具直接使用注解来自动帮我们生成权限请求代码,使用时需要在项目中添加如下两个注解:
implementation "com.github.permissions-dispatcher:permissionsdispatcher:4.8.0" annotationProcessor "com.github.permissions-dispatcher:permissionsdispatcher-processor:4.8.0"
注解 | 是否必须 | 作用 |
---|---|---|
@RuntimePermissions | 是 | 这是必须使用的注解,标记Activity/Fragment,则注解解释器会生成对应类的代码 |
@NeedsPermission | 是 | 标记需要请求使用权限的方法,也就是说你获取了相应的权限之后就会执行这个方法,可以在括号里面加一个权限或者多个权限。 |
@OnShowRationale | 否 | 对应之前的shouldShowRequestPermissionRationale(),当应用之前请求过此权限但用户拒绝了请求,再次请求时调用,告知用户为什么必须要授权这个权限,注解括号里面有参数,传入想要申请的权限,而且这个方法还要传入一个PermissionRequest对象,这个对象有两种方法:proceed()让权限请求继续,cancel()让请求中断。也就是说,这个方法会拦截你发出的请求,这个方法用于告诉用户你接下来申请的权限是干嘛的,说服用户给你权限。 |
@OnPermissionDenied | 否 | 当请求权限遭拒绝时调用,可以告知用户已经拒绝该权限 |
@OnNeverAskAgain | 否 | 当用户勾选不再提示,并拒绝权限时,再次请求时调用(如上图中禁止之后不再询问 按钮)也就是说,我们可以在这个方法做申请权限失败并选择不再询问之后的处理。例如,可以告诉作者想开启权限的就从手机设置里面开启。 |
注意:上面这些注解的方法都不能是private,原因看下面
使用PermissionsDispatcher除了要实现注解之外,还要重写Activity的onRequestPermissionsResult()方法,在里面让一个PermissionsDispatcher执行回调。这个PermissionsDispatcher是什么来的呢?
原来只要我们实现了@RuntimePermissions和@NeedsPermission这两个必须的注解之后,再build一次project之后,编译器就会在在app\build\intermediates\classes\debug目录下与被注解的Activity同一个包下生成一个辅助类,名称为 “被注解的Activity的名称+PermissionsDispatcher” 的辅助类,用来调用被注解的Activity的方法(就是因为这个所以被注解的方法不能private,private方法的作用域不在其他的类)。所以,第一次用的话,要注解好之后,build一次,下面的方法里面的PermissionsDispatcherActivityPermissionsDispatcher
才不会令AS报红。
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); PermissionsDispatcherActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); }
注意代码中的onclick方法PermissionsDispatcherActivityPermissionsDispatcher
的takePhotoWithPermissionCheck
方法在第一次编译完成之后会自动生成。
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { PermissionsDispatcherActivityPermissionsDispatcher.takePhotoWithPermissionCheck(PermissionsDispatcherActivity.this); } });
上面的具体代码链接:
https://github.com/Haoocker/Android_Learn/tree/master/RuntimePermissionDemo