在 AndroidTest 中编写外部存储
Write External Storage in AndroidTest
我想为一个涉及写入外部存储的应用程序编写一个 AndroidTest
,但不知何故我无法使其工作。 (在 Pixel2、Lg G6、Huawai P8 等多种设备上进行测试)
所以我简化了一切,只是为了测试是否可以写入外部存储。我尝试了 GrantPermissionRule 并在 Granter 上写了我的文章,但完全没有成功。
我可能最终做了太多不必要的事情,所以这是我所做的:
- 在
AndroidStudio
中创建没有 Activity 的新项目
- 添加了对 Manifest 的权限
- 添加了
AndroidManifest
和 uses-permission
标签
- 自己写的
PermissionRequester
- 编写测试以尝试在 /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);
}
}
测试结果为:
junit.framework.AssertionFailedError: Folder
' /storage/emulated/0/Download/CompanyTest' not Present! at
junit.framework.Assert.fail(Assert.java:50) at
junit.framework.Assert.assertTrue(Assert.java:20) at
com.company.android.testpermissions.ExampleInstrumentedTest.sdcardTest(ExampleInstrumentedTest.java:69)
at java.lang.reflect.Method.invoke(Native Method) at
...
(omitted rest coming from the TestRunner)
我在这里遗漏了什么或做错了什么?
经过大量痛苦尝试无效的方法后,我终于找到了解决方案:
Google 声称
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
或在 WriteExternalStorage 文档中:
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
添加到我的清单中并在运行时请求它,它会像我习惯的那样工作。
所以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);
}
}
一切正常。
唯一剩下的问题是 if/why 这是自 Android 8.1
以来的预期行为
正如我在评论中所说,
我唯一能想到的是,在您引用的两个示例中,它们似乎都引用了 Android 的旧版本(看起来它们引用的是 Android 4.1) .这是 link 明确指出用户必须明确批准不属于 Android 6.0 之后应用程序的 "sandbox" 范围内的每个权限:
https://developer.android.com/training/permissions/requesting.html
此外,他们在我上面提供的 link 中明确声明您必须在清单中同时包含 READ 和 WRITE。然后你可以使用 运行 时间权限,这将涵盖两者,但前提是你将两者都包含在你的清单中。 (在 "Handle the permissions request response" 部分下)
从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 时默认不会授予。
我想为一个涉及写入外部存储的应用程序编写一个 AndroidTest
,但不知何故我无法使其工作。 (在 Pixel2、Lg G6、Huawai P8 等多种设备上进行测试)
所以我简化了一切,只是为了测试是否可以写入外部存储。我尝试了 GrantPermissionRule 并在 Granter 上写了我的文章,但完全没有成功。
我可能最终做了太多不必要的事情,所以这是我所做的:
- 在
AndroidStudio
中创建没有 Activity 的新项目
- 添加了对 Manifest 的权限
- 添加了
AndroidManifest
和uses-permission
标签 - 自己写的
PermissionRequester
- 编写测试以尝试在 /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);
}
}
测试结果为:
junit.framework.AssertionFailedError: Folder ' /storage/emulated/0/Download/CompanyTest' not Present! at
junit.framework.Assert.fail(Assert.java:50) at
junit.framework.Assert.assertTrue(Assert.java:20) at
com.company.android.testpermissions.ExampleInstrumentedTest.sdcardTest(ExampleInstrumentedTest.java:69)
at java.lang.reflect.Method.invoke(Native Method) at ... (omitted rest coming from the TestRunner)
我在这里遗漏了什么或做错了什么?
经过大量痛苦尝试无效的方法后,我终于找到了解决方案:
Google 声称
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
或在 WriteExternalStorage 文档中:
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
添加到我的清单中并在运行时请求它,它会像我习惯的那样工作。
所以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);
}
}
一切正常。
唯一剩下的问题是 if/why 这是自 Android 8.1
以来的预期行为正如我在评论中所说,
我唯一能想到的是,在您引用的两个示例中,它们似乎都引用了 Android 的旧版本(看起来它们引用的是 Android 4.1) .这是 link 明确指出用户必须明确批准不属于 Android 6.0 之后应用程序的 "sandbox" 范围内的每个权限:
https://developer.android.com/training/permissions/requesting.html
此外,他们在我上面提供的 link 中明确声明您必须在清单中同时包含 READ 和 WRITE。然后你可以使用 运行 时间权限,这将涵盖两者,但前提是你将两者都包含在你的清单中。 (在 "Handle the permissions request response" 部分下)
从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 时默认不会授予。