Android M-检查运行时权限-如何确定用户是否选中了“不再询问”?


307

据此:http : //developer.android.com/preview/features/runtime-permissions.html#coding应用程序可以检查运行时权限,如果尚未授予运行权限,则可以请求权限。然后将显示以下对话框:

在此处输入图片说明

万一用户拒绝重要许可,则imo应用程序应显示解释为何需要此许可以及拒绝影响如何。该对话框有两个选项:

  1. 重试(再次请求许可)
  2. 拒绝(未经该许可,该应用将可以运行)。

Never ask again但是,如果用户进行了检查,则不应显示带有说明的第二个对话框,特别是如果用户之前已经拒绝过一次。现在的问题是:我的应用程序如何知道用户是否已检查Never ask again?IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)没有给我该信息。

第二个问题是:Google是否计划在权限对话框中加入自定义消息,以解释应用程序为何需要权限?这样,就不会再有第二个对话框,它肯定会带来更好的用户体验。


9
“ Google是否计划在权限对话框中合并一条自定义消息,以说明应用程序为何需要该权限?” -在有关M权限系统的Google I | O演示中,我似乎想起有人在问答中提出的问题,答案是他们正在考虑这一问题。
CommonsWare 2015年

1
我自己没有进行测试,但是文档中提到了Activity.shouldShowRequestPermissionRationale(String):如果应用程序先前已请求此权限并且用户拒绝了此请求,则此方法返回true。这表明您可能应该向用户解释为什么需要许可。如果用户过去拒绝了权限请求,并在权限请求系统对话框中选择了“不再询问”选项,则此方法返回false。如果设备策略禁止应用拥有该权限,则该方法还返回false。
Fraid 2015年

1
@Fraid:看来他们在Android M的预览版#2中添加了此代码:developer.android.com/preview/support.html#preview2-notes,这可能正是我想要的。我目前无法测试,但下周将进行测试。如果它符合我的期望,则可以将其发布为答案并获得一定的声誉。同时,这可能会对其他人有所帮助:youtube.com/watch?
v=f17qe9vZ8RM

危险权限和特殊权限的示例: github.com/henrychuangtw/AndroidRuntimePermission
HenryChuang

1
对于开发人员来说,@ Alex更加困难,但这是肯定的,但是从用户角度来看,能够授予或拒绝特定权限是有意义的。我看到的主要问题是权限的粒度非常不一致,您最终要求的权限可能与您在应用程序中尝试执行的操作几乎没有任何关系(例如,当我想连接到Google云端硬盘,因为它需要用于身份验证的设备帐户列表,并且帐户权限属于联系人权限组的一部分)。
伊曼纽尔·莫克林

Answers:


341

Developer Preview 2对应用程序请求权限的方式进行了一些更改(另请参见http://developer.android.com/preview/support.html#preview2-notes)。

现在,第一个对话框如下所示:

在此处输入图片说明

没有“不再显示”复选框(与开发人员预览1不同)。如果用户拒绝该许可,并且该许可对于应用是必不可少的,则它可能会显示另一个对话框,以解释该应用请求该许可的原因,例如:

在此处输入图片说明

如果用户再次拒绝,则该应用程序应在绝对需要该权限的情况下关闭,或者继续使用功能受限的程序运行。如果用户重新考虑(并选择重试),则再次请求许可。这次提示如下:

在此处输入图片说明

第二次显示“不再询问”复选框。如果用户再次拒绝,并且勾选了该复选框,则不会再发生其他情况。可以通过使用Activity.shouldShowRequestPermissionRationale(String)来确定是否选中该复选框,例如:

if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {...

这就是Android文档所说的内容(https://developer.android.com/training/permissions/requesting.html):

为了帮助查找需要提供额外说明的情况,系统提供了Activity.shouldShowRequestPermissionRationale(String)方法。如果应用先前已请求此权限,并且用户拒绝了此请求,则此方法返回true。这表明您可能应该向用户解释为什么需要许可。

如果用户过去拒绝了权限请求,并在权限请求系统对话框中选择了“不再询问”选项,则此方法返回false。如果设备策略禁止应用拥有该权限,则该方法还返回false。

要知道用户是否拒绝“不再询问”,可以在用户未授予许可时再次检查onRequestPermissionsResult中的shouldShowRequestPermissionRationale方法。

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == REQUEST_PERMISSION) {
        // for each permission check if the user granted/denied them
        // you may want to group the rationale in a single dialog,
        // this is just an example
        for (int i = 0, len = permissions.length; i < len; i++) {
            String permission = permissions[i];
            if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
            // user rejected the permission
                boolean showRationale = shouldShowRequestPermissionRationale( permission );
                if (! showRationale) {
                    // user also CHECKED "never ask again"
                    // you can either enable some fall back,
                    // disable features of your app
                    // or open another dialog explaining
                    // again the permission and directing to
                    // the app setting
                } else if (Manifest.permission.WRITE_CONTACTS.equals(permission)) {
                    showRationale(permission, R.string.permission_denied_contacts);
                    // user did NOT check "never ask again"
                    // this is a good place to explain the user
                    // why you need the permission and ask if he wants
                    // to accept it (the rationale)
                } else if ( /* possibly check more permissions...*/ ) {
                }
            }
        }
    }
}

