我们如何区分 Android M 的运行时权限中的从不询问和停止询问?

Posted

技术标签:

【中文标题】我们如何区分 Android M 的运行时权限中的从不询问和停止询问?【英文标题】:How Do We Distinguish Never-Asked From Stop-Asking in Android M's Runtime Permissions? 【发布时间】:2015-11-02 22:00:35 【问题描述】:

说到M Developer Preview运行时权限,根据Google:

    如果您以前从未请求过某个权限,请直接请求

    如果你之前问过,用户说“不”,然后用户尝试做一些需要被拒绝权限的事情,你应该提示用户解释你为什么需要权限,然后再继续再次请求权限

    如果您之前询问过几次,而用户说“不,并停止询问”(通过运行时权限对话框上的复选框),您应该停止打扰(例如,禁用需要许可)

然而,我们只有一个方法,shouldShowRequestPermissionRationale(),返回一个boolean,我们有三个状态。我们需要一种方法来区分从未询问状态和停止询问状态,因为我们从 shouldShowRequestPermissionRationale() 得到false

对于第一次运行应用程序时请求的权限,这不是一个大问题。有很多方法可以确定这可能是您的应用程序的第一次运行(例如,SharedPreferences 中的 boolean 值),因此您假设如果这是您的应用程序的第一次运行,那么您永远不会- 询问状态。

但是,运行时权限的部分愿景是您可能不会预先要求所有这些权限。当用户点击需要该权限的东西时,您可能只会在以后请求与边缘功能相关的权限。在这里,应用程序可能已经运行了好几个月,然后我们突然需要请求另一个权限。

在这些情况下,我们是否应该跟踪我们是否自己请求了许可?还是我在 android M API 中遗漏了一些东西来告诉我们我们之前是否询问过?

【问题讨论】:

这是我得到的所有信息,和你刚刚发布的一样plus.google.com/+BenjaminWeiss/posts/PFSd7wau4n8 示例应用程序如此琐碎和不完整,它可能不存在。 所以最好的猜测是在 SharedPreferences 每个权限或任何类似的工件中存储一个布尔值,这是 Google 在 IO 上的建议。 我担心SharedPreferences 可能与 Android 自己存储的权限信息不同步。 Android 是关于运行时权限的“记录系统”。它显然有信息,否则它永远无法从shouldShowRequestPermissionRationale() 返回true。我只是看看是否有一些我缺少的方法被添加了,仅此而已。 了解 Google,他们将在 6.1 中弃用 shouldShowRequestPermissionRationale() 并添加一个返回 int 的新方法。 【参考方案1】:

关于 MLProgrammer-CiM 的回答,我知道如何解决在 SharedPrefrences 中存储的布尔值已经为 true 后用户撤销权限的场景,

只需创建另一个常量布尔值,如果第一个调用例如:Constant.FIRST_TIME_REQUEST(其默认状态将为真) 第二个将被称为Constant.PERMISSION_ALREADY_GRANTED(默认为false)

onRequestPermissionsResult 上,如果授予权限,您当然可以将其值更改为 true。

现在,在你想通过预先解释请求许可的部分,写下这样的内容:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
   SharedPreferences sp = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
   boolean isPermissionGranted = sp.getBoolean(Constant.PERMISSION_ALREADY_GRANTED, false);
   if (isPermissionGranted) 
      sp.putBoolean(Constant.PERMISSION_ALREADY_GRANTED, false);
      sp.putBoolean(Constant.FIRST_TIME_REQUEST, true);
   

   if (ActivityCompat.shouldShowRequestPermissionRationale(activity, androidPermissionName) || sp.getBoolean(Constant.FIRST_TIME_REQUEST, true) ) 
   showDialogExplanation();


这样,即使用户将删除权限,布尔值也会再次设置为 false。

祝你好运,我希望它会有所帮助。

【讨论】:

【参考方案2】:

所以我终于有时间回答来自 COMMONSWARE 的问题了


业务流程:-

1. 当用户第一次点击“拒绝权限”时,我会显示理由对话框来解释权限的必要性。然后,如果用户单击基本原理对话框上的“取消”按钮,我将显示一个 toast 显示消息“请授予获取位置的权限”。

2. 之后,当用户在权限对话框中点击拒绝权限(不再询问)时,我将显示一条消息“请从应用设置中授予位置权限”。请注意,我添加了“来自应用程序设置”的字样,因为用户已选中“不再询问”框。

3. 所以从现在开始,权限对话框将不会显示。基本原理对话框也不会显示。

