转载请标明出处:【顾林海的博客】
个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持!
我们知道在Android中想要申请权限就需要在AndroidManifest配置文件中通过uses-permission标签设置申请的权限,通过这种方式申请权限固然方便,但在安全性方面却不高,比如开发者申请获取用户隐私的权限,这样用户在不知情的情况下获取到了用户的隐私,如何避免这种不安全的权限获取?从Android 6.0开始,Google将权限分为两类,分别是默认权限(Normal Permission)和危险权限(Dangerous Permission),默认权限在程序安装时授权,但危险权限需要用户在运行APP时手动授权,这里使用EasyPermissions库来简化运行时权限的操作。
/**
* 权限管理类
*
* @author glh
*/
public abstract class BasePermissionsActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {
@Override
protected 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);
}
}
@Override
public void onPermissionsGranted(int requestCode, List perms) {
ToastUtils.showToast(this, "用户授权成功");
}
@Override
public void onPermissionsDenied(int requestCode, List perms) {
/*
* 若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。
* 这时候,需要跳转到设置界面去,让用户手动开启。
*/
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog
.Builder(this)
.setTitle("\"某APP\"权限提示")
.setRationale("\"某APP\"需要使用相关权限,是否打开设置")
.setPositiveButton("是")
.setNegativeButton("否")
.build()
.show();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
protected 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方法,源码如下:
@Override
public 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 gDefault = new Singleton() {
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来检查权限
}
@Override
public 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.java
public 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.java
public static IPackageManager getPackageManager() {
return ActivityThread.getPackageManager(); // 调用ActivityThread的getPackageManager()函数
}
./base/core/java/android/app/ActivityThread.java
public 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
@Override
public 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 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 {
.....
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
LowApiPermissionsHelper的几个方法都是空的,这是因为在API小于23时,不需要对运行时权限做处理。
ActivityPermissionHelper和FrameworkFragmentPermissionHelper针对的是在android.app包的Actvity和Fragment。
ActivityPermissionHelper:
class ActivityPermissionHelper extends BaseFrameworkPermissionsHelper {
public ActivityPermissionHelper(Activity host) {
super(host);
}
@Override
@SuppressLint("NewApi")
public FragmentManager getFragmentManager() {
return getHost().getFragmentManager();
}
@Override
public void directRequestPermissions(int requestCode, @NonNull String... perms) {
ActivityCompat.requestPermissions(getHost(), perms, requestCode);
}
@Override
public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
return ActivityCompat.shouldShowRequestPermissionRationale(getHost(), perm);
}
@Override
public Context getContext() {
return getHost();
}
}
FrameworkFragmentPermissionHelper:
class FrameworkFragmentPermissionHelper extends BaseFrameworkPermissionsHelper {
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 extends PermissionHelper {
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 {
public SupportFragmentPermissionHelper(@NonNull Fragment host) {
super(host);
}
@Override
public FragmentManager getSupportFragmentManager() {
return getHost().getChildFragmentManager();
}
@Override
public void directRequestPermissions(int requestCode, @NonNull String... perms) {
getHost().requestPermissions(perms, requestCode);
}
@Override
public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
return getHost().shouldShowRequestPermissionRationale(perm);
}
@Override
public 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方法。如下:
@Override
public 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 granted = new ArrayList<>();
//授权失败或没授权
List 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 private
if (!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);
}
}
}
}
//获取父类的Class
clazz = 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方法:
@Override
public void onPermissionsDenied(int requestCode, List 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分析分析完毕。