您可以使用以下代码打开您的应用设置:

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);

无法将用户直接发送到“授权”页面。


30
我将shouldShowRequestPermissionRationale()方法的返回值验证为false,以检查用户是否选择了“不再询问”。但是,当我请求许可时,我也第一次得到它的值为false。因此,我无法区分用户是否选择了“不再询问”复选框。请建议?
Sagar Trehan 2015年

32
根据我的理解,shouldShowRationalePermissionRationale()方法在以下三种情况下返回false:1.如果在请求权限之前第一次调用此方法。2.如果用户选择“不再询问”并拒绝许可。3.如果设备策略禁止该应用获得该许可,请执行以下操作
Sagar Trehan 2015年

24
一切都很好...但是我们开发人员确实需要知道用户是否说“不再询问”。我有一个不错的按钮来访问功能。用户第一次单击:应问理由?不,请允许。用户否认。用户再次单击按钮:基本原理?是的!显示基本原理,用户说好,然后拒绝,再也不问(好,他是个白痴,但用户经常是白痴)。稍后用户再次按下按钮,理由是什么?不,请允许,用户什么都不会发生。我确实需要一种方法来告诉用户:嘿,如果您想要此功能,请立即进入应用设置并授予权限。
Daniele Segato

4
太好了@EmanuelMoecklin,这比现在的Google文档要好:D
Daniele Segato 2015年

4
除非您请求许可,否则不会调用onRequestPermissionsResult。由于第一次请求权限时没有复选框“不再询问”,shouldShowRequestPermissionRationale将返回True(已请求许可,但不再询问)。因此,总是在用户第一次拒绝该权限时显示其基本原理,但是之后,仅当未选中该复选框时,才会显示该基本原理。
伊曼纽尔·莫克林

95

您可以签shouldShowRequestPermissionRationale()onRequestPermissionsResult()

应该ShowRequestPermissionRationale https://youtu.be/C8lUdPVSzDk?t=2m23s

检查中是否授予了权限onRequestPermissionsResult()。如果没有,请检查shouldShowRequestPermissionRationale()

  1. 如果此方法返回,true则说明为什么需要此特定权限。然后再次根据用户的选择requestPermissions()
  2. 如果返回,false则显示一条错误消息,表明未授予权限,应用无法继续进行或已禁用特定功能。

下面是示例代码。

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case STORAGE_PERMISSION_REQUEST:
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission was granted :)
                downloadFile();
            } else {
                // permission was not granted
                if (getActivity() == null) {
                    return;
                }
                if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    showStoragePermissionRationale();
                } else {
                    Snackbar snackbar = Snackbar.make(getView(), getResources().getString(R.string.message_no_storage_permission_snackbar), Snackbar.LENGTH_LONG);
                    snackbar.setAction(getResources().getString(R.string.settings), new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (getActivity() == null) {
                                return;
                            }
                            Intent intent = new Intent();
                            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                            Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
                            intent.setData(uri);
                            OrderDetailFragment.this.startActivity(intent);
                        }
                    });
                    snackbar.show();
                }
            }
            break;
    }
}

显然,谷歌地图正是这样做的位置许可。


感谢您提供图片和Youtube链接。它或多或少与我自己的答案相符。必须注意的是,当只有不具有shouldShowRequestPermissionRationale方法的开发者预览版1可用时,才提出问题。
伊曼纽尔·莫克林

我是android的新手,我想超越这个onRequestPermissionsResult()方法。但我收到错误,它必须实现一个超级类型方法。你能告诉我如何使用它

39

这是一种检查当前权限状态的好方法:

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({GRANTED, DENIED, BLOCKED_OR_NEVER_ASKED })
    public @interface PermissionStatus {}

    public static final int GRANTED = 0;
    public static final int DENIED = 1;
    public static final int BLOCKED_OR_NEVER_ASKED = 2;

    @PermissionStatus 
    public static int getPermissionStatus(Activity activity, String androidPermissionName) {
        if(ContextCompat.checkSelfPermission(activity, androidPermissionName) != PackageManager.PERMISSION_GRANTED) {
            if(!ActivityCompat.shouldShowRequestPermissionRationale(activity, androidPermissionName)){
                return BLOCKED_OR_NEVER_ASKED;
            }
            return DENIED;
        }
        return GRANTED;
    }

警告:在用户通过用户提示接受/拒绝权限之前(在sdk 23+设备上),在第一个应用启动时返回BLOCKED_OR_NEVER_ASKED。

更新:

现在,Android支持库似乎也有一个非常相似的类android.support.v4.content.PermissionChecker,其中包含一个checkSelfPermission()返回以下内容的类:

public static final int PERMISSION_GRANTED = 0;
public static final int PERMISSION_DENIED = -1;
public static final int PERMISSION_DENIED_APP_OP = -2;

