Android权限 - 第三篇

2893次阅读  |  发布于5年以前

原文链接 : Permissions – Part 3

在Marshmallow(棉花糖,Android6.0版本)中Android添加了一个新的权限模块,需要开发者在授权的时候做一些不同的处理。在这一系列中,我们从技术角度看下如何处理请求的权限和如何提供流畅的用户体验。

Icon_no_permission

我们已经知道了如何检测我们需要的权限,现在我们看下如何请求被拒绝的权限。

令人高兴的流程是(至少对于开发者来说),我们直接询问用户授权需要的权限,用户授权了,大家都高兴。但是,就像我们之前讨论过的,我们需要考虑到用户没有授予权限的情况。

现在让我们看下,我们直接提出请求的操作:

PermissionsActivity.java

public class PermissionsActivity extends AppCompatActivity {
    private static final int PERMISSION_REQUEST_CODE = 0;
    private static final String EXTRA_PERMISSIONS = "com.stylingandroid.permissions.EXTRA_PERMISSIONS";
    private static final String EXTRA_FINISH = "com.stylingandroid.permissions.EXTRA_FINISH";
    private static final String PACKAGE_URL_SCHEME = "package:";

    private PermissionsChecker checker;
    private boolean requiresCheck;

    public static void startActivityForResult(Activity activity, int requestCode, String... permissions) {
        Intent intent = new Intent(activity, PermissionsActivity.class);
        intent.putExtra(EXTRA_PERMISSIONS, permissions);
        ActivityCompat.startActivityForResult(activity, intent, requestCode, null);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getIntent() == null || !getIntent().hasExtra(EXTRA_PERMISSIONS)) {
            throw new RuntimeException("This Activity needs to be launched using the static startActivityForResult() method.");
        }
        setContentView(R.layout.activity_permissions);

        checker = new PermissionsChecker(this);
        requiresCheck = true;
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (requiresCheck) {
            String[] permissions = getPermissions();

            if (checker.lacksPermissions(permissions)) {
                requestPermissions(permissions);
            } else {
                allPermissionsGranted();
            }
        } else {
            requiresCheck = true;
        }
    }

    private String[] getPermissions() {
        return getIntent().getStringArrayExtra(EXTRA_PERMISSIONS);
    }

    private void allPermissionsGranted() {
        setResult(PERMISSIONS_GRANTED);
        finish();
    }
    .
    .
    .
}

我们有一个非常实用的startActivityForResult()方法,其他Activity必须使用这个传入需要请求的一套权限来启动PermissionsActivity。所以我们可以直接一次都请求所需要的权限。然而,你可能会疑惑我们为什么需要这个-我们启动Activity是因为我们已经知道我们没有获得所需要的权限么?我们需要这个的原因是如下的这些小概率情况-如果用户临时离开了这个Activity,在设置中授权了权限,然后再回来的时候这个流程会次执行。

但是当我们没有所需要的权限时怎么办-我们需要请求它们:

PermissionsActivity.java

public class PermissionsActivity extends AppCompatActivity {
    .
    .
    .
    private void requestPermissions(String... permissions) {
        ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
    }

   @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_CODE && hasAllPermissionsGranted(grantResults)) {
            requiresCheck = true;
            allPermissionsGranted();
        } else {
            requiresCheck = false;
            showMissingPermissionDialog();
        }
    }

    private boolean hasAllPermissionsGranted(@NonNull int[] grantResults) {
        for (int grantResult : grantResults) {
            if (grantResult == PackageManager.PERMISSION_DENIED) {
                return false;
            }
        }
        return true;
    }
    .
    .
    .

这就是处理核心。我们调用requestPermissions()请求我们需要的权限。我这里总是强调标准和危险级别的权限,虽然我们自动获得标准的权限。背后的原因是,如果标准权限在未来变为一个危险的权限,那么仍会正常工作。

requestPermissions()的操作和startActivityForResult()的操作很像 - 我们在另一个Activity中处理,然后在本Activity中用一个回调做为结束。这个例子中的回调函数是onRequestPermissionResult()。它检测了所有被授权的权限和要么返回处理(要么通过finish(),要么重新唤醒MainActivity,就像之前那样),要么事情变为稍有复杂:我们请求所需要的权限但用户拒绝了。

首先,安装时我们会问用户一个特殊的权限,用户可以选择:允许或拒绝。如果拒绝了权限,我们会在此请求,这时会有一个单选框“不再询问”。如果用户选择了一个单选框并按了“拒绝”,之后的任何权限请求都会被自动的拒绝。不幸的是,我们的权限被拒绝了,我们并不知道用户是否执行了上面的操作。考虑到这个特殊的权限是App运行所急需的,我们需要明确告诉用户为什么我们需要这个权限,如果仍然被拒绝,那就退出App:

PermissionsActivity.java

public class PermissionsActivity extends AppCompatActivity {
    .
    .
    .
    private void showMissingPermissionDialog() {
        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PermissionsActivity.this);
        dialogBuilder.setTitle(R.string.help);
        dialogBuilder.setMessage(R.string.string_help_text);
        dialogBuilder.setNegativeButton(R.string.quit, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                setResult(PERMISSIONS_DENIED);
                finish();
            }
        });
        dialogBuilder.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                startAppSettings();
            }
        });
        dialogBuilder.show();
    }

     private void startAppSettings() {
        Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse(PACKAGE_URL_SCHEME + getPackageName()));
        startActivity(intent);
    }
}

我故意让这里的说明文本说的有点含糊,因为每一个app都要解释需要这个权限的具体原因。同样,为了清晰明了,当只有一个危险权限被请求时,我没有办法没有针对不同权限提供不同的信息。然而,一个真实的app,你很可能需要确定哪一个权限缺失了和针对每一个缺失的提供一个恰当的描述信息。

这里我们可以看到当用户最初拒绝了这个权限,退出和后来的权限的授予。

video

如果用户永远拒绝了权限(通过“不再提醒”选择框)。接下来会稍微复杂,因为我们不能提供一个我们app权限设置页面的直接链接,所以我们需要给用户提供一些说明:

video

需要注意的是是否我们改变了这两者之间的流程。第一个视频展示的是我们告知用户通过设置改变,即使我们随后看到了退出和重新启动后再次提示的允许或拒绝。不幸的,我们没有办法知道用户是否选择了“不再提示”,所以我们要做最坏情况的打算。

即便如此我们现在有一个相对简单,可以重复使用的请求我们应用关键权限的方法。在这个系列的最后一篇文章我们来看看非关键权限和更多的练习。

源代码在这里

© 2016, Mark Allison. All rights reserved.

Copyright© 2013-2019

京ICP备2023019179号-2