Android 运行时权限之EasyPermissions
2017-09-08 本文已影响554人
安静的学点东西
上一篇我们介绍了Android M之后系统引入了新的权限管理机制即运行时权限管理,但是真正的要在自己项目中引入该机制,需要编写很多额外的代码,让人望而生畏!为了解决这个问题,很多网友为了能简化代码、降低使用难度,封装了很多优秀的权限库,主要有RxPermissions,AndPermission和Google提供的easypermissions。
今天我们主要介绍的就是Google为我们提供的EasyPermissions
相关API
函数 | |
---|---|
EasyPermissions.requestPermissions() | 请求权限 |
EasyPermissions.hasPermissions() | 是否拥有 |
EasyPermissions.onRequestPermissionsResult() | 将权限处理结果托管给EasyPermission |
EasyPermissions.PermissionCallbacks | 权限结果处理回调 |
onPermissionsGranted() | 返回获取成功的权限 |
onPermissionsDenied() | 返回获取失败的权限 |
AfterPermissionGranted | 当权限获取成功之后会回调到该注解方法中 |
使用
注意:该部分的内容主要是从官网翻译而来~
-
导入EasyPermissions
dependencies { compile 'pub.devrel:easypermissions:1.0.0' }
-
请求权限
下面的例子展示了在一个方法中如何同时去获取拍照和定位两个权限,有些知识点是你必须要注意:
- 使用
EasyPermissions#hasPermissions(...)
去检查是否已经获取了需要申请的权限,该方法可以同时传多个权限作为参数; - 使用
EasyPermissions#requestPermissions
申请权限,该方法会申请系统权限并且如果需要的话会提示用户提供的申请权限的原因。传入的requestCode必须唯一,该方法同时可以传入多个权限作为参数。 - 使用
AfterPermissionGranted
注解,这是可选的,但是很便捷。只要所有的权限被允许,所有的被添加想通requestCode的注解的方法将会被执行。这种方法大大简化了只有所有权限都被允许时进行下一步操作的流程。你也可以实现onPermissionsGranted
接口对结果进行详细的处理。
@AfterPermissionGranted(RC_CAMERA_AND_LOCATION) private void methodRequiresTwoPermission() { String[] perms = {Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION}; if (EasyPermissions.hasPermissions(this, perms)) { // Already have permission, do the thing // ... } else { // Do not have permissions, request them now EasyPermissions.requestPermissions(this, getString(R.string.camera_and_location_rationale), RC_CAMERA_AND_LOCATION, perms); } }
- 使用
-
可选选项,为了更好的进行控制,你可以让你的Activity/Fragment实现PermissionCallBacks接口。
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); // 将处理结果托管给EasyPermissions进行处理 EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } @Override public void onPermissionsGranted(int requestCode, List<String> list) { // Some permissions have been granted // ... } @Override public void onPermissionsDenied(int requestCode, List<String> list) { // Some permissions have been denied // ... } }
-
必要的权限
有些时候,你的应用必须拥有某些必要的权限才能够运行,但是用户拒绝权限并且点击了"不再提醒"选项,你将不能够请求这些权限并且你必须去设置里面进行修改。对于上面的情况你可以调用
EasyPermissions.somePermissionPermanentlyDenied(...)
方法进行检查,然后弹出一个对话框提示用户去系统设置里面进行修改。@Override public void onPermissionsDenied(int requestCode, List<String> perms) { Log.d(TAG, "onPermissionsDenied:" + requestCode + ":" + perms.size()); // (Optional) 检查用户拒绝权限的时候是否选择了“不再提醒”的情况 // 这里将弹出对话框引导用户去系统设置 if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { new AppSettingsDialog.Builder(this).build().show(); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE) { // 监听从设置页面返回,然后进行下一步处理,如弹出一个toast Toast.makeText(this, R.string.returned_from_app_settings_to_activity, Toast.LENGTH_SHORT) .show(); } }
原理解析
核心文件之EasyPermissions.java:
-
hasPermissions()
/** * 检查是否有这一系列的权限 * * @param context the calling context. 调用该方法的context * @param perms 一个或多个权限,比如:Manifest.permission.CAMERA * @return 如果所有的权限都被授予了,返回true;如果至少有一个权限没被授予,那么返回false; * @see Manifest.permission */ public static boolean hasPermissions(@NonNull Context context, @NonNull String... perms) { //因为SDK版本如果小于M,那么会采用之前的权限管理机制,所以总是返回true,让系统自己去决定需要的权限。 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { Log.w(TAG, "hasPermissions: API version < M, returning true by default"); // DANGER ZONE!!! Changing this will break the library. return true; } for (String perm : perms) { //这里最终还是调用ContextCompat.checkSelfPermission检查权限 boolean hasPerm = (ContextCompat.checkSelfPermission(context, perm) == PackageManager.PERMISSION_GRANTED); if (!hasPerm) { //遍历所申请的权限,只要有一个被拒绝就返回false; return false; } } //只有所有的权限都被允许才会返回true return true; }
-
requestPermissions()
//因为requestPermissions被重载多次,我们只分析最终被调用的方法 private static void requestPermissions( @NonNull PermissionHelper helper, @NonNull String rationale, @StringRes int positiveButton, @StringRes int negativeButton, int requestCode, @NonNull String... perms) { // Check for permissions before dispatching the request if (hasPermissions(helper.getContext(), perms)) { //这里会检查以下所有的权限,如果全部被允许就调用notifyAlreadyHasPermissions方法。 notifyAlreadyHasPermissions(helper.getHost(), requestCode, perms); //helper.getHost就是我们传入的Context或Activity或Fragment return; } // Request permissions 请求权限 helper.requestPermissions(rationale, positiveButton, negativeButton, requestCode, perms); }
-
notifyAlreadyHasPermissions()
/** * Run permission callbacks on an object that requested permissions but already has them by * simulating {@link PackageManager#PERMISSION_GRANTED}. * * @param object the object requesting permissions. * @param requestCode the permission request code. * @param perms a list of permissions requested. */ private static void notifyAlreadyHasPermissions(@NonNull Object object, int requestCode, @NonNull String[] perms) { int[] grantResults = new int[perms.length]; for (int i = 0; i < perms.length; i++) { grantResults[i] = PackageManager.PERMISSION_GRANTED; } onRequestPermissionsResult(requestCode, perms, grantResults, object); //该方法会统计所有的权限的结果,将结果回调到UI中。 }
-
onRequestPermissionsResult()
public static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults, @NonNull Object... receivers) { // Make a collection of granted and denied permissions from the request. List<String> granted = new ArrayList<>(); List<String> denied = new ArrayList<>(); for (int i = 0; i < permissions.length; i++) { String perm = permissions[i]; if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { granted.add(perm); } else { denied.add(perm); } } // iterate through all receivers for (Object object : receivers) { // Report granted permissions, if any. if (!granted.isEmpty()) { if (object instanceof PermissionCallbacks) { //将被用户同意的权限回调到UI中的onPermissionsGranted方法中 ((PermissionCallbacks) object).onPermissionsGranted(requestCode, granted); } } // Report denied permissions, if any. if (!denied.isEmpty()) { if (object instanceof PermissionCallbacks) { ////将被用户拒绝的权限回调到UI中的onPermissionsDenied方法中 ((PermissionCallbacks) object).onPermissionsDenied(requestCode, denied); } } // If 100% successful, call annotated methods if (!granted.isEmpty() && denied.isEmpty()) { //当所有的权限都被同意的时候,调用runAnnotatedMethods方法通知requestcode相等且申明AfterPermissionGranted的方法去执行 runAnnotatedMethods(object, requestCode); } } }
-
PermissionHelper中的requestPermissions()
public void requestPermissions(@NonNull String rationale, @StringRes int positiveButton, @StringRes int negativeButton, int requestCode, @NonNull String... perms) { if (shouldShowRationale(perms)) { //如果存在其中一个权限是禁止且没有选择“不再询问”就会调用到这里,弹出一个对话框提示为什么要申请这些权限的原因。 showRequestPermissionRationale( rationale, positiveButton, negativeButton, requestCode, perms); } else { //直接发出权限申请,首次申请或全部都被用户禁止的情况下才调用 directRequestPermissions(requestCode, perms);//这个方法其实调用的就是CompatContext.requestPermissions()方法 } }
代码太多了,建议对照源码进行学习,这里提供一个ER图给大家就会清晰明了。
-
![](https://img.haomeiwen.com/i2187866/20cb09ec72326933.jpg)