1
对于首次启动,我将在共享首选项中存储一个布尔值。
Saeid Farivar

5
BLOCKED_OR_NEVER_ASKED如果尚未请求许可,则始终返回。
Saket

6
是的,这就是其被称为“ BLOCKED_OR_NEVER_ASKED”的原因,另请参阅最后一句话
Patrick Favre

3
android.content.pm已经定义PERMISSION_GRANTED = 0PERMISSION_DENIED = -1。也许设置BLOCKED_OR_NEVER_ASKED = PERMISSION_DENIED - 1或什么?
samis

有关处理警告的信息,请参见下面的mVck的答案。
samis

28

用户标记“不再询问”后,该问题将无法再次显示。但是可以向用户解释,他先前已拒绝许可,并且必须在设置中授予许可。并使用以下代码将其引用给设置:

@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults) {

    if (grantResults.length > 0
            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // now, you have permission go ahead
        // TODO: something

    } else {

        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                Manifest.permission.READ_CALL_LOG)) {
            // now, user has denied permission (but not permanently!)

        } else {

            // now, user has denied permission permanently!

            Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "You have previously declined this permission.\n" +
                "You must approve this permission in \"Permissions\" in the app settings on your device.", Snackbar.LENGTH_LONG).setAction("Settings", new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + BuildConfig.APPLICATION_ID)));

            }
        });
        View snackbarView = snackbar.getView();
        TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
        textView.setMaxLines(5);  //Or as much as you need
        snackbar.show();

        }

    }
    return;
}

在迁移到androidX时,您可以将com.google.android.material.R替换为android.support.design.R
Ridha Rezzag,

26

可能对某人有用:

我注意到的是,如果我们检查onRequestPermissionsResult()回调方法中的shouldShowRequestPermissionRationale()标志,它将仅显示两种状态

状态1:-返回true:-每当用户单击“拒绝”权限时(包括第一次)。

状态2:-返回假:-如果用户选择“不再询问”。

详细工作实例链接


2
这是检测用户是否选择了“不再询问”选项的正确方法。
穆罕默德·巴巴尔

嗯,这里的关键是您在中处理此问题onRequestPermissionsResult,而不是在实际请求许可时处理。
约书亚·品特

26

可以通过检查是否在回调方法中显示许可理由来确定onRequestPermissionsResult()。并且,如果您发现任何权限设置都不再询问,则可以请求用户从设置中授予权限。

我的完整实现将如下所示。它适用于单个多个权限请求。使用以下内容或直接使用我的图书馆。

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if(permissions.length == 0){
        return;
    }
    boolean allPermissionsGranted = true;
    if(grantResults.length>0){
        for(int grantResult: grantResults){
            if(grantResult != PackageManager.PERMISSION_GRANTED){
                allPermissionsGranted = false;
                break;
            }
        }
    }
    if(!allPermissionsGranted){
        boolean somePermissionsForeverDenied = false;
        for(String permission: permissions){
            if(ActivityCompat.shouldShowRequestPermissionRationale(this, permission)){
                //denied
                Log.e("denied", permission);
            }else{
                if(ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED){
                    //allowed
                    Log.e("allowed", permission);
                } else{
                    //set to never ask again
                    Log.e("set to never ask again", permission);
                    somePermissionsForeverDenied = true;
                }
            }
        }
        if(somePermissionsForeverDenied){
            final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
            alertDialogBuilder.setTitle("Permissions Required")
                    .setMessage("You have forcefully denied some of the required permissions " +
                            "for this action. Please open settings, go to permissions and allow them.")
                    .setPositiveButton("Settings", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                                    Uri.fromParts("package", getPackageName(), null));
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            startActivity(intent);
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    })
                    .setCancelable(false)
                    .create()
                    .show();
        }
    } else {
        switch (requestCode) {
            //act according to the request code used while requesting the permission(s).
        }
    }
}

hii @nabin我的要求是,当我单击下载按钮(下载pdf文件)时,该时间必须检查是否允许写许可,因此如何使用此代码!你能指导我吗
Rucha Bhatt Joshi

你好@RuchaBhatt看看我的图书馆。github.com/nabinbhandari/Android-Permissions
Nabin Bhandari

15

如果要检测所有“状态”(第一次被拒绝,刚刚被拒绝,仅被“不再询问”或永久被拒绝),则可以执行以下操作:

创建2个布尔值

private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;

在请求许可之前设置第一个:

beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

在onRequestPermissionsResult方法中设置第二个:

afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

使用以下“表”在onRequestPermissionsResult()中执行所需的任何操作(在确认您仍然没有权限之后):

// before after
// FALSE  FALSE  =  Was denied permanently, still denied permanently --> App Settings
// FALSE  TRUE   =  First time deny, not denied permanently yet --> Nothing
// TRUE   FALSE  =  Just been permanently denied --> Changing my caption to "Go to app settings to edit permissions"
// TRUE   TRUE   =  Wasn't denied permanently, still not denied permanently --> Nothing

