Google-EasyPermissions

来源:互联网 发布:楚留香1979源码 编辑:程序博客网 时间:2024/06/05 09:24

Github: easypermissions 分析版本: 962b99d

EasyPermissions 是一个在 Android M 或者更高版本的上使用去简化系统权限逻辑的开源库。

使用

添加依赖到 Gradle :

dependencies {  compile 'pub.devrel:easypermissions:0.1.5'}

准备

在使用 EasyPermissions 之前,需要在 Activity 或者 Fragment 中实现EasyPermissions.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) {        // 权限被授予        // ...    }    @Override    public void onPermissionsDenied(int requestCode, List<String> list) {        // 权限被拒绝        // ...    }}

请求权限

  • 使用 EasyPermissions#hasPermissions(...) 去判断 app 是否已经有权限了。该方法的最后个参数是可变数组形式的,所以可以一次性查询多个权限。

  • 使用 EasyPermissions#requestPermissions 去请求权限。该方法在请求权限的同时有必要的话会显示使用权限理由。 requestCode 对于该方法来说必须是唯一的,同时最后个参数也是可变数组形式的,所以可以一次性请求多个权限。

  • 使用 AfterPermissioonGranted 注解。这是可选的,但是提供出来是为了方便。如果所有的请求的权限都被授予了,被注解的方法将会被执行,这样做是为了简化通常的请求权限成功之后再调用方法的流程。同时也可以在onPermissionsGranted 的回调中添加逻辑操作:

    @AfterPermissionGranted(RC_CAMERA_AND_WIFI)private void methodRequiresTwoPermission() {  String[] perms = {Manifest.permission.CAMERA, Manifest.permission.CHANGE_WIFI_STATE};  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_wifi_rationale),                    RC_CAMERA_AND_WIFI, perms);  }}

源码

hasPermissions

public class EasyPermissions {  /**     * Check if the calling context has a set of permissions.     *     * @param context the calling context.     * @param perms   one ore more permissions, such as {@code android.Manifest.permission.CAMERA}.     * @return true if all permissions are already granted, false if at least one permission     * is not yet granted.     */    public static boolean hasPermissions(Context context, String... perms) {        for (String perm : perms) {            //通过ContextCompat#checkSelfPermission判断            boolean hasPerm = (ContextCompat.checkSelfPermission(context, perm) == PackageManager.PERMISSION_GRANTED);            if (!hasPerm) {                return false;            }        }        return true;    }}

该方法的作用是判断是否授予了权限,通过 v4 的ContextCompat#checkSelfPermission 来判断,在返回结果的时候,如果所有的请求的权限都是被授予了的话,就返回 true ,否则返回 false 。

requestPermissions

public class EasyPermissions {  public static void requestPermissions(final Object object, String rationale,                                          final int requestCode, final String... perms) {        requestPermissions(object, rationale,                android.R.string.ok,                android.R.string.cancel,                requestCode, perms);    }    /**     * Request a set of permissions, showing rationale if the system requests it.     *     * @param object         Activity or Fragment requesting permissions. Should implement     *                       {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}     *                       or     *                       {@link android.support.v13.app.FragmentCompat.OnRequestPermissionsResultCallback}     * @param rationale      a message explaining why the application needs this set of permissions, will     *                       be displayed if the user rejects the request the first time.     * @param positiveButton custom text for positive button     * @param negativeButton custom text for negative button     * @param requestCode    request code to track this request, must be < 256.     * @param perms          a set of permissions to be requested.     */    public static void requestPermissions(final Object object, String rationale, @StringRes int positiveButton, @StringRes int negativeButton, final int requestCode, final String... perms) {        //判断传入参数是否合适        checkCallingObjectSuitability(object);        //拿到PermissionCallbacks对象        final PermissionCallbacks callbacks = (PermissionCallbacks) object;        boolean shouldShowRationale = false;        for (String perm : perms) {            //是否需要显示理由            shouldShowRationale = shouldShowRationale || shouldShowRequestPermissionRationale(object, perm);        }        //如果需要的话,显示dialog进行显示        if (shouldShowRationale) {            AlertDialog dialog = new AlertDialog.Builder(getActivity(object))                    .setMessage(rationale)                    .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {                        @Override                        public void onClick(DialogInterface dialog, int which) {                            //用户同意了,进行系统权限申请操作                            executePermissionsRequest(object, perms, requestCode);                        }                    })                    .setNegativeButton(negativeButton, new DialogInterface.OnClickListener() {                        @Override                        public void onClick(DialogInterface dialog, int which) {                            // act as if the permissions were denied                            //没有同意的话,回调出去                            callbacks.onPermissionsDenied(requestCode, Arrays.asList(perms));                        }                    }).create();            dialog.show();        } else {            //不需要显示理由,直接进行权限请求操作            executePermissionsRequest(object, perms, requestCode);        }    }    /**     * 判断传入的对象合适合法,判断规则是传入的object是不是 Fragment 或者 Activity 类,同时是否实现了 PermissionCallbacks     *     * @param object     */    private static void checkCallingObjectSuitability(Object object) {        // Make sure Object is an Activity or Fragment        if (!((object instanceof Fragment) || (object instanceof Activity))) {            throw new IllegalArgumentException("Caller must be an Activity or a Fragment.");        }        // Make sure Object implements callbacks        if (!(object instanceof PermissionCallbacks)) {            throw new IllegalArgumentException("Caller must implement PermissionCallbacks.");        }    }    /**     * 是否需要显示请求权限理由     *     * @param object     * @param perm     * @return 需要的话返回true,不需要的话返回false     */    private static boolean shouldShowRequestPermissionRationale(Object object, String perm) {        if (object instanceof Activity) {            return ActivityCompat.shouldShowRequestPermissionRationale((Activity) object, perm);        } else if (object instanceof Fragment) {            return ((Fragment) object).shouldShowRequestPermissionRationale(perm);        } else {            return false;        }    }    /**     * 执行权限请求操作     *     * @param object     * @param perms     * @param requestCode     */    private static void executePermissionsRequest(Object object, String[] perms, int requestCode) {        //判断传入参数是否合适        checkCallingObjectSuitability(object);        if (object instanceof Activity) {            ActivityCompat.requestPermissions((Activity) object, perms, requestCode);        } else if (object instanceof Fragment) {            ((Fragment) object).requestPermissions(perms, requestCode);        }    }    /**     * 拿到Activity对象     *     * @param object     * @return     */    private static Activity getActivity(Object object) {        if (object instanceof Activity) {            return ((Activity) object);        } else if (object instanceof Fragment) {            return ((Fragment) object).getActivity();        } else {            return null;        }    }}

requestPermission() 有两个不同参数的实现,区别在于弹出的对话框中的 positive 和 negative 文字是否自定义。传入的 Object 对象得是 Activity 或者Fragment 并且实现了android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback 或者android.support.v13.app.FragmentCompat.OnRequestPermissionsResultCallback 。然后通过 shouldShowRequestPermissionRationale 方法去判断是否需要显示请求权限的理由,当申请的权限中有一个需要显示请求权限的话,那么就会弹出 dialog 。如果需要弹出 dialog ,用户取消的话那么直接回调出去,没有取消的话就让系统进行权限的申请。

走到这里 requestPermission() 的任务完成了,那么当用户同意或者不同意授予请求的权限,会进入到android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback 或者android.support.v13.app.FragmentCompat.OnRequestPermissionsResultCallback 。再回过头看看 使用#准备 中的内容,发现在OnRequestPermissionsResultCallback 方法中调用了EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); 。

onRequestPermissionsResult

public class EasyPermissions {    /**     * Handle the result of a permission request, should be called from the calling Activity's     * {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}     * method.     * <p/>     * If any permissions were granted or denied, the Activity will receive the appropriate     * callbacks through {@link PermissionCallbacks} and methods annotated with     * {@link AfterPermissionGranted} will be run if appropriate.     *     * @param requestCode  requestCode argument to permission result callback.     * @param permissions  permissions argument to permission result callback.     * @param grantResults grantResults argument to permission result callback.     * @param object       the calling Activity or Fragment.     * @throws IllegalArgumentException if the calling Activity does not implement     *                                  {@link PermissionCallbacks}.     */    public static void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults, Object object) {        //判断传入参数是否合适        checkCallingObjectSuitability(object);        //合适的话直接强转,不合适抛异常        PermissionCallbacks callbacks = (PermissionCallbacks) object;        // Make a collection of granted and denied permissions from the request.        ArrayList<String> granted = new ArrayList<>();        ArrayList<String> denied = new ArrayList<>();      //判断返回的权限数据,如果权限被授予,添加到granted的List中,没有被授予则添加到denied的List中        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);            }        }        // Report granted permissions, if any.      //进行回调        if (!granted.isEmpty()) {            // Notify callbacks            callbacks.onPermissionsGranted(requestCode, granted);        }        // Report denied permissions, if any.      //进行回调        if (!denied.isEmpty()) {            callbacks.onPermissionsDenied(requestCode, denied);        }        // If 100% successful, call annotated methods      //如果所有请求的权限都被授予,则调用被注解的方法        if (!granted.isEmpty() && denied.isEmpty()) {            runAnnotatedMethods(object, requestCode);        }    }}

onRequestPermissionsResult() 方法处理系统请求权限之后返回的数据,将授予和没有授予的权限通过 PermissionCallbacks 分别回调出去。最后如果请求的权限都被授予的话,则自动去调用被注解了的方法。

runAnnotatedMethods

public class EasyPermissions {    /**     * 通过反射的方式调用被注解了的方法     *     * @param object     * @param requestCode     */    private static void runAnnotatedMethods(Object object, int requestCode) {        Class clazz = object.getClass();        for (Method method : clazz.getDeclaredMethods()) {          //是否被AfterPermissionGranted注解了的方法            if (method.isAnnotationPresent(AfterPermissionGranted.class)) {                // Check for annotated methods with matching request code.                AfterPermissionGranted ann = method.getAnnotation(AfterPermissionGranted.class);              //requestCode和AfterPermissionGranted注解传入的requestCode相同的话                if (ann.value() == requestCode) {                    // Method must be void so that we can invoke it                  //必须是没有参数的方法                    if (method.getParameterTypes().length > 0) {                        throw new RuntimeException("Cannot execute non-void method " + method.getName());                    }                    try {                        // Make method accessible if private                      //如果是private的话,设置Accessible                        if (!method.isAccessible()) {                            method.setAccessible(true);                        }                      //调用                        method.invoke(object);                    } catch (IllegalAccessException e) {                        Log.e(TAG, "runDefaultMethod:IllegalAccessException", e);                    } catch (InvocationTargetException e) {                        Log.e(TAG, "runDefaultMethod:InvocationTargetException", e);                    }                }            }        }    }}

runAnnotatedMethods() 方法通过反射方法去调用被注解了的方法,同时这个方法得满足 requestCode 相同且方法没有参数。

AfterPermissionGranted

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface AfterPermissionGranted {    int value();}

这是一个 RUNTIME 的注解,常用使用方式就是通过反射的形式。

总结

EasyPermissions 通过注解的方式巧妙的减少了在成功请求权限之后的操作,减少的步奏是完成获取权限成功之后自动调用被注解的方法。简单的例子就如 demo 中的一样:

@AfterPermissionGranted(RC_CAMERA_PERM)public void cameraTask() {    if (EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA)) {        // Have permission, do the thing!        Toast.makeText(this, "TODO: Camera things", Toast.LENGTH_LONG).show();    } else {        // Ask for one permission        EasyPermissions.requestPermissions(this, getString(R.string.rationale_camera), RC_CAMERA_PERM, Manifest.permission.CAMERA);    }}

没有权限的情况下请求权限,请求完之后如果成功则又自动进入这个方法,进行 Toast 操作。

运行时权限

group:android.permission-group.CONTACTS  permission:android.permission.WRITE_CONTACTS  permission:android.permission.GET_ACCOUNTS  permission:android.permission.READ_CONTACTSgroup:android.permission-group.PHONE  permission:android.permission.READ_CALL_LOG  permission:android.permission.READ_PHONE_STATE  permission:android.permission.CALL_PHONE  permission:android.permission.WRITE_CALL_LOG  permission:android.permission.USE_SIP  permission:android.permission.PROCESS_OUTGOING_CALLS  permission:com.android.voicemail.permission.ADD_VOICEMAILgroup:android.permission-group.CALENDAR  permission:android.permission.READ_CALENDAR  permission:android.permission.WRITE_CALENDARgroup:android.permission-group.CAMERA  permission:android.permission.CAMERAgroup:android.permission-group.SENSORS  permission:android.permission.BODY_SENSORSgroup:android.permission-group.LOCATION  permission:android.permission.ACCESS_FINE_LOCATION  permission:android.permission.ACCESS_COARSE_LOCATIONgroup:android.permission-group.STORAGE  permission:android.permission.READ_EXTERNAL_STORAGE  permission:android.permission.WRITE_EXTERNAL_STORAGEgroup:android.permission-group.MICROPHONE  permission:android.permission.RECORD_AUDIOgroup:android.permission-group.SMS  permission:android.permission.READ_SMS  permission:android.permission.RECEIVE_WAP_PUSH  permission:android.permission.RECEIVE_MMS  permission:android.permission.RECEIVE_SMS  permission:android.permission.SEND_SMS  permission:android.permission.READ_CELL_BROADCASTS

同时也可以通过 adb shell pm list permissions -d -g 进行查看。

运行权限也分为了一组一组的,同时申请权限的时候也是按组来申请的,也就是说app 对 READ_CONTACTS 已经授权了,当你的 app 申请 WRITE_CONTACTS 时,系统会直接授权通过 。

注意

EasyPermissions 提供的 Fragment 是 v4 包的,如果要使用android.app.Fragment 的话就需要自己添加了。

0 0
原创粉丝点击