Android Q (10)要求获取访问所有存储权限。Scoped storage

27

在Android 10中,需要访问存储器的应用程序需要请求访问具体路径的权限。但是像文件浏览器这样的应用程序可以请求访问根存储器的权限,并获得读写所有存储器的权限。那就是我想做的。

根据Android的规定,我们必须使用 ACTION_OPEN_DOCUMENT_TREE 来实现这一点。我的问题是,虽然一切似乎都正确,但是该应用程序未被授予权限。

  private void askAndroid10Perm()
    {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
        intent.addFlags(
                Intent.FLAG_GRANT_READ_URI_PERMISSION
                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
        startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT_TREE);
    }

用户可以在这里查看Android文件树,并选择主存储,点击授权。"这将允许 - 应用程序名称 - 对当前存储在此位置下的所有文件以及未来存储在此处的任何内容拥有完全访问权限"-> 允许

然后:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case REQUEST_CODE_OPEN_DOCUMENT_TREE:
            if (resultCode == Activity.RESULT_OK) {
                Uri treeUri = data.getData();
                int takeFlags = data.getFlags();
                takeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION |
                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

                    Log.i("TAG", "takePersistableUriPermission: " + treeUri);
                    this.getContentResolver().takePersistableUriPermission(treeUri, takeFlags);

                }

            }
    }
}

日志:

takePersistableUriPermission: content://com.android.externalstorage.documents/tree/primary%3A

那么该应用程序仍然没有访问根目录的权限。

open failed: EACCES(拒绝访问权限)

当然我已经具备了:

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

由用户授权。


我认为在Android Q中你无法获得root访问权限。 - Gaurav Mall
我测试过的一些应用程序已经使用了作用域存储来访问。例如,“Solid Explorer” 应用程序。 - user2983041
1
是的,我知道。我曾经问过类似的问题,CommonWare告诉我那是因为这些公司拥有的工程师。我知道,他的回答并不是很有用。 - Gaurav Mall
什么是 root 访问权限? - greywolf82
访问设备存储文件。这些文件与预装的默认文件浏览器应用程序相同。我不是在谈论系统根目录文件。挂载设备、外部SD卡和内部存储的根目录。 - user2983041
你可以做到,但是你在问题中没有展示如何尝试“打开”根目录。你可以使用DocumentFile类,然后使用list files。 - greywolf82
5个回答

42
在清单文件中添加一行。
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:requestLegacyExternalStorage="true" //Add this Line
    android:label="@string/app_name">

---------<Activity, Sevices or Recivers>----------

</application>

运行得非常顺利! - Mohd Zaid
4
这只是暂时的,请访问https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage。 - dominikkv
那刚刚浪费了我三个小时的生命...谢谢D_K。 - Simon Corcos
在Android 11中,它将不再起作用。那么,在Android 11上如何访问完整的存储访问权限?有什么想法吗? - Chirag Prajapati
你必须在Android 11或更高版本中使用MediaStore。请查看以下链接:https://developer.android.com/training/data-storage/use-cases - D_K

14

您必须在AndroidManifest.xml中添加此行代码。

android:requestLegacyExternalStorage="true"

8
问题在于我使用File对象来访问文件,但是只有使用DocumentFile对象才能操作文件时生效的权限。使用DocumentFile代替File解决了这个问题。
使用DocumentFile具有多个负面点,如性能和没有访问apache commons库的权限等,但是没有其他选择。

File(path),file.delete(),mkdirs()在Android Q上不起作用吗?Scoped Storage的改变是什么? - Aswin P Ashok
3
Java有一些库可以在“File”对象中复制、剪切或压缩目录,但是这些库并不存在于DocumentFile中。 - user2983041
Android 10 中的文件 API 不支持访问共享存储中您的应用程序没有所有权的文件。请参考 https://developer.android.com/training/data-storage/shared/media#direct-file-paths。 - M. Reza Nasirloo

1

如果使用简单存储,就会更加简单。您可以向用户请求完全访问存储空间:

class MainActivity : AppCompatActivity() {

    private val storageHelper = SimpleStorageHelper(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btnRequestStorageAccess.setOnClickListener {
            storageHelper.requestStorageAccess()
        }

        storageHelper.onStorageAccessGranted = { requestCode, root ->
            // do stuff
        }
    }
}

它适用于Android 10及更高版本。无需将android:requestLegacyExternalStorage="true"添加到清单文件中。

0
(不适用于Android 11)将以下内容添加到应用程序的清单文件中,以使您的应用程序如下所示:
<application
    android:requestLegacyExternalStorage="true"

对于安卓11,我是这样做的

        ContextWrapper cw = new ContextWrapper(mContext);
String fullPath =cw.getExternalFilesDir(Environment.DIRECTORY_MUSIC).toString();
File directory = cw.getExternalFilesDir(Environment.DIRECTORY_MUSIC);

点击此处查看 https://www.programmersought.com/article/50766505696/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接