除非您想在请求权限之前显示基本原理,否则在调用requestPermissions之前检查shouldShowRequestPermissionRationale是没有意义的。但是,直到用户拒绝许可后才显示基本原理,这似乎是当今大多数应用程序处理它的方式。
伊曼纽尔·莫克林

2
@EmanuelMoecklin,据我所知,这是检查它是否已被拒绝的唯一方法(通过在真值表中说明的前后检查),还是第一次拒绝(在我的情况下,我将用户重定向至应用设置(如果被永久拒绝)
mVck

1
// TRUE FALSE当用户在先前拒绝它之后允许许可时,也会发生这种情况。
samis

11

我遇到了同样的问题,我想出了办法。为了简化生活,我编写了一个util类来处理运行时权限。

public class PermissionUtil {
    /*
    * Check if version is marshmallow and above.
    * Used in deciding to ask runtime permission
    * */
    public static boolean shouldAskPermission() {
        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
    }
private static boolean shouldAskPermission(Context context, String permission){
        if (shouldAskPermission()) {
            int permissionResult = ActivityCompat.checkSelfPermission(context, permission);
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                return true;
            }
        }
        return false;
    }
public static void checkPermission(Context context, String permission, PermissionAskListener listener){
/*
        * If permission is not granted
        * */
        if (shouldAskPermission(context, permission)){
/*
            * If permission denied previously
            * */
            if (((Activity)context).shouldShowRequestPermissionRationale(permission)) {
                listener.onPermissionPreviouslyDenied();
            } else {
                /*
                * Permission denied or first time requested
                * */
if (PreferencesUtil.isFirstTimeAskingPermission(context, permission)) {
                    PreferencesUtil.firstTimeAskingPermission(context, permission, false);
                    listener.onPermissionAsk();
                } else {
                    /*
                    * Handle the feature without permission or ask user to manually allow permission
                    * */
                    listener.onPermissionDisabled();
                }
            }
        } else {
            listener.onPermissionGranted();
        }
    }
/*
    * Callback on various cases on checking permission
    *
    * 1.  Below M, runtime permission not needed. In that case onPermissionGranted() would be called.
    *     If permission is already granted, onPermissionGranted() would be called.
    *
    * 2.  Above M, if the permission is being asked first time onPermissionAsk() would be called.
    *
    * 3.  Above M, if the permission is previously asked but not granted, onPermissionPreviouslyDenied()
    *     would be called.
    *
    * 4.  Above M, if the permission is disabled by device policy or the user checked "Never ask again"
    *     check box on previous request permission, onPermissionDisabled() would be called.
    * */
    public interface PermissionAskListener {
/*
        * Callback to ask permission
        * */
        void onPermissionAsk();
/*
        * Callback on permission denied
        * */
        void onPermissionPreviouslyDenied();
/*
        * Callback on permission "Never show again" checked and denied
        * */
        void onPermissionDisabled();
/*
        * Callback on permission granted
        * */
        void onPermissionGranted();
    }
}

PreferenceUtil方法如下。

public static void firstTimeAskingPermission(Context context, String permission, boolean isFirstTime){
SharedPreferences sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE;
 sharedPreference.edit().putBoolean(permission, isFirstTime).apply();
 }
public static boolean isFirstTimeAskingPermission(Context context, String permission){
return context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE).getBoolean(permission, true);
}

现在,您所需要的只是使用带有适当参数的方法checkPermission *。

这是一个例子

PermissionUtil.checkPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    new PermissionUtil.PermissionAskListener() {
                        @Override
                        public void onPermissionAsk() {
                            ActivityCompat.requestPermissions(
                                    thisActivity,
              new String[]{Manifest.permission.READ_CONTACTS},
                            REQUEST_EXTERNAL_STORAGE
                            );
                        }
@Override
                        public void onPermissionPreviouslyDenied() {
                       //show a dialog explaining permission and then request permission
                        }
@Override
                        public void onPermissionDisabled() {
Toast.makeText(context, "Permission Disabled.", Toast.LENGTH_SHORT).show();
                        }
@Override
                        public void onPermissionGranted() {
                            readContacts();
                        }
                    });

我的应用程序如何知道用户是否已选中“不再询问”?

如果用户选中了“ 从不询问”则将onPermissionDisabled上获得回调。

快乐的编码:)


shouldShowRequestPermissionRationale我在这里出错,您能帮我吗。
Rucha Bhatt Joshi

我找不到此方法shouldShowRequestPermissionRationale可能是我无法获取上下文..但是很好,我找到了其他替代解决方案..谢谢您的帮助:)
Rucha Bhatt Joshi

1
我的错。shouldShowRequestPermissionRationale可通过活动而不是上下文获得。我通过在调用该方法之前将上下文转换为Activity来更新了答案。

1
这是解决所返回的第一个假值的唯一方法shouldShowRequestPermissionRationale,将发送给用户的请求保存为首选项。我有同样的想法,找到了答案。
做得好

4

每种情况下的完整说明