所以这里的关键是,如果权限对话框和理由对话框都没有显示,则表示用户已选中“不再询问”复选框。

代码:-

        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
            if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION))
                AlertDialogHelper.showDialogWithYesNoCallback(mContext, getString(R.string.confirm), getString(R.string.please_give_permission_to_get_location), new onItemClickReturnBoolean() 
                    @Override
                    public void onItemClick(Boolean status) 
                        if(status)
                            ActivityCompat.requestPermissions(SplashScreenActivity.this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE);
                        
                        else
                            ShowToast.showShortToast(SplashScreenActivity.this,getString(R.string.please_give_permission_to_get_location));
                            finish();
                        
                    
                );
            
            else
                ActivityCompat.requestPermissions(this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE);
            
        
        else
            gettingLocationAfterPermissionGranted();
        

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE)
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED)
                gettingLocationAfterPermissionGranted();
            
            else
                if(ActivityCompat.shouldShowRequestPermissionRationale(SplashScreenActivity.this,Manifest.permission.ACCESS_FINE_LOCATION))
                    ShowToast.showShortToast(this,getString(R.string.please_give_permission_to_get_location));
                
                else
                    ShowToast.showShortToast(this,getString(R.string.please_give_location_permission_from_app_settings));
                
                finish();
            
        
    

检查此存储库: https://github.com/debChowdhury/PermissionHelperEasy


Easy peasy


【讨论】:

【参考方案3】:

在这里尝试了所有答案并在互联网上发布了其他一些帖子之后。我开始知道我必须使用 sharedPreference isLocationPermissionDialogShown (默认为 false),并且每件事都按预期工作。

    如果第一次请求许可。在这种情况下,shouldShowRequestPermissionRationale 返回 falseisLocationPermissionDialogShown 以及 false 第二次shouldShowRequestPermissionRationale 返回true 并在显示对话框时将isLocationPermissionDialogShown 设置为true。当我们检查条件时,两者都是true 每次都打勾 shouldShowRequestPermissionRationale 返回 trueisLocationPermissionDialogShown 返回 true 如果不再询问已勾选 shouldShowRequestPermissionRationale 返回 falseisLocationPermissionDialogShown 返回 true。这正是我们所需要的。

请检查工作示例。

public class MainActivity extends AppCompatActivity 
    SharedPreferences sharedPreferences;
    String locationPermission;
    String prefLocationPermissionKey = "isLocationPermissionDialogShown";
    private final int PERMISSION_REQUEST_CODE_LOCATION = 1001;


    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        locationPermission = Manifest.permission.ACCESS_FINE_LOCATION;
        sharedPreferences = getSharedPreferences("configuration", MODE_PRIVATE);

        //check for android version
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
            //Check for permission
            if (checkSelfPermission(locationPermission) != PackageManager.PERMISSION_GRANTED) 
                //check if clarification dialog should be shown.
                if (shouldShowRequestPermissionRationale(locationPermission)) 
                    showClarificationDialog(locationPermission, PERMISSION_REQUEST_CODE_LOCATION);
                 else  
                    requestPermissions(new String[]  locationPermission, PERMISSION_REQUEST_CODE_LOCATION);
                
             else 
                Log.d("nets-debug", "permission already grranted");
            
        

    

    @Override
    @TargetApi(Build.VERSION_CODES.M)
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 

        if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) 
            //for location permission
            if (requestCode == PERMISSION_REQUEST_CODE_LOCATION) 
                boolean isLocationPermissionDialogShown = sharedPreferences.getBoolean(prefLocationPermissionKey, false);

                if (!shouldShowRequestPermissionRationale(locationPermission) && isLocationPermissionDialogShown) 
                    // user selected Never Ask Again. do something
                    Log.d("nets-debug", "never ask again");
                 else 
                    // all other conditions like first time asked, previously denied etc are captured here and can be extended if required.
                    Log.d("nets-debug", "all other cases");
                
            

        

    

    @TargetApi(Build.VERSION_CODES.M)
    public void showClarificationDialog(final String permission, final int requestCode) 
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Permission Required");
        builder.setMessage("Please grant Location permission to use all features of this app");
        builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() 
            @Override
            public void onClick(DialogInterface dialog, int which) 
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.putBoolean(prefLocationPermissionKey, true);
                editor.apply();
                requestPermissions(new String[] permission, requestCode);
            
        );
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() 
            @Override
            public void onClick(DialogInterface dialog, int which) 
                Toast.makeText(getApplicationContext(), "This permission required", Toast.LENGTH_LONG).show();
            
        );
        builder.create().show();
    


