Android Marshmallow:如何以编程方式允许运行时权限?

Android Marshmallow: How to allow Runtime Permissions programatically?

我正在开发一个需要一些系统权限的应用程序,但是在 Android Marshmallow 上安装时不再自动授予这些权限。

我想在 运行 时请求这些权限,并且 运行 某种自动化来授予它们,而不需要用户在系统权限对话框时点击 'allow' 按钮出现。

我怎样才能做到这一点?在 Marshmallow 和更高版本中有什么方法可以这样做吗?

对于 Marshmallow 或更高版本的权限不会在安装时授予,必须在需要时在 运行 时请求(如果之前未授予。)

为此,您需要 运行 ActivityCompat.requestPermissions() 在您的 Activity 中弹出系统权限对话框,当用户正在执行需要额外的操作时系统权限。

WRITE_EXTERNAL_STORAGE 权限的示例如下:

ActivityCompat.requestPermissions(
    this, 
    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
    WRITE_EXTERNAL_STORAGE_REQUEST_CODE
);

注意:WRITE_EXTERNAL_STORAGE_REQUEST_CODE 是您应该在其他地方定义的任意整数常量。

您请求的权限也应在您的 AndroidManifest.xml 中声明。在此示例中,声明为:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

为了处理系统权限对话框响应,您还需要在 Activity 中实施 onRequestPermissionsResult()。对于此示例,代码类似于

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    if (grantResults.length == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED) {
        return; //permission not granted, could also optionally log an error
    }
    if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
        //Do whatever you needed the write permissions for            
    }
}

如果您通过 Espresso, UIAutomator and/or 其他一些 UI 测试框架自动化您的应用程序,您将需要在测试期间预期并单击系统对话框,这可以通过以下测试代码:

private void allowPermissionsIfNeeded()  {
    if (Build.VERSION.SDK_INT >= 23) {
        UiObject allowPermissions = mDevice.findObject(new UiSelector().text("Allow"));
        if (allowPermissions.exists()) {
            try {
                allowPermissions.click();
            } catch (UiObjectNotFoundException e) {
                Timber.e(e, "There is no permissions dialog to interact with ");
            }
        }
    }
}

更全面的测试系统说明UI权限可用here

如果您的应用需要危险权限,您必须在每次执行需要该权限的操作时检查您是否拥有该权限

您需要按照以下步骤操作。 1. 检查权限

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

请求权限

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an explanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

处理权限请求响应

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

我发现在 CI 场景中不使用 UIAutomator 或 espresso 来自动接受权限的更简单方法是简单地通过 adb 使用以下方法预安装 apk:

adb install -g {my_apk_file}

-g 标志自动向应用程序授予所有清单权限。之后,如果您启动浓缩咖啡测试 suite,ui 将不会再次要求您授予它们。