/**
 *    Case 1: User doesn't have permission
 *    Case 2: User has permission
 *
 *    Case 3: User has never seen the permission Dialog
 *    Case 4: User has denied permission once but he din't clicked on "Never Show again" check box
 *    Case 5: User denied the permission and also clicked on the "Never Show again" check box.
 *    Case 6: User has allowed the permission
 *
 */
public void handlePermission() {
    if (ContextCompat.checkSelfPermission(MainActivity.this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        // This is Case 1. Now we need to check further if permission was shown before or not

        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

            // This is Case 4.
        } else {
            // This is Case 3. Request for permission here
        }

    } else {
        // This is Case 2. You have permission now you can do anything related to it
    }
}

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // This is Case 2 (Permission is now granted)
    } else {
        // This is Case 1 again as Permission is not granted by user

        //Now further we check if used denied permanently or not
        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            // case 4 User has denied permission but not permanently

        } else {
            // case 5. Permission denied permanently.
            // You can open Permission setting's page from here now.
        }

    }
}

4

一个有用的函数,用于确定是否已阻止任意许可权的请求(在Kotlin中):

private fun isPermissionBlockedFromAsking(activity: Activity, permission: String): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        return ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
            && !activity.shouldShowRequestPermissionRationale(permission)
            && PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false)
    }
    return false
}

要使用此功能,需要在您首次请求权限时将共享的偏好设置布尔值设置为所需的权限名称(例如android.Manifest.permission.READ_PHONE_STATEtrue


说明:

Build.VERSION.SDK_INT >= Build.VERSION_CODES.M 因为某些代码只能在API级别23+上运行。

ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED 检查我们是否尚未获得许可。

!activity.shouldShowRequestPermissionRationale(permission)检查用户是否已拒绝该应用再次询问。由于此功能的怪癖,还需要以下行。

PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false) 由于前一行不会返回此信息,因此使用此方法(以及在首次权限请求时将值设置为true)来区分“从未询问”和“不再询问”状态。


3

请不要为我解决这个问题。

这可行,但有点“ hacky”。

通话时requestPermissions,注册当前时间。

        mAskedPermissionTime = System.currentTimeMillis();

然后在 onRequestPermissionsResult

如果结果不正确,请再次检查时间。

 if (System.currentTimeMillis() - mAskedPermissionTime < 100)

由于用户可能无法快速单击“拒绝”按钮,因此我们知道他选择了“不再询问”,因为回调是即时的。

使用风险自负。


如果我们看到要求的对话框5分钟然后拒绝,该怎么办?
萨克斯姆(Saksham)'18年

如果不能满足基本要求,那么该方法有什么用。如果代码可以清楚地满足所有其他情况下的所有要求,那么它就可以被接受。
萨克斯姆(Saksham)'18年

是的,这很糟糕。像这样的自动测试人员可能会点击的速度更快:developer.android.com/training/testing/crawler
stackzebra

3

方法shouldShowRequestPermissionRationale()可用于检查用户是否选择了“不再询问”选项并拒绝了权限。有很多代码示例,所以我宁愿解释如何将其用于此目的,因为我认为它的名称和实现使它实际上变得更加复杂。

运行时请求权限中所述,如果选项“不再询问”可见,则该方法返回true,否则返回false。因此,它在第一次显示对话框时返回false,然后从第二次开始返回true,并且仅当用户拒绝选择该选项的权限时,才再次返回false。

要检测这种情况,您可以检测到序列false-true-false,或者(更简单)可以使用一个标志来跟踪显示对话框的初始时间。之后,该方法将返回true或false,其中false将允许您检测何时选择了该选项。


2

我在Android M中编写了权限请求的简写。此代码还可以处理对旧Android版本的向后兼容性。

所有丑陋的代码都提取到一个片段中,该片段将自身附加到请求权限的活动上PermissionRequestManager

new PermissionRequestManager()
        // We need a AppCompatActivity here, if you are not using support libraries you will have to slightly change 
        // the PermissionReuqestManager class
        .withActivity(this)

        // List all permissions you need
        .withPermissions(android.Manifest.permission.CALL_PHONE, android.Manifest.permission.READ_CALENDAR)

        // This Runnable is called whenever the request was successfull
        .withSuccessHandler(new Runnable() {
            @Override
            public void run() {
                // Do something with your permissions!
                // This is called after the user has granted all 
                // permissions, we are one a older platform where 
                // the user does not need to grant permissions 
                // manually, or all permissions are already granted

            }
        })

        // Optional, called when the user did not grant all permissions
        .withFailureHandler(new Runnable() {
            @Override
            public void run() {
                // This is called if the user has rejected one or all of the requested permissions
                L.e(this.getClass().getSimpleName(), "Unable to request permission");

            }
        })

        // After calling this, the user is prompted to grant the rights
        .request();

看看:https : //gist.github.com/crysxd/385b57d74045a8bd67c4110c34ab74aa


2
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    switch (requestCode) {
        case PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
            if (grantResults.length > 0) {
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    // Denied
                } else {
                    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                        // To what you want
                    } else {
                       // Bob never checked click
                    }
                }
            }
        }
    }
}

2

