一. 概述
感谢郭神,自从Android6.0发布以来,在权限上做出了很大的变动,不再是之前的只要在manifest设置就可以任意获取权限,而是更加的注重用户的隐私和体验,不会再强迫用户因拒绝不该拥有的权限而导致的无法安装的事情,也不会再不征求用户授权的情况下,就可以任意的访问用户隐私,而且即使在授权之后也可以及时的更改权限。这就是6.0版本做出的更拥护和注重用户的一大体现。
1.1 Android 6.0 权限
- 以前,申请一个子权限会自动获取权限组中其他子权限。组内其他子权限可以直接使用。
- 现在,申请一个子权限,组内其他子权限不会自动获取。使用组内其他子权限的时候。需要再次申请。(但是这种情况不会弹出系统的权限申请框)如果不申请。会FC。
- 同组权限一起申请。当我们申请权限时。申请同组的多个权限时,也只会弹出一次申请框。所以建议一起申请。
- 6.0以下版本(系统自动申请)
- 暂时发现vivo、oppo、魅族的6.0以上版本
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
第二步:检测当前权限是否已授权
通过 ContextCompat.checkSelfPermission(context,permission) 方法,方法返回值为 PackageManager.PERMISSION_GRANTED or PackageManager.PERMISSION_DENIED :
第三步:请求权限
通过 ActivityCompat.requestPermissions(activity,permissions,requestCode) ,第二个参数是一个String数组,第三个参数是请求码,便于在 onRequestPermissionsResult() 方法中根据requestCode进行判断:
第四步:处理权限申请的结果
当请求权限后,界面会显示一个对话框提示用户让用户选择是否同意我们申请的权限,在用户处理完毕后,系统会回调 onRequestPermissionsResult 方法,我们需要在在activity中重写 onRequestPermissionsResult(requestCode,permissions,grantResults) 方法, grantResults 是int类型的数组,每个值为 PackageManager.PERMISSION_GRANTED or PackageManager.PERMISSION_DENIED 分别对应 申请的每个permissions 的每个请求(比如申请了6个权限,那这个grantResult数组的值就会有6个,按顺序分别代表每个权限是否已被用户同意)。
这里存在几种情况需要注意:
1)用户同意了我们的申请:
2)用户拒绝了我们的申请。
这个时候为了良好的用户体验,我们可以向用户阐明我们为什么需要这个权限,提醒用户这个权限的必要性。 ActivityCompat.shouldShowRequestPermissionRationale(activity,permission) 这个方法是在用户拒绝权限后返回true。什么意思呢,也就是说:用户第一次点击一个需要权限的地方,该方法返回false(因为用户没拒绝~),当用户拒绝掉该权限,下次点击此权限处,该方法会返回true。可在里面进行对该权限的说明,然后弹出权限让用户选择,并且对话框有don't ask again选项:
3) 用户选择don't ask again 后
我们已经向用户阐明我们为什么需要这个权限,但是用户依然置之不理,并且还选择了don't ask again 复选框,那这个时候 ActivityCompat.shouldShowRequestPermissionRationale(activity,permission) 会一直返回false, 并且 ActivityCompat.requestPermissions 不会弹出对话框,系统直接deny,并回调 onRequestPermissionsResult 方法:
<用户拒绝时>
<用户接受时>
三. 代码演示
比如我要申请以下4种权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
为了最大程度给予用户自由,我在首界面,也就是SplashActivity去申请这些权限,我们约定只有定位权限是必须的,只要用户同意定位权限,我们就允许用户进入主界面。实际的代码如下:
package com.yongdaimi.android.androidapitest; import android.Manifest; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.Log; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class SplashActivity extends AppCompatActivity { private String[] mNeedGrantedPermissionArr = null; private static final int REQUEST_CODE_GRANT_PERMISSION = 10001; public static final String TAG = "xp.chen"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); initNeedGrantedPermissionArr(); } private void initNeedGrantedPermissionArr() { mNeedGrantedPermissionArr = new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, }; } @Override protected void onStart() { super.onStart(); checkRuntimePermissions(); } private void checkRuntimePermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { List<String> needApplyPermissionList = new ArrayList<String>(); for (String s : mNeedGrantedPermissionArr) { if (ContextCompat.checkSelfPermission(getApplicationContext(), s) != PackageManager.PERMISSION_GRANTED) { needApplyPermissionList.add(s); } } if (needApplyPermissionList.size() > 0) { ActivityCompat.requestPermissions(this, needApplyPermissionList.toArray(new String[0]), REQUEST_CODE_GRANT_PERMISSION); } else { skip2MainActivity(); Log.i(TAG, "checkRuntimePermissions: needApplyPermissionList num is 0"); } } else { skip2MainActivity(); } } @Override public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CODE_GRANT_PERMISSION) { for (String permission: permissions) { Log.i(TAG, "onRequestPermissionsResult: permission: "+permission); } for (int grantRet : grantResults) { Log.i(TAG, "onRequestPermissionsResult: grantRet: "+ grantRet); } List<String> permissionList = Arrays.asList(permissions); if (permissionList.contains(Manifest.permission.ACCESS_FINE_LOCATION)) { int pos = permissionList.indexOf(Manifest.permission.ACCESS_FINE_LOCATION); if (grantResults[pos] != PackageManager.PERMISSION_GRANTED) { mNeedGrantedPermissionArr = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { AlertDialog dialog = new AlertDialog.Builder(this) .setTitle("Tips") .setMessage("I really need Location Permission to work") .setCancelable(false) .setNegativeButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { checkRuntimePermissions(); } }) .create(); dialog.show(); } else { AlertDialog dialog = new AlertDialog.Builder(this) .setTitle("Tips") .setMessage("The Location permission is not yet enabled, It is necessary, you can open it in the settings ui") .setCancelable(false) .setNegativeButton("Close App", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setPositiveButton("Go Open", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(); intent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent); } }) .create(); dialog.show(); } } else { skip2MainActivity(); Log.i(TAG, "checkRuntimePermissions: grantResults[pos] != PackageManager.PERMISSION_GRANTED"); } } else { skip2MainActivity(); Log.i(TAG, "checkRuntimePermissions: not necessary"); } } } private void skip2MainActivity() { new Handler().postDelayed(new Runnable() { @Override public void run() { Intent intent = new Intent(getApplicationContext(), BLEBluetoothApiUseDemoActivity.class); startActivity(intent); finish(); } }, 1000); } }
参考链接:
1. Android 权限管理