Android 11:DocumentFile findFile() 太慢

Android 11: DocumentFile findFile() is too slow

随着 Google 存储策略的更新,我不得不改变我保存文件的方式。

首先,我的应用程序之前将项目文件夹保存到/sdcard/。但是,由于这个政策,无法使用文件访问,我尝试了几种方法。

我也想过把它保存在一个App专用文件里space,但是由于删除app时项目文件夹不能被删除,所以不能用

1.使用 MediaStore

起初,我尝试使用 MediaStore。 我可以使用RELATIVE_PATH将文件放在一个文件夹中,但是文件的控制非常困难,而且项目文件夹中的文件也是与媒体(照片,视频,音乐)无关的文件.

2。使用文档文件

我找到了另一种方法。使用 Intent 的 ACTION_OPEN_DOCUMENT_TREE,我从用户那里获得了项目文件夹的访问权限和树 uri,并且我能够通过使用它创建一个 DocumentFile 来访问该文件。

起初我认为这是一个很好的方法,但很快我发现它很慢。

特别是findFile()方法,扫描单个文件需要将近一秒的时间。 我的项目文件夹中有几十个文件,所以不能商业化。

有没有更好的方法? 已经有一些帖子要求您试用 DocumentContracts,但似乎没有获取文件列表或检查它们是否存在的功能。

下面是我当前的代码: 首先,请求树uri:

public void getTreeUri(View view) {
    Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    startActivityForResult(i, 200);
}

其次,获取树uri:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode==200 && resultCode == Activity.RESULT_OK) {
        final int takeFlags = data.getFlags()  & (Intent.FLAG_GRANT_READ_URI_PERMISSION  | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        getContentResolver().takePersistableUriPermission(data.getData(), takeFlags);

        Log.d("test", "Tree Uri: " + data.getDataString());

        treeUri = data.getDataString();
        getFileList();
    }
}

而且,特别是,当像这样使用 findFile 时它变得非常慢。

public void getFileList(){
    DocumentFile df = DocumentFile.fromTreeUri(this, Uri.parse(treeUri));
    DocumentFile[] df_list = df.listFiles();
    for (DocumentFile each : df_list){
        Log.d("test","File: "+ df.findFile(each.getName()));  //findFile method is too SLOW!
    }
}

对于 Android 10,您可以在清单中请求旧的外部存储,然后您可以使用文件 class 等以旧方式继续。

对于 Android 11,在 public 目录之一中使用您自己的目录,例如下载、文档、图片、警报等。也不需要 saf。

SAF确实很慢,findFile()算法没有优化的时候更慢。根据 my benchmark with extension function DocumentFile.child() from SimpleStorage,它的执行速度可以提高 36%。

要使用此功能,只需将 findFile() 替换为 child():

// Before
val file = folder.findFile("Movies")?.findFile("Infinity War.mp4")

// After
val file = folder.child("Movies/Infinity War.mp4")