代码之家  ›  专栏  ›  技术社区  ›  Rafael T

在AndroidTest中写入外部存储

  •  5
  • Rafael T  · 技术社区  · 6 年前

    我想写一篇 AndroidTest 对于一个涉及到写入外部存储的应用程序,但不知何故我无法使其工作。(在Pixel2、Lg G6、华威P8等多种设备上进行测试)

    所以我简化了所有内容,只是为了测试是否可以写入外部存储。我尝试过 GrantPermissionRule 以及在格兰特上写我的作品,但一点也没有成功。

    我可能最终做了太多不必要的事情,所以以下是我所做的:

    1. 在中创建没有活动的新项目 AndroidStudio
    2. 添加了清单权限
    3. 已添加 AndroidManifest 具有 uses-permission 标签
    4. 我自己写的 PermissionRequester
    5. 编写测试以尝试在/sdcard/Download中添加文件夹并将其删除

    以下是上面编号列表引用的代码段

    2.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.company.android.testpermissions">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="com.company.TestPermissions"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" />
    </manifest>
    

    3.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.company.android.testpermissions.test">
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
    </manifest>
    

    也尝试过

    <manifest package="com.company.android.testpermissions">...
    

    省略“test”后缀

    4.

    public class MyPermissionRequester {
    
        public static final String TAG = MyPermissionRequester.class.getSimpleName();
    
        public static void request(String... permissions) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                UiAutomation auto = InstrumentationRegistry.getInstrumentation().getUiAutomation();
                String cmd = "pm grant " + InstrumentationRegistry.getTargetContext().getPackageName() + " %1$s";
                String cmdTest = "pm grant " + InstrumentationRegistry.getContext().getPackageName() + " %1$s";
                String currCmd;
                for (String perm : permissions) {
                    execute(String.format(cmd, perm), auto);
                    execute(String.format(cmdTest, perm), auto);
                }
            }
            GrantPermissionRule.grant(permissions);
        }
    
        private static void execute(String currCmd, UiAutomation auto){
            Log.d(TAG, "exec cmd: " + currCmd);
            auto.executeShellCommand(currCmd);
        }
    }
    

    5.

    @RunWith(AndroidJUnit4.class)
    public class ExampleInstrumentedTest {
        @Test
        public void sdcardTest(){
    
            //check sdcard availability 
            Assert.assertEquals("Media is not mounted!", Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED);
           //get and check Permissions
    MyPermissionRequester.request(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            int grantResult = ActivityCompat.checkSelfPermission(InstrumentationRegistry.getTargetContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
            Assert.assertEquals(PackageManager.PERMISSION_GRANTED, grantResult);
            grantResult = ActivityCompat.checkSelfPermission(InstrumentationRegistry.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
            Assert.assertEquals(PackageManager.PERMISSION_GRANTED, grantResult);
    
            //finally try to create folder
            File f = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "CompanyTest");
            if (!f.exists()){
                boolean mkdir = f.mkdirs();
                Assert.assertTrue("Folder '"+f.getAbsolutePath() + "' not Present!", mkdir);
            }
            boolean delete = f.delete();
            Assert.assertTrue("Folder '"+f.getAbsolutePath() + "' is Present!", delete);
        }
    }
    

    试验结果为:

    朱尼特。框架断言失败错误:文件夹 “/存储/模拟/0/下载/公司测试”不存在!在

    朱尼特。框架明确肯定失败(Assert.java:50)

    朱尼特。框架明确肯定assertTrue(Assert.java:20)位于

    com。公司安卓测试权限。示例InstrumentedTest。sdcardTest(示例InstrumentedTest.java:69)

    在java。lang.reflect。方法调用(本机方法) 。。。 (省略了来自TestRunner的其余部分)

    我错过了什么或做错了什么?

    3 回复  |  直到 6 年前
        1
  •  5
  •   Rafael T    6 年前

    在经历了很多痛苦的尝试之后,我终于找到了解决方案:

    谷歌声称

    if your application already requests write access, it will automatically get read access as well.
    

    https://developer.android.com/about/versions/android-4.1.html#Permissions

    或书面外部存储文档:

    Note: If your app uses the WRITE_EXTERNAL_STORAGE permission, then it implicitly has permission to read the external storage as well.
    

    https://developer.android.com/training/data-storage/files.html#WriteExternalStorage

    或在清单权限中:

    Any app that declares the WRITE_EXTERNAL_STORAGE permission is implicitly granted this permission.
    

    https://developer.android.com/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE

    但事实并非如此!

    如果我明确添加 READ_EXTERNAL_STORAGE 除了在运行时请求清单外,它的工作方式和我以前的一样。

    So 3。必须添加

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.company.android.testpermissions.test">
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    </manifest>
    

    测试应该如下所示:

    @RunWith(AndroidJUnit4.class)
    public class ExampleInstrumentedTest {
        @Test
        public void sdcardTest(){
    
            //check sdcard availability 
            Assert.assertEquals("Media is not mounted!", Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED);
           //get and check Permissions
    MyPermissionRequester.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);
            int grantResult = ActivityCompat.checkSelfPermission(InstrumentationRegistry.getTargetContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
            Assert.assertEquals(PackageManager.PERMISSION_GRANTED, grantResult);
            grantResult = ActivityCompat.checkSelfPermission(InstrumentationRegistry.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
            Assert.assertEquals(PackageManager.PERMISSION_GRANTED, grantResult);
    
            //finally try to create folder
            File f = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "CompanyTest");
            if (!f.exists()){
                boolean mkdir = f.mkdirs();
                Assert.assertTrue("Folder '"+f.getAbsolutePath() + "' not Present!", mkdir);
            }
            boolean delete = f.delete();
            Assert.assertTrue("Folder '"+f.getAbsolutePath() + "' is Present!", delete);
        }
    }
    

    一切都正常。

    剩下的唯一问题是,自Android 8.1以来,这是否/为什么是预期行为

        2
  •  1
  •   cjnash Vikey    6 年前

    正如我在评论中所说,

    我唯一能想到的是,在您引用的两个示例中,它们似乎都引用了Android的旧版本(看起来它们引用的是Android 4.1)。这里有一个链接明确指出,在Android 6.0之后,用户必须明确批准每个不属于应用程序“沙盒”的权限:

    https://developer.android.com/training/permissions/requesting.html

    此外,它们在我上面提供的链接中明确指出,您必须在清单中同时包含读和写。然后,您可以使用运行时权限,这将涵盖这两个权限,但前提是您在清单中同时包含这两个权限。(在“处理权限请求响应”部分下)

        3
  •  1
  •   Liu    6 年前

    自Android 6.0以来,您需要手动授予运行时权限才能运行androidTest

    adb shell pm grant <pkg_name> android.permission.READ_EXTERNAL_STORAGE adb shell pm grant <pkg_name> android.permission.WRITE_EXTERNAL_STORAGE 您可以通过以下方式验证权限 adb shell dumpsys package <pkg_name> AndroidManifest。xml仅请求这些权限,但在安装androidTest APK时默认不授予这些权限。