试试这个简单的权限库。它将通过3个简单的步骤处理与许可相关的所有操作。它节省了我的时间。您可以在15分钟内完成所有与许可相关的工作

它可以处理“拒绝”,它可以处理“不再询问”,可以调用应用程序设置以获得权限,可以给出一个Rational消息,可以给出一个拒绝消息,可以给出一个接受的权限列表,可以给出一个拒绝列表。权限等

https://github.com/ParkSangGwon/TedPermission

步骤1:添加您的依赖

dependencies {
     compile 'gun0912.ted:tedpermission:2.1.1'
     //check the above link for latest libraries
}

步骤2:询问权限

TedPermission.with(this)
    .setPermissionListener(permissionlistener)
    .setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
    .setPermissions(Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION)
    .check();

步骤3:处理权限回应

PermissionListener permissionlistener = new PermissionListener() {
    @Override
    public void onPermissionGranted() {
        Toast.makeText(MainActivity.this, "Permission Granted", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onPermissionDenied(ArrayList<String> deniedPermissions) {
        Toast.makeText(MainActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show();
    }
};

大。它节省了我的时间
Vigneswaran

尼斯,易于使用
Uray Febri '18

2

你可以听漂亮的。

听众

interface PermissionListener {
    fun onNeedPermission()
    fun onPermissionPreviouslyDenied(numberDenyPermission: Int)
    fun onPermissionDisabledPermanently(numberDenyPermission: Int)
    fun onPermissionGranted()
}

MainClass获得许可

class PermissionUtil {

    private val PREFS_FILENAME = "permission"
    private val TAG = "PermissionUtil"

    private fun shouldAskPermission(context: Context, permission: String): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val permissionResult = ActivityCompat.checkSelfPermission(context, permission)
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                return true
            }
        }
        return false
    }

    fun checkPermission(context: Context, permission: String, listener: PermissionListener) {

        Log.i(TAG, "CheckPermission for $permission")

        if (shouldAskPermission(context, permission)) {

            // Load history permission
            val sharedPreference = context.getSharedPreferences(PREFS_FILENAME, 0)
            val numberShowPermissionDialog = sharedPreference.getInt(permission, 0)

            if (numberShowPermissionDialog == 0) {

                (context as? Activity)?.let {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(it, permission)) {
                        Log.e(TAG, "User has denied permission but not permanently")
                        listener.onPermissionPreviouslyDenied(numberShowPermissionDialog)
                    } else {
                        Log.e(TAG, "Permission denied permanently.")
                        listener.onPermissionDisabledPermanently(numberShowPermissionDialog)
                    }
                } ?: kotlin.run {
                    listener.onNeedPermission()
                }

            } else {
                // Is FirstTime
                listener.onNeedPermission()
            }


            // Save history permission
            sharedPreference.edit().putInt(permission, numberShowPermissionDialog + 1).apply()


        } else {
            listener.onPermissionGranted()
        }

    }
}

通过这种方式使用

      PermissionUtil().checkPermission(this, Manifest.permission.ACCESS_FINE_LOCATION,
                object : PermissionListener {
                    override fun onNeedPermission() {
                        log("---------------------->onNeedPermission")

//                            ActivityCompat.requestPermissions(this@SplashActivity,
//                                    Array(1) { Manifest.permission.ACCESS_FINE_LOCATION },
//                                    118)

                    }

                    override fun onPermissionPreviouslyDenied(numberDenyPermission: Int) {
                        log("---------------------->onPermissionPreviouslyDenied")
                    }

                    override fun onPermissionDisabledPermanently(numberDenyPermission: Int) {
                        log("---------------------->onPermissionDisabled")
                    }

                    override fun onPermissionGranted() {
                        log("---------------------->onPermissionGranted")
                    }

                })

在活动或fragmnet中覆盖onRequestPermissionsResult

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
 if (requestCode == 118) {
        if (permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getLastLocationInMap()
        }
        }
    }

1

相反onRequestPermissionsResult(),当您再次请求许可而处于错误的状态时,您将以PERMISSION_DENIED的形式收到回调shouldShowRequestPermissionRationale()

从Android文档:

当系统要求用户授予许可时,用户可以选择告诉系统不要再次请求该许可。在这种情况下,无论何时应用requestPermissions()再次使用该权限,系统都会立即拒绝该请求。系统将调用您的onRequestPermissionsResult()回调方法并通过PERMISSION_DENIED,就像用户再次明确拒绝您的请求一样。这意味着,当您调用时requestPermissions(),您不能假定已与用户进行了任何直接交互。


1

您可以使用 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)方法来检测是否从未询问。

有关更多参考:请检查此

要检查多个权限,请使用:

  if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)
                                || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                                || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)
                                || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
                            showDialogOK("Service Permissions are required for this app",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            switch (which) {
                                                case DialogInterface.BUTTON_POSITIVE:
                                                    checkAndRequestPermissions();
                                                    break;
                                                case DialogInterface.BUTTON_NEGATIVE:
                                                    // proceed with logic by disabling the related features or quit the app.
                                                    finish();
                                                    break;
                                            }
                                        }
                                    });
                        }
                        //permission is denied (and never ask again is  checked)
                        //shouldShowRequestPermissionRationale will return false
                        else {
                            explain("You need to give some mandatory permissions to continue. Do you want to go to app settings?");
                            //                            //proceed with logic by disabling the related features or quit the app.
                        }