希望这会有所帮助。

【讨论】:

【参考方案4】:

这是跟踪第一次显示权限对话框时的方法,当用户检查时不再询问,当用户检查后直接拒绝权限时不再询问,我们需要保留一个标志,如果权限理由对话框已经被在获取 onRequestPermissionsResult 的结果之前显示。 需要时调用方法 checkPermission()。

public boolean mPermissionRationaleDialogShown = false;

public void checkPermission() 
    if (ContextCompat.checkSelfPermission(this, "PermissionName")
            != PackageManager.PERMISSION_GRANTED) 
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")) 
            showPermissionRequiredDialog();
         else 
            askPermission();
        
     else 
       // Permission Granted
    


public void askPermission() 
    ActivityCompat.requestPermissions(this,
            new String[]"PermissionName", permissionRequestCode);


public void showPermissionRequiredDialog() 
    mPermissionRationaleDialogShown = true;
    // Dialog to show why permission is required


@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 
    if (requestCode == PERMISSION_REQUEST_CODE) 
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
            // Permission Granted
         else 
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")
                    && !mPermissionRationaleDialogShown) 
                // Permission dialog was shown for first time
             else if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")
                    && mPermissionRationaleDialogShown)
                // User deny permission without Never ask again checked
             else if (!ActivityCompat.shouldShowRequestPermissionRationale(this, PERMISSION_READ_EXTERNAL)
                    && mPermissionRationaleDialogShown) 
                // User has checked Never ask again during this permission request
             else 
                // No permission dialog shown to user has user has previously checked Never ask again. Here we can show dialog to open setting screen to change permission
            
        
     else 
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    

【讨论】:

【参考方案5】:

不需要为权限状态创建一个并行持久化状态,你可以使用这个随时返回当前权限状态的方法:

@Retention(RetentionPolicy.SOURCE)
    @IntDef(GRANTED, DENIED, BLOCKED)
    public @interface PermissionStatus 

    public static final int GRANTED = 0;
    public static final int DENIED = 1;
    public static final int BLOCKED = 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;
            
            return DENIED;
        
        return GRANTED;
    

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

I also used this answered here.

【讨论】:

这将在第一次用例中失败,如果您从未向用户请求权限,您的方法getPermissionStatus 将错误地返回BLOCKED,这实际上是不正确的。此设计中需要第 4 种状态,可能称为 HAVENT_ASKED,检测它的唯一方法是使用共享首选项或类似的东西。 true,对某些用例可能仍然有用;我真的不喜欢使用共享首选项跟踪并行状态【参考方案6】:

我知道我发布得很晚,但详细的示例可能对某人有所帮助。

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

状态1:-返回true:--任何时候用户点击拒绝权限(包括第一次。

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

这是一个具有多个权限请求的示例:-

应用在启动时需要 2 个权限。 SEND_SMS 和 ACCESS_FINE_LOCATION(都在 manifest.xml 中提到)。

应用程序一启动,就会同时请求多个权限。如果两个权限都被授予,则正常流程进行。

public static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if(checkAndRequestPermissions()) 
        // carry on the normal flow, as the case of  permissions  granted.
    


private  boolean checkAndRequestPermissions() 
    int permissionSendMessage = ContextCompat.checkSelfPermission(this,
            Manifest.permission.SEND_SMS);
    int locationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
    List<String> listPermissionsNeeded = new ArrayList<>();
    if (locationPermission != PackageManager.PERMISSION_GRANTED) 
        listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
    
    if (permissionSendMessage != PackageManager.PERMISSION_GRANTED) 
        listPermissionsNeeded.add(Manifest.permission.SEND_SMS);
    
    if (!listPermissionsNeeded.isEmpty()) 
        ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),REQUEST_ID_MULTIPLE_PERMISSIONS);
        return false;
    
    return true;

如果未授予一项或多项权限, activityCompat.requestPermissions() 将请求权限,控制转到 onRequestPermissionsResult() 回调方法。

您应该检查 onRequestPermissionsResult() 回调方法中 shouldShowRequestPermissionRationale() 标志的值。

只有两种情况:--

案例 1:-任何时候用户点击拒绝权限(包括第一次),它都会返回 true。所以当用户拒绝的时候,我们可以给出更多的解释并继续询问。

案例 2:-只有当用户选择“不再询问”时,它才会返回 false。在这种情况下,我们可以继续使用有限的功能,并引导用户从设置中激活权限以获得更多功能,或者如果权限对于应用程序来说是微不足道的,我们可以完成设置。

案例- 1

案例-2

@Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) 
        Log.d(TAG, "Permission callback called-------");
        switch (requestCode) 
            case REQUEST_ID_MULTIPLE_PERMISSIONS: 

                Map<String, Integer> perms = new HashMap<>();
                // Initialize the map with both permissions
                perms.put(Manifest.permission.SEND_SMS, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
                // Fill with actual results from user
                if (grantResults.length > 0) 
                    for (int i = 0; i < permissions.length; i++)
                        perms.put(permissions[i], grantResults[i]);
                    // Check for both permissions
                    if (perms.get(Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED
                            && perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) 
                        Log.d(TAG, "sms & location services permission granted");
                        // process the normal flow
                        //else any one or both the permissions are not granted
                     else 
                            Log.d(TAG, "Some permissions are not granted ask again ");
                            //permission is denied (this is the first time, when "never ask again" is not checked) so ask again explaining the usage of permission
//                        // shouldShowRequestPermissionRationale will return true
                            //show the dialog or snackbar saying its necessary and try again otherwise proceed with setup.
                            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.SEND_SMS) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) 
                                showDialogOK("SMS and Location Services Permission 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.
                                                        break;
                                                
                                            
                                        );
                            
                            //permission is denied (and never ask again is  checked)
                            //shouldShowRequestPermissionRationale will return false
                            else 
                                Toast.makeText(this, "Go to settings and enable permissions", Toast.LENGTH_LONG)
                                        .show();
    //                            //proceed with logic by disabling the related features or quit the app.
                            
                    
                
            
        

    

    private void showDialogOK(String message, DialogInterface.OnClickListener okListener) 
        new AlertDialog.Builder(this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", okListener)
                .create()
                .show();
    

【讨论】:

问题是 shouldShowRequestPermissionRationale 如果他们检查了“不再询问”或者他们之前从未拒绝过,则返回 false。所以检查这个方法的返回值并不足以知道我们是否应该预先教育。 我们如何使用这个解决方案? shouldShowRequestPermissionRationale() 需要 minSdkVersion 23 @NaPro 此功能(运行时权限)仅在 API23 中提供。并且支持向后兼容性,所以 minSdkVersion ,所以除非你有 compileSDK 版本 23 或更高版本,否则对你来说不是一个问题。 非常好。拯救了我的一天。 嘿@Nicks你能告诉我那个for循环在那里做什么这个for (int i = 0; i &lt; permissions.length; i++)有什么用@【参考方案7】:

我有办法解决你的问题,对我来说似乎效果很好。

我使用 SharedPreferences 区分 Never-Asked 和 Stop-Asking,我将举一个例子说明我是如何使用它的。

private void requestAccountPermission() 

        SharedPreferences mPreferences = getSharedPreferences("configuration", MODE_PRIVATE);
        boolean firstTimeAccount = mPreferences.getBoolean("firstTimeAccount", true);

        if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.GET_ACCOUNTS)) 
            // 2. Asked before, and the user said "no"
            ActivityCompat.requestPermissions(this, new String[]Manifest.permission.GET_ACCOUNTS, REQUEST_CODE_ACCOUNTS);
        else 
            if(firstTimeAccount)  
                // 1. first time, never asked 
                SharedPreferences.Editor editor = mPreferences.edit();
                editor.putBoolean("firstTimeAccount", false);
                editor.commit();

                // Account permission has not been granted, request it directly.
                ActivityCompat.requestPermissions(this, new String[]Manifest.permission.GET_ACCOUNTS,REQUEST_CODE_ACCOUNTS);
            else
                // 3. If you asked a couple of times before, and the user has said "no, and stop asking"

                // Your code
            
        
    

【讨论】:

ActivityCompat.requestPermissions 在我们点击后不会显示不再询问?它进入 else 条件。有没有办法让它再次显示?就像脸书一样。【参考方案8】:

不,您不需要跟踪您是否请求了许可,也不需要区分 Never-Asked 和 Stop-Asking。

状态1和3对于应用开发者来说是一样的:你需要权限和ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED,然后你只要通过ActivityCompat.requestPermissions()请求权限,只要用户点击需要权限的功能,不管有多少您要求的次数。用户最终将“授予”它,或“拒绝”它并选中“不再询问”。该设计不会阻止您多次弹出权限请求对话框。

但是,该设计确实鼓励您在某些时候解释许可的目的 - 您的状态 2。shouldShowRequestPermissionRationale() 不用于确定您是否应该请求许可,它用于确定您是否应该显示解释,在您请求许可之前。

