【问题标题】:Android Marshmallow: How to allow Runtime Permissions programatically?Android Marshmallow:如何以编程方式允许运行时权限?
【发布时间】:2016-12-23 02:03:46
【问题描述】:

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

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

我怎样才能做到这一点?在 Marshmallow 及更高版本中是否有任何方法可以做到这一点?

【问题讨论】:

    标签: android automated-tests android-6.0-marshmallow android-uiautomator runtime-permissions


    【解决方案1】:

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

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

    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            
        }
    }
    

    如果您通过EspressoUIAutomator 和/或其他一些 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

    【讨论】:

    • 感谢您的详细解释,尽管我只需要“Espresso”之后的最后一部分。顺便说一句,我不是通过“Espresso”自动化我的应用程序,我只是在应用程序内单击按钮并控制应用程序流程。是否可以在不使用“Espresso”之类的东西的情况下这样做?
    • 不客气 :) 您到底想实现什么自动化以及出于什么目的?你目前是怎么做的?从问题描述中有点不清楚您要实现的目标(或者您在应用程序中“单击”按钮的意思)。
    • 这只是意味着每当我想点击button1时,我都会执行button1.performClick()。那么,同样,每当我的应用请求许可时,是否有任何方法可以在系统对话框中单击“允许”?
    • 简单来说,我想在内部授予权限,无需任何用户交互。
    • 这会破坏向用户请求权限的目的并构成安全漏洞 - 自动化这种交互的唯一方法是控制应用程序本身之外的设备(通常使用某种仪器/UI 测试框架。)
    【解决方案2】:

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

    adb install -g {my_apk_file}
    

    -g 标志会自动将所有清单权限授予应用程序。之后,如果您启动 espresso 测试套件,用户界面不会再次要求您授予它们。

    【讨论】:

    • 这在超级用户模式下帮助了我
    【解决方案3】:

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

    您需要按照以下步骤操作。 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
        }
    }
    

    【讨论】:

    • 谢谢@Surya,我已经将上面的代码添加到我的应用程序中,它工作得很好。问题是,我想从应用程序内授予权限。就像每当出现权限对话框一样,它只是自动点击“允许”或类似的东西。
    猜你喜欢
    • 1970-01-01
    • 2015-12-23
    • 2016-07-16
    • 1970-01-01
    • 2017-09-23
    • 2016-03-13
    • 2018-05-08
    • 2015-12-25
    • 1970-01-01
    相关资源
    最近更新 更多