androidandroid-filestorage-access-frameworkandroid-document-provider

DocumentFile.canWrite(), DocumentFile.exists() - poor performance (takes too much CPU time) when working with local built-in phone storage (not cloud)


I'm not really sure but why the following DocumentFile's exist and canWrite functions takes too much CPU time compare to File when working with built-in phone external storage (it's not a cloud)?

It's like ~8-30 ms per function call. It should be at least the same (the same storage).

For File it usually doesn't go higher 10 ms and average is usually less than 10 ms.

Tested on Samsung S10 (Android 12) and on Samsung S22 Ultra (Android 13) and still performance is quite bad when using SAF

Updated omg, there is another function which works even worse - DocumentFile.findFile(), it basically takes ages though the folder contains 0-2 files, insane...

Updated 2: yes, never use DocumentFile, do all operations with DocumentContract with your Uris but still even this one needs some optimization in the code to not be used a lot (like implementation of listFiles and so on), also if you know that file at the specific location and has the specific name then you can combine your own Uri instead of trying to get them with ContentResolver which is eating CPU as hell

Updated 3: Google is a joke - https://github.com/aosp-mirror/platform_frameworks_support/blob/d4003121f8a9f65ee072f4e32d21f4a97de0227e/documentfile/src/main/java/androidx/documentfile/provider/TreeDocumentFile.java#L144

@Override
public DocumentFile[] listFiles() {
    final ContentResolver resolver = mContext.getContentResolver();
    final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(mUri,
            DocumentsContract.getDocumentId(mUri));
    final ArrayList<Uri> results = new ArrayList<>();

    Cursor c = null;
    try {
        c = resolver.query(childrenUri, new String[] {
                DocumentsContract.Document.COLUMN_DOCUMENT_ID }, null, null, null);

From https://www.reddit.com/r/androiddev/comments/bbejc4/caveats_with_documentfile/

For example, an app might use DocumentFile.listFiles() to get the files in a folder. While the individual call for it isn't that bad, this call only gets the document ids for the children, and naively iterating over each of the children to get the name will be super expensive (as each child will need to query the content provider again, incurring the overhead costs again).

To avoid this, content providers should be queried yourself and include the display name in the projection:

cursor = contentResolver.query(
    childrenUri, 
    new String[]{
        DocumentsContract.Document.COLUMN_DISPLAY_NAME,
        DocumentsContract.Document.COLUMN_MIME_TYPE,
        DocumentsContract.Document.COLUMN_DOCUMENT_ID}, 
    null,
    null,
    null);

Why did they even implemented this DocumentFile at all? I spent a lot of time to switch from File to DocumentFile and then I didn't expect the app to work so slow because of that piece of we know what. If there was initially only DocumentContract I would use this one strait away... though it's also not perfect, for example if you want to find some file in folder then instead of getting all children and matching the name we can just concertante folder's Uri string + file's name we try to find and it will be much faster to try get the data of the file (size) directly or check if file exists


Solution

  • DocumentFile is about twenty times slower than the classic File class.

    Use DocumentsContract instead.

    It has about the same speed as File class.