关于状态 3 的更多解释:

    是的,我们应该通过停止显示解释来停止打扰用户,而不是停止请求。这就是他们提供shouldShowRequestPermissionRationale() 的原因。 保留许可请求并不麻烦。用户选择“不再询问”后,ActivityCompat.requestPermissions() 将不再弹出对话框。 在单用户会话期间,每次我们发现我们没有权限时,最好禁用相关 UI。而不是在shouldShowRequestPermissionRationale() 返回 false 之后禁用 UI。

【讨论】:

"在单用户会话期间,每次我们发现我们没有权限时,最好禁用相关的 UI。而不是在 shouldShowRequestPermissionRationale() return false 之后禁用 UI" - 这是我们分歧的核心。如果我们使用 Google 不禁用 UI 而是显示引导用户进入设置的快餐栏的方法,我们就会遇到类似的问题。不知何故,我们需要知道显示该小吃吧,而直接的解决方案需要我们区分状态 3 和状态 1。 好消息是shouldShowRequestPermissionRationale() 似乎已经在onRequestPermissionsResult() 中返回了预期值,所以如果用户拒绝再次询问,shouldShowRequestPermissionRationale() 确实返回了@ 987654330@。因此,如果您想要获得相同的响应(例如,显示一个小吃店),无论用户是再次拒绝还是之前这样做,您都不需要状态 1。如果您想要不同的响应(例如,仅当用户在一段时间前(而不是现在)再次拒绝时才显示小吃栏),您仍然需要状态 1。 @CommonsWare 这绝对有很大帮助!尽管这似乎有点骇人听闻。希望这在未来的版本中仍然有效。 请注意,如果您之前从未显示过权限对话框,shouldShowRequestPermissionRationale() 将返回 false,即使用户之前从未拒绝过该权限。所以第一次你应该强制权限对话框,然后你可以依赖它。考虑到这种行为将来可能会发生变化。【参考方案9】:

你可以看看here - 有一个流程图很好地解释了这个过程。它还解释了何时应该调用 shouldShowRequestPermissionRationale() 以及何时返回 true。

基本上根据Android的文档,如果你没有权限,你应该总是请求权限(如果用户说永远不要再询问,Android会在回调中自动返回DENIED),如果用户应该显示一条短消息过去已经拒绝过您一次,但没有标记“不再询问”选项。

【讨论】:

这并没有解决问题。【参考方案10】:

根据当前示例:https://github.com/googlesamples/android-RuntimePermissions/blob/master/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java#L195

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
        int[] grantResults) 
    if (requestCode == REQUEST_CAMERA) 
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) 
            doThing();
            //STORE FALSE IN SHAREDPREFERENCES
         else 
            //STORE TRUE IN SHAREDPREFERENCES
        
    

在 SharedPreferences 中存储一个布尔值,键作为您的权限代码和值,如上所示,以指示该首选项之前是否已被拒绝。

遗憾的是,您可能无法检查在您的应用运行时已被接受但后来被拒绝的首选项。最终规范不可用,但您的应用有可能会重新启动或获取模拟值,直到下一次启动。

【讨论】:

“您可能无法检查在您的应用程序运行时已被接受但后来被拒绝的首选项” - 我的印象是设置中的权限更改会导致您的应用程序进程被终止,从而实际上迫使您检查您是否再次拥有权限。正如对该问题的评论中所指出的,我希望在 Android 中有一些适当的机制,以避免数据重复和不同步的风险。谢谢! 我相信针对目标 M 编译的应用程序之间存在不同的行为。您将获得旧版本的模拟值,以及其他版本的 SecurityExceptions。 我尝试过这种机制,但可惜它并没有涵盖所有场景。如果您在应用程序内允许权限,然后转到权限设置并拒绝它,则存储在共享首选项中的布尔值仍然为真(从以前),您需要它为假。如果您不打算预先教育,shouldShowRequestPermissionRationale 很好,但如果您这样做,那就不好了。应该有一个“shouldEducateUpFront”方法来确定用户之前没有拒绝权限的情况。特别是因为谷歌建议在某些情况下这样做:youtube.com/watch?v=iZqDdvhTZj0

以上是关于我们如何区分 Android M 的运行时权限中的从不询问和停止询问?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android M 或更高版本中在运行时更改权限时防止重新创建 Activity

Android M新的运行时权限开发者需要知道的一切

android M 之前的运行时权限(api 14 及更高版本)

低于 M 的 android 版本的 Android 运行时权限?

在运行时请求权限,Android M+

使用android studio运行应用程序时如何启用android 6.0中的所有权限