2

With this update of Google's storage policy, I had to change the way I save files.

First, my application previously saved the project folder to /sdcard/. However, due to this policy, access using File became impossible, and I tried several methods.

I also thought about saving it in an App-specific files space, but since the project folder should not be deleted when deleting the app, it cannot be used.

1. Using MediaStore

At first, I tried using MediaStore. I was able to put files in a folder by using RELATIVE_PATH, but the control of the files was very difficult, and the files in the project folder are also files that are not related to media (photos, videos, music).

2. Using DocumentFile

I found another way. Using Intent's ACTION_OPEN_DOCUMENT_TREE, I got access rights and tree uri of the project folder from the user, and I was able to access the file by creating a DocumentFile with it.

At first I thought this was a very good way to do it, but soon I found it to be slow.

In particular, for the findFile() method, it takes nearly a second to scan a single file. There are dozens of files in my project folder, so this couldn't be commercialized.

Is there a better way? There have been some posts asking you to try out DocumentContracts, but it seems that there is no function to get a list of files or check their existence.

Below is my current code: First, request tree uri:

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

Second, get tree 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();
    }
}

And, in particular, it gets very slow when findFile is used like this.

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!
    }
}
rosered65
  • 75
  • 1
  • 6
  • Did you read this: https://developer.android.com/training/data-storage/use-cases ? – Dan Baruch Nov 15 '20 at 08:30
  • `There have been some posts asking you to try out DocumentContracts, but it seems that there is no function to get a list of files or check their existence.` Of course there is. And about 20 times faster then DocumentFile. – blackapps Nov 15 '20 at 09:36
  • Thank you. This document also helped me. – rosered65 Nov 16 '20 at 08:48

2 Answers2

1

SAF is slow indeed, and it is even slower when findFile() algorithm is not optimized. According to my benchmark with extension function DocumentFile.child() from SimpleStorage, it could perform 36% faster.

To use this function, simply replace findFile() with child():

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

// After
val file = folder.child("Movies/Infinity War.mp4")
Anggrayudi H
  • 14,977
  • 11
  • 54
  • 87
0

For Android 10 you can request legacy external storage in manifest and you can continue in the old way using File class and so.

For Android 11 use your own directory in one of the public dirs like Download, Documents, Pictures, Alarms and so on. No need for saf also.

blackapps
  • 8,011
  • 2
  • 11
  • 25
  • Thank you for answer. I've tried the following in a public directory and it works fine. `new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + "file.txt")` But it says that `Environment.getExternalStoragePublicDirectory()` is Depricated, is there any problem? – rosered65 Nov 16 '20 at 08:47
  • No. If it works there is no problem. Further you should distinguish between 10 and 11. Unclear why you did not. – blackapps Nov 16 '20 at 08:52
  • 1
    I agree with: `MediaStore`: "control of the files was very difficult," and `DocumentFile` is terrible slooow. Both are unusable. Good topic: https://www.reddit.com/r/androiddev/comments/mwaqn1/scoped_storage_recap/?sort=new – t0m Apr 29 '21 at 11:16
  • Use DocumentsContract instead of DocumentFile. Its about twenty times as fast and about as fast as the File class. – blackapps Apr 29 '21 at 11:20