说明()方法

private void explain(String msg){
        final android.support.v7.app.AlertDialog.Builder dialog = new android.support.v7.app.AlertDialog.Builder(this);
        dialog.setMessage(msg)
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        //  permissionsclass.requestPermission(type,code);
                        startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:com.exampledemo.parsaniahardik.marshmallowpermission")));
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        finish();
                    }
                });
        dialog.show();
    }

上面的代码还将显示一个对话框,该对话框会将用户重定向到应用设置屏幕,如果选中了“不再询问”按钮,他可以从该屏幕授予权限。


1

您可以使用

shouldShowRequestPermissionRationale()

onRequestPermissionsResult()

请参阅以下示例:

用户单击按钮时检查它是否具有权限:

@Override
public void onClick(View v) {
    if (v.getId() == R.id.appCompatBtn_changeProfileCoverPhoto) {
        if (Build.VERSION.SDK_INT < 23) { // API < 23 don't need to ask permission
            navigateTo(MainActivity.class); // Navigate to activity to change photos
        } else {
            if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                // Permission is not granted yet. Ask for permission...
                requestWriteExternalPermission();
            } else {
                // Permission is already granted, good to go :)
                navigateTo(MainActivity.class);
            }
        } 
    }
}

当用户回答权限对话框时,我们将转到onRequestPermissionResult:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == WRITE_EXTERNAL_PERMISSION_REQUEST_CODE) {
        // Case 1. Permission is granted.  
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {  
            if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED) {
                // Before navigating, I still check one more time the permission for good practice.
                navigateTo(MainActivity.class);
            }
        } else { // Case 2. Permission was refused
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                // Case 2.1. shouldShowRequest... returns true because the
                // permission was denied before. If it is the first time the app is running we will 
                // end up in this part of the code. Because he need to deny at least once to get 
                // to onRequestPermissionsResult. 
                Snackbar snackbar = Snackbar.make(findViewById(R.id.relLayout_container), R.string.you_must_verify_permissions_to_send_media, Snackbar.LENGTH_LONG);
                snackbar.setAction("VERIFY", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        ActivityCompat.requestPermissions(SettingsActivity.this
                                , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}
                                , WRITE_EXTERNAL_PERMISSION_REQUEST_CODE);
                    }
                });
                snackbar.show();
            } else {
                // Case 2.2. Permission was already denied and the user checked "Never ask again". 
                // Navigate user to settings if he choose to allow this time.
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage(R.string.instructions_to_turn_on_storage_permission)
                        .setPositiveButton(getString(R.string.settings), new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                Uri uri = Uri.fromParts("package", getPackageName(), null);
                                settingsIntent.setData(uri);
                                startActivityForResult(settingsIntent, 7);
                            }
                        })
                        .setNegativeButton(getString(R.string.not_now), null);
                Dialog dialog = builder.create();
                dialog.show();
            }
        }
    }

}

0

我还想获得有关用户是否选择了“不再询问”的信息。我已经用一个难看的标志实现了“几乎解决方案”,但是在我告诉你如何做之前,我将告诉我我的动机:

我想首先提供权限引用功能。如果用户使用它并且没有权限,则他/她将从上方获得第一个对话框,或者从第二个和第三个对话框中获得。当用户选择“不再询问”时,我想禁用该功能并以不同的方式显示它。-我的操作由微调器文本输入触发,我也想在显示的标签文本中添加“((权限已撤销)”)。向用户显示:“有功能,但是由于我的权限设置,我无法使用它。” 但是,这似乎是不可能的,因为我无法检查是否已选择“不再询问”。

通过始终通过活动权限检查启用我的功能,我找到了可以接受的解决方案。在否定响应的情况下,我会在onRequestPermissionsResult()中显示Toast消息,但前提是我没有显示自定义基本原理弹出窗口。因此,如果用户选择了“不再询问”,那么他只会收到一条祝酒消息。如果用户不愿意选择“不再询问”,那么他只会从操作系统中获得自定义基本原理和权限请求弹出窗口,而不会得到吐司,因为连续三个通知会很痛苦。


0

我必须对摄像机实施动态许可。如果发生3种可能的情况:1.允许,2.拒绝,3.不再询问。

 @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

    for (String permission : permissions) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), permission)) {
            //denied
            Log.e("denied", permission);
        } else {
            if (ActivityCompat.checkSelfPermission(getActivity(), permission) == PackageManager.PERMISSION_GRANTED) {
                //allowed
                Log.e("allowed", permission);
            } else {
                //set to never ask again
                Log.e("set to never ask again", permission);
                //do something here.
            }
        }
    }
    if (requestCode != MaterialBarcodeScanner.RC_HANDLE_CAMERA_PERM) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        return;
    }
    if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        mScannerView.setResultHandler(this);
        mScannerView.startCamera(mCameraId);
        mScannerView.setFlash(mFlash);
        mScannerView.setAutoFocus(mAutoFocus);
        return;
    } else {
        //set to never ask again
        Log.e("set to never ask again", permissions[0]);
    }
    DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            dialog.cancel();
        }
    };
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle("Error")
            .setMessage(R.string.no_camera_permission)
            .setPositiveButton(android.R.string.ok, listener)
            .show();


}

private void insertDummyContactWrapper() {
        int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.CAMERA);
        if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.CAMERA},
                    REQUEST_CODE_ASK_PERMISSIONS);
            return;
        }
        mScannerView.setResultHandler(this);
        mScannerView.startCamera(mCameraId);
        mScannerView.setFlash(mFlash);
        mScannerView.setAutoFocus(mAutoFocus);
    }

private int checkSelfPermission(String camera) {
    if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
        return REQUEST_CODE_ASK_PERMISSIONS;
    } else {
        return REQUEST_NOT_CODE_ASK_PERMISSIONS;
    }
}

0

扩展上面的mVck的答案,以下逻辑确定是否已针对给定的权限请求检查了“不再询问”:

bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
    !bStorage && (
        _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
        _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
    );

摘录自下面(有关完整示例,请参见此答案

private bool _bStorageRationaleBefore;
private bool _bStorageRationaleAfter;        
private const int ANDROID_PERMISSION_REQUEST_CODE__SDCARD = 2;
//private const int ANDROID_PERMISSION_REQUEST_CODE__CAMERA = 1;
private const int ANDROID_PERMISSION_REQUEST_CODE__NONE = 0;

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);

    switch (requestCode)
    {
        case ANDROID_PERMISSION_REQUEST_CODE__SDCARD:               
            _bStorageRationaleAfter = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            bool bStorage = grantResults[0] == Permission.Granted;
            bool bNeverAskForStorage =
                !bStorage && (
                    _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
                    _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
                );      
            break;                
    }
}

private List<string> GetRequiredPermissions(out int requestCode)
{
    // Android v6 requires explicit permission granting from user at runtime for security reasons            
    requestCode = ANDROID_PERMISSION_REQUEST_CODE__NONE; // 0
    List<string> requiredPermissions = new List<string>();

    _bStorageRationaleBefore = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
    Permission writeExternalStoragePerm = ApplicationContext.CheckSelfPermission(Android.Manifest.Permission.WriteExternalStorage);
    //if(extStoragePerm == Permission.Denied)
    if (writeExternalStoragePerm != Permission.Granted)
    {
        requestCode |= ANDROID_PERMISSION_REQUEST_CODE__SDCARD;
        requiredPermissions.Add(Android.Manifest.Permission.WriteExternalStorage);
    }

    return requiredPermissions;
}

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);

        // Android v6 requires explicit permission granting from user at runtime for security reasons
        int requestCode;
        List<string> requiredPermissions = GetRequiredPermissions(out requestCode);
        if (requiredPermissions != null && requiredPermissions.Count > 0)
        {
            if (requestCode >= ANDROID_PERMISSION_REQUEST_CODE__SDCARD)                    
            {
                _savedInstanceState = savedInstanceState;
                RequestPermissions(requiredPermissions.ToArray(), requestCode);
                return;
            }
        }
    }            

    OnCreate2(savedInstanceState);
}


0

为了准确回答这个问题,当用户按下“不再询问”时会发生什么?

覆盖的方法/功能

onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)

grantResult数组显示为空,因此您可以在那里做些什么?但不是最佳实践。

如何处理“不再询问”?

我正在使用Fragment,需要READ_EXTERNAL_STORAGE权限。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        when {
            isReadPermissionsGranted() -> {

                /**
                 * Permissions has been Granted
                 */

                getDirectories()
            }

            isPermissionDeniedBefore() -> {

                /**
                 * User has denied before, explain why we need the permission and ask again
                 */

                updateUIForDeniedPermissions()
                checkIfPermissionIsGrantedNow()

            }
            else -> {

                /**
                 * Need to ask For Permissions, First Time
                 */

                checkIfPermissionIsGrantedNow()

                /**
                 * If user selects, "Dont Ask Again" it will never ask again! so just update the UI for Denied Permissions
                 */

                updateUIForDeniedPermissions()

            }
        }
    }

其他功能是微不足道的。

// Is Read Write Permissions Granted
fun isReadWritePermissionGranted(context: Context): Boolean {
    return (ContextCompat.checkSelfPermission(
        context as Activity,
        Manifest.permission.READ_EXTERNAL_STORAGE
    ) == PackageManager.PERMISSION_GRANTED) and
            (ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ) == PackageManager.PERMISSION_GRANTED)
}

fun isReadPermissionDenied(context: Context) : Boolean {
    return ActivityCompat.shouldShowRequestPermissionRationale(
        context as Activity,
        PermissionsUtils.READ_EXTERNAL_STORAGE_PERMISSIONS)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.