转载请标明出处:【顾林海的博客】

个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持!


前言

我们知道在Android中想要申请权限就需要在AndroidManifest配置文件中通过uses-permission标签设置申请的权限,通过这种方式申请权限固然方便,但在安全性方面却不高,比如开发者申请获取用户隐私的权限,这样用户在不知情的情况下获取到了用户的隐私,如何避免这种不安全的权限获取?从Android 6.0开始,Google将权限分为两类,分别是默认权限(Normal Permission)和危险权限(Dangerous Permission),默认权限在程序安装时授权,但危险权限需要用户在运行APP时手动授权,这里使用EasyPermissions库来简化运行时权限的操作。

EasyPermissions使用

/*** 权限管理类** @author glh*/
public abstract class BasePermissionsActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestPermissions(this);}public void requestPermissions(Context context) {String[] perms = {Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_PHONE_STATE};if (!EasyPermissions.hasPermissions(context, perms)) {EasyPermissions.requestPermissions((Activity) context, context.getResources().getString(R.string.camera_rationale),124, perms);}}@Overridepublic void onPermissionsGranted(int requestCode, List<String> perms) {ToastUtils.showToast(this, "用户授权成功");}@Overridepublic void onPermissionsDenied(int requestCode, List<String> perms) {/** 若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。* 这时候,需要跳转到设置界面去,让用户手动开启。*/if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {new AppSettingsDialog.Builder(this).setTitle("\"某APP\"权限提示").setRationale("\"某APP\"需要使用相关权限,是否打开设置").setPositiveButton("是").setNegativeButton("否").build().show();}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {//当从软件设置界面,返回当前程序时候case AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE:requestPermissions(this);break;}}@AfterPermissionGranted(value = 0x99)public void checkPermissions() {String[] perms = {Manifest.permission.CAMERA, Manifest.permission.CHANGE_WIFI_STATE};if (EasyPermissions.hasPermissions(this, perms)) {//已经授权后的操作} else {//没有授权后的操作EasyPermissions.requestPermissions(this, getString(R.string.camera_rationale),0x99, perms);}}}

使用EasyPermissions时,需要实现它的PermissionCallbacks接口,这个接口提供了三个方法,其中onPermissionsGranted方法是授权成功后的回调,onPermissionsDenied方法是授权失败的回调,onRequestPermissionsResult方法是android.support.v4.app的ActivityCompat中的公共接口,当我们进行权限的请求时onRequestPermissionsResult方法会返回相应的请求结果,通过这个权限回调,可以对相应的操作做出响应。

EasyPermissions提供了hasPermissions方法,第二个参数传入的是一个权限数组,该方法会判断APP是否对这些权限进行过授权,如果其中有一个权限并没有被授权,最终结果会返回false,这时可以通过EasyPermissions的requestPermissions方法进行权限授权,其中requestCode是请求码,可以根据对应的请求码在授权回调中进行相应的处理,第三个可变参数就是我们需要授权的相关权限。具体的权限回调处理上面只是给出一个模板,具体的操作还是根据相关业务场景来调整。

EasyPermissions还提供了注解形式来处理授权后对应的requestCode处理,具体使用如下:

    @AfterPermissionGranted(value = 0x99)public void checkPermissions() {String[] perms = {Manifest.permission.CAMERA, Manifest.permission.CHANGE_WIFI_STATE};if (EasyPermissions.hasPermissions(this, perms)) {//已经授权后的操作} else {//没有授权后的操作EasyPermissions.requestPermissions(this, getString(R.string.camera_rationale),0x99, perms);}}

源码解析

    public static boolean hasPermissions(Context context, @NonNull String... perms) {//Android版本判断if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {Log.w(TAG, "hasPermissions: API version < M, returning true by default");return true;}if (context == null) {throw new IllegalArgumentException("Can't check permissions for null context");}//对权限列表进行检查for (String perm : perms) {if (ContextCompat.checkSelfPermission(context, perm)!= PackageManager.PERMISSION_GRANTED) {return false;}}return true;}

hasPermissions方法比较简单,先是对Android版本进行判断,小于Android 6.0的版本直接检查通过,只有大于Android 6.0时才会对权限列表进行检查,这里使用了ContextCompat的checkSelfPermissions方法进行权限检查,检查的结果不为0说明未授权。

在Android源码中,权限的检查最终会调用到 ContextImp中的checkPermission方法,源码如下:

    @Overridepublic int checkPermission(String permission, int pid, int uid) {if (permission == null) {throw new IllegalArgumentException("permission is null");}try {return ActivityManagerNative.getDefault().checkPermission(permission, pid, uid);} catch (RemoteException e) {return PackageManager.PERMISSION_DENIED;}}

在ContextImp中的checkPermission方法中,调用了ActivityManagerNative的getDefault()方法来获取ActivityManagerService的对象。ActivityManagerService 的职责是管理着应用程序中创建的所有组件(Activity、Service等),实现组件的管理,每个组件的状态变化都需要通知AMS,组件间的跨进程通信(IPC)是由ActivityManagerService 来操作的。

ActivityManagerNative中的getDefault方法如下:

    static public IActivityManager getDefault() {return gDefault.get();}private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {protected IActivityManager create() {IBinder b = ServiceManager.getService("activity");if (false) {Log.v("ActivityManager", "default service binder = " + b);}IActivityManager am = asInterface(b);if (false) {Log.v("ActivityManager", "default service = " + am);}return am;}};static public IActivityManager asInterface(IBinder obj) {if (obj == null) {return null;    }IActivityManager in =(IActivityManager) obj.queryLocalInterface(descriptor);if (in != null) {return in;}return new ActivityManagerProxy(obj);}

在上面的create方法中通过ServiceManager.getService(“activity”)获取到ActivityManagerService对象,ActivityManagerService继承了 ActivityManagerNative,而 ActivityManagerNative继承了Binder并实现了IActivityManager接口,因此ActivityManagerService也是一个Binder对象,在asInterface方法中创建远程代理ActvityManagerProxy对象,经过一系列调用,最终调用ActivityManagerService的checkPermission方法。

    int checkComponentPermission(String permission, int pid, int uid,int owningUid, boolean exported) {// We might be performing an operation on behalf of an indirect binder// invocation, e.g. via {@link #openContentUri}.  Check and adjust the// client identity accordingly before proceeding.Identity tlsIdentity = sCallerIdentity.get();if (tlsIdentity != null) {Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"+ tlsIdentity.pid + "," + tlsIdentity.uid + "}");uid = tlsIdentity.uid;pid = tlsIdentity.pid;}if (pid == MY_PID) {return PackageManager.PERMISSION_GRANTED;}return ActivityManager.checkComponentPermission(permission, uid,owningUid, exported); // 调用ActivityManager的checkComponentPermission来检查权限}@Overridepublic int checkPermission(String permission, int pid, int uid) {if (permission == null) {return PackageManager.PERMISSION_DENIED;}// 调用checkComponentPermission函数来检查权限return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);}

调用ActivityManager的checkComponentPermission方法:

    ./base/core/java/android/app/ActivityManager.javapublic static int checkComponentPermission(String permission, int uid,int owningUid, boolean exported) {........try {// 调用AppGlobals的getPackageManager()函数返回IPackageManager对象return AppGlobals.getPackageManager().checkUidPermission(permission, uid);} catch (RemoteException e) {// Should never happen, but if it does... deny!Slog.e(TAG, "PackageManager is dead?!?", e);}return PackageManager.PERMISSION_DENIED;}
    ./base/core/java/android/app/AppGlobals.javapublic static IPackageManager getPackageManager() {return ActivityThread.getPackageManager(); // 调用ActivityThread的getPackageManager()函数}
    ./base/core/java/android/app/ActivityThread.javapublic static IPackageManager getPackageManager() {if (sPackageManager != null) {//Slog.v("PackageManager", "returning cur default = " + sPackageManager);return sPackageManager;}IBinder b = ServiceManager.getService("package");//Slog.v("PackageManager", "default service binder = " + b);sPackageManager = IPackageManager.Stub.asInterface(b);//Slog.v("PackageManager", "default service = " + sPackageManager);return sPackageManager;}

AppGlobals的getPackageManager方法中,通过ActivityThread的getPackageManager方法获取PackageManageService对象。
接着通过PackageManageService的checkUidPermission方法来检查权限。

    ./base/services/core/java/com/android/server/pm/PackageManagerService.java@Overridepublic int checkUidPermission(String permName, int uid) {synchronized (mPackages) {Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));if (obj != null) {GrantedPermissions gp = (GrantedPermissions) obj;if (gp.grantedPermissions.contains(permName)) {return PackageManager.PERMISSION_GRANTED;}} else {HashSet<String> perms = mSystemPermissions.get(uid);if ( perms != null &;&;perms.contains(permName)){return PackageManager.PERMISSION_GRANTED;}}}return PackageManager.PERMISSION_DENIED;}

PackageManageService的checkUidPermission方法中,通过Settings的getUserIdLPr方法根据UID获取权限列表获,如果存在授权的权限返回PackageManager.PERMISSION_GRANTED,表示授权成功。

hasPermissions方法算是分析完毕了,这里在分析该方法的同时也梳理了权限检查的流程。

在分析EasyPermissions的requestPermissions方法前,先介绍一下几个重要的辅助类。

PermissionHelper是一个抽象类(模板方法),内部定义了几个抽象方法:

@RestrictTo(RestrictTo.Scope.LIBRARY)
public abstract class PermissionHelper<T> {.....public abstract void directRequestPermissions(int requestCode, @NonNull String... perms);public abstract boolean shouldShowRequestPermissionRationale(@NonNull String perm);public abstract void showRequestPermissionRationale(@NonNull String rationale,@StringRes int positiveButton,@StringRes int negativeButton,int requestCode,@NonNull String... perms);public abstract Context getContext();}

shouldShowRequestPermissionRationale方法:如果应用之前请求过此权限但用户拒绝了请求,并在权限请求系统对话框中没有选择了 Don’t ask again 选项,此方法将返回 true。如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false。

directRequestPermissions方法:进行权限请求。

showRequestPermissionRationale方法:显示权限弹框,具体逻辑有子类实现。

getContext方法:获取对应的上下文。

很显然PermissionHelper是权限操作的抽象类,具体实现由子类来实现,请求权限时,可以在Activity、android.support.v4.app.Fragment或是android.app.Fragment中执行请求,PermissionHelper提供了创建这几种情况下的实现类。

@NonNull
public static PermissionHelper newInstance(Activity host) {if (Build.VERSION.SDK_INT < 23) {return new LowApiPermissionsHelper(host);}return new ActivityPermissionHelper(host);
}@NonNull
public static PermissionHelper newInstance(Fragment host) {if (Build.VERSION.SDK_INT < 23) {return new LowApiPermissionsHelper(host);}return new SupportFragmentPermissionHelper(host);
}@NonNull
public static PermissionHelper newInstance(android.app.Fragment host) {if (Build.VERSION.SDK_INT < 23) {return new LowApiPermissionsHelper(host);}return new FrameworkFragmentPermissionHelper(host);
}

如果权限授权在小于Android 6.0的版本时,会创建LowApiPermissionsHelper对象,LowApiPermissionsHelper实现了PermissionHelper的几个抽象方法。

class LowApiPermissionsHelper extends PermissionHelper<Object> {public LowApiPermissionsHelper(@NonNull Object host) {super(host);}@Overridepublic void directRequestPermissions(int requestCode, @NonNull String... perms) {throw new IllegalStateException("Should never be requesting permissions on API < 23!");}@Overridepublic boolean shouldShowRequestPermissionRationale(@NonNull String perm) {return false;}@Override@SuppressLint("NewApi")public void showRequestPermissionRationale(@NonNull String rationale,int positiveButton,int negativeButton,int requestCode,@NonNull String... perms) {throw new IllegalStateException("Should never be requesting permissions on API < 23!");}@Overridepublic Context getContext() {return null;}
}

LowApiPermissionsHelper的几个方法都是空的,这是因为在API小于23时,不需要对运行时权限做处理。

ActivityPermissionHelper和FrameworkFragmentPermissionHelper针对的是在android.app包的Actvity和Fragment。

ActivityPermissionHelper:

class ActivityPermissionHelper extends BaseFrameworkPermissionsHelper<Activity> {public ActivityPermissionHelper(Activity host) {super(host);}@Override@SuppressLint("NewApi")public FragmentManager getFragmentManager() {return getHost().getFragmentManager();}@Overridepublic void directRequestPermissions(int requestCode, @NonNull String... perms) {ActivityCompat.requestPermissions(getHost(), perms, requestCode);}@Overridepublic boolean shouldShowRequestPermissionRationale(@NonNull String perm) {return ActivityCompat.shouldShowRequestPermissionRationale(getHost(), perm);}@Overridepublic Context getContext() {return getHost();}
}

FrameworkFragmentPermissionHelper:

class FrameworkFragmentPermissionHelper extends BaseFrameworkPermissionsHelper<Fragment> {public FrameworkFragmentPermissionHelper(@NonNull Fragment host) {super(host);}@Override@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)public FragmentManager getFragmentManager() {return getHost().getChildFragmentManager();}@Override@SuppressLint("NewApi")public void directRequestPermissions(int requestCode, @NonNull String... perms) {getHost().requestPermissions(perms, requestCode);}@Override@SuppressLint("NewApi")public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {return getHost().shouldShowRequestPermissionRationale(perm);}@Override@SuppressLint("NewApi")public Context getContext() {return getHost().getActivity();}
}

ActivityPermissionHelper和FrameworkFragmentPermissionHelper实现逻辑都差不多,在Activity中请求权限会通过ActivityCompat的requestPermissions方法进行请求,在Fragment中会通过Fragment的requestPermissions方法进行权限请求。并且这两个Helper类都继承了BaseFrameworkPermissionsHelper类。

    public abstract class BaseFrameworkPermissionsHelper<T> extends PermissionHelper<T> {public BaseFrameworkPermissionsHelper(@NonNull T host) {super(host);}public abstract FragmentManager getFragmentManager();@Override@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)public void showRequestPermissionRationale(@NonNull String rationale,int positiveButton,int negativeButton,int requestCode,@NonNull String... perms) {RationaleDialogFragment.newInstance(positiveButton, negativeButton, rationale, requestCode, perms).show(getFragmentManager(), RationaleDialogFragment.TAG);}}

BaseFrameworkPermissionsHelper类非常简单,就两个方法,其中一个抽象方法getFragmentManager获取FragmentManager对象,还有一个实现了PermissionHelper类中的抽象方法showRequestPermissionRationale,showRequestPermissionRationale方法会显示一个权限弹框。
具体的子类实现在PermissionHelper中已经介绍过,继续看support.v4.app包的下Fragment的权限操作(SupportFragmentPermissionHelper)。

 class SupportFragmentPermissionHelper extends BaseSupportPermissionsHelper<Fragment> {public SupportFragmentPermissionHelper(@NonNull Fragment host) {super(host);}@Overridepublic FragmentManager getSupportFragmentManager() {return getHost().getChildFragmentManager();}@Overridepublic void directRequestPermissions(int requestCode, @NonNull String... perms) {getHost().requestPermissions(perms, requestCode);}@Overridepublic boolean shouldShowRequestPermissionRationale(@NonNull String perm) {return getHost().shouldShowRequestPermissionRationale(perm);}@Overridepublic Context getContext() {return getHost().getActivity();}}

SupportFragmentPermissionHelper的逻辑与之前两个Helper类一样,不需要再介绍了。

继续分析requestPermissions方法,EasyPermissions提供了多个requestPermissions方法:

    public static void requestPermissions(@NonNull Activity host, @NonNull String rationale,int requestCode, @NonNull String... perms) {requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,requestCode, perms);}public static void requestPermissions(@NonNull Fragment host, @NonNull String rationale,int requestCode, @NonNull String... perms) {requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,requestCode, perms);}public static void requestPermissions(@NonNull android.app.Fragment host, @NonNull String rationale,int requestCode, @NonNull String... perms) {requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,requestCode, perms);}

这个三个requestPermissions方法应该很熟悉了,权限弹框按钮的文案是默认的,要想设置权限弹框的文案,可以调用以下三种requestPermissions方法。

    public static void requestPermissions(@NonNull Activity host, @NonNull String rationale,@StringRes int positiveButton, @StringRes int negativeButton,int requestCode, @NonNull String... perms) {requestPermissions(PermissionHelper.newInstance(host), rationale,positiveButton, negativeButton,requestCode, perms);}public static void requestPermissions(@NonNull Fragment host, @NonNull String rationale,@StringRes int positiveButton, @StringRes int negativeButton,int requestCode, @NonNull String... perms) {requestPermissions(PermissionHelper.newInstance(host), rationale,positiveButton, negativeButton,requestCode, perms);}public static void requestPermissions(@NonNull android.app.Fragment host, @NonNull String rationale,@StringRes int positiveButton, @StringRes int negativeButton,int requestCode, @NonNull String... perms) {requestPermissions(PermissionHelper.newInstance(host), rationale,positiveButton, negativeButton,requestCode, perms);}

通过一系列调用,最后都是都用以下方法:

    private static void requestPermissions(@NonNull PermissionHelper helper, @NonNull String rationale,@StringRes int positiveButton, @StringRes int negativeButton,int requestCode, @NonNull String... perms) {//进行权限检查if (hasPermissions(helper.getContext(), perms)) {notifyAlreadyHasPermissions(helper.getHost(), requestCode, perms);return;}//请求权限helper.requestPermissions(rationale, positiveButton,negativeButton, requestCode, perms);}

方法第一个参数PermissionHelper就是前三个requestPermissions方法针对app包和suppor包下的Activity和Fragment权限操作类,也就是前面提到的那几个Helper类。在请求权限之前会检查之前权限有没有授权过,如果授权过调用notifyAlreadyHasPermissions方法。

    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);}

notifyAlreadyHasPermissions方法非常简单,授权成功后将 grantResults 数组全部赋值未PackageManager.PERMISSION_GRANTED,再通过onRequestPermissionsResult方法将结果回调给客户端。继续看requestPermissions方法中的helper.requestPermissions方法,该方法就是调用app和support下Activity和Fragment的
shouldShowRequestPermissionRationale方法,该方法就是Android进行权限授权的方法,当进行授权操作后会回调onRequestPermissionsResult方法。如下:

    @Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);}

拿到授权结果后通过EasyPermissions的onRequestPermissionsResult方法进行结果回调,onRequestPermissionsResult方法如下。

    public static void onRequestPermissionsResult(int requestCode,@NonNull String[] permissions,@NonNull int[] grantResults,@NonNull Object... receivers) {//授权成功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);}for (Object object : receivers) {// 授权成功if (!granted.isEmpty()) {if (object instanceof EasyPermissions.PermissionCallbacks) {((EasyPermissions.PermissionCallbacks) object).onPermissionsGranted(requestCode, granted);}}//未授权if (!denied.isEmpty()) {if (object instanceof EasyPermissions.PermissionCallbacks) {((EasyPermissions.PermissionCallbacks) object).onPermissionsDenied(requestCode, denied);}}//全部授权通过 if (!granted.isEmpty() && denied.isEmpty()) {runAnnotatedMethods(object, requestCode);}}}

方法中granted集合用于保存授权成功的权限,denied集合保存的是未授权或授权失败的权限,接着通过循环遍历权限请求类,对授权成功和未成功集合做判断,如果granted集合不为空说明授权成功,会回调onPermissionsGranted方法,如果denied集合不为空,说明有权限未授权成功,会回调onPermissionsDenied方法,当然如果granted集合不为空并且denied集合为空,说明百分百之一百授权成功,这时调用runAnnotatedMethods方法。

    private static void runAnnotatedMethods(@NonNull Object object, int requestCode) {Class clazz = object.getClass();if (isUsingAndroidAnnotations(object)) {clazz = clazz.getSuperclass();}while (clazz != null) {//遍历请求权限类中的方法for (Method method : clazz.getDeclaredMethods()) {//判断是否使用了AfterPermissionGranted注解if (method.isAnnotationPresent(AfterPermissionGranted.class)) {AfterPermissionGranted ann = method.getAnnotation(AfterPermissionGranted.class);if (ann.value() == requestCode) {//无参方法if (method.getParameterTypes().length > 0) {throw new RuntimeException("Cannot execute method " + method.getName() + " because it is non-void method and/or has input parameters.");}try {// Make method accessible if privateif (!method.isAccessible()) {method.setAccessible(true);}//反射调用AfterPermissionGranted注解的方法method.invoke(object);} catch (IllegalAccessException e) {Log.e(TAG, "runDefaultMethod:IllegalAccessException", e);} catch (InvocationTargetException e) {Log.e(TAG, "runDefaultMethod:InvocationTargetException", e);}}}}//获取父类的Classclazz = clazz.getSuperclass();}}

该方法主要对AfterPermissionGranted注解的方法进行处理,通过遍历请求权限类的方法,如果是通过AfterPermissionGranted注解的方法并且是无参的,通过请求的requestCode与该注解方法的value一样,就通过反射执行该方法,告之请求类权限请求成功。这里稍微提下,一开始会执行isUsingAndroidAnnotations方法进行判断是否获取直接继承父类的class,isUsingAndroidAnnotations方法如下:

    private static boolean isUsingAndroidAnnotations(@NonNull Object object) {if (!object.getClass().getSimpleName().endsWith("_")) {return false;}try {Class clazz = Class.forName("org.androidannotations.api.view.HasViews");return clazz.isInstance(object);} catch (ClassNotFoundException e) {return false;}}

其实这个方法也就是判断是否使用了 AndroidAnnotations这个注解框架,关于这个框架大家可以自行网上搜索。

之前看了onRequestPermissionsResult方法,也就是授权后的结果回调,现在看下权限请求的逻辑。

//请求权限helper.requestPermissions(rationale, positiveButton,negativeButton, requestCode, perms);

调用了PermissionHelper的requestPermissions方法。

    public boolean shouldShowRationale(@NonNull String... perms){for (String perm:perms){// 上次弹出权限点击了禁止(但没有勾选“下次不在询问”)if(shouldShowRequestPermissionRationale(perm)){return true;}}//第一次打开App时或者上次选择禁止并勾选“下次不在询问”return false;}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{//第一次打开App时或者上次选择禁止并勾选“下次不在询问”//进行权限请求directRequestPermissions(requestCode,perms);}}

requestPermissions方法中,当上次弹出权限点击了禁止(但没有勾选“下次不在询问”),这时需要弹出权限弹框,当第一次打开App时或者上次选择禁止并勾选“下次不在询问”,这时进行权限请求。shouldShowRequestPermissionRationale、showRequestPermissionRationale和directRequestPermissions都是抽象方法,具体实现由子类实现,关于这三个方法在上面已经介绍过了。
结合上面的一系列分析,权限请求后,通过回调onPermissionsGranted和onPermissionsDenied方法告之请求类请求结果,比如当我们选择禁止并勾选“下次不在询问”时,这时请求授权失败,回调onPermissionsDenied方法:

    @Overridepublic void onPermissionsDenied(int requestCode, List<String> perms) {/*** 若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。* 这时候,需要跳转到设置界面去,让用户手动开启。*/if(requestCode==124) {if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {new AppSettingsDialog.Builder(this).setTitle("\"火链财经\"权限提示").setRationale("\"火链财经\"需要使用相关权限,是否打开设置").setPositiveButton("是").setNegativeButton("否").build().show();} else {requestPermissions(this);}}}

可以针对具体业务进行操作,就像上面若是在权限弹窗中,用户勾选了’NEVER ASK AGAIN.‘或者’不在提示’,且拒绝权限。 这时候,需要跳转到设置界面去,让用户手动开启。否则再次请求授权。
到这里EasyPermissions分析分析完毕。

Android之EasyPermissions源码解析相关推荐

  1. http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

    http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

  2. BAT高级架构师合力熬夜15天,肝出了这份PDF版《Android百大框架源码解析》,还不快快码住。。。

    前言 为什么要阅读源码? 现在中高级Android岗位面试中,对于各种框架的源码都会刨根问底,从而来判断应试者的业务能力边际所在.但是很多开发者习惯直接搬运,对各种框架的源码都没有过深入研究,在面试时 ...

  3. Android通知系统源码解析

    Android通知系统源码解析 1. 概述 2. 流程图 2.1. 发送通知流程图 3. 源码解析 3.1. 使用通知--APP进程 3.1.1. 创建通知: 3.1.2. 发送(更新)通知: 3.1 ...

  4. Android Gradle Plugin 源码解析(上)

    一.源码依赖 本文基于: android gradle plugin版本: com.android.tools.build:gradle:2.3.0 gradle 版本:4.1 Gradle源码总共3 ...

  5. Android之AsyncTask源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! ##前言 AsyncTask是一种轻量级的异步任务类,内部封装了Thread和Ha ...

  6. Android之DiskLruCache源码解析

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/73863258 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  7. Android Hawk的源码解析,一款基于SharedPreferences的存储框架

    转载请标注:http://blog.csdn.net/friendlychen/article/details/76218033 一.概念 SharedPreferences的使用大家应该非常熟悉啦. ...

  8. Android之LocalBroadcastManager源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! 前言 广播想必大家都不陌生,日常开发中同一个APP中的多个进程之间需要进行传输信息 ...

  9. android debug database 源码解析

    我们今天分析下android debug database 的源码: 项目地址: https://github.com/amitshekhariitbhu/Android-Debug-Database ...

最新文章

  1. 人类应鼎力进行探索的35种颠覆性技术
  2. 远程连接MySQL慢的原因及解决
  3. javascript页面登录代码_自己动手做一个很酷的登录页面
  4. C#语法基础之第三节
  5. 使用Spring Boot和H2可以完全工作的原型
  6. 第十八期:网页禁止复制粘贴怎么办?教你六招轻松搞定
  7. 转 php 观察者模式
  8. 从Hadoop到云原生,谈如何消除程序员35岁危机
  9. 《软技能—代码之外的生存指南》
  10. linux压缩比,Linux下各压缩方式测试(压缩率和使用时间)
  11. WKWebView终极指南
  12. Excel导入SQL datetime的处理
  13. java fckeditor下载_FCKEditor的使用
  14. 非负矩阵分解 NMF 总结
  15. JS 实现列表移动(JQuery实现)
  16. Windows10 Windows Store安装 应用商店重新安装
  17. 自己计算机设置盘密码怎么操作,电脑硬盘,教您电脑硬盘怎么设置密码
  18. 学习自旋电子学的笔记00:杂谈(闲话) OOMMF软件的安装
  19. 视频教程-移动端Web开发-JavaScript
  20. Linux操作系统PS命令详细 解析

热门文章

  1. setInterval和setTimeout的区别
  2. bzoj3171: [Tjoi2013]循环格(费用流)
  3. TagCloudView云标签的灵活运用
  4. 用Docker镜像搭建ELK环境
  5. 你还记得当初为什么进入IT行业吗?
  6. ubuntu server 改变系统语言支持中文
  7. SpringBoot配置属性之Server
  8. android项目中记录ListView滚动停止位置与设置显示位置
  9. android 听筒播放声音
  10. HLG 1349 Graph [floyed]