简体   繁体   中英

(Not Recuisive) Get list file type in android java

I have an issue for months, need to show all list file path (or Uri) of file ".epub" on my phone (SD-card, memory card). I use this to get root Uri (user pick folder):

public void onActivityResult(ActivityResult result) {
                    if (result.getResultCode() == Activity.RESULT_OK) {
                        Intent data = result.getData();
                        resultUri = data.getData();

The result uri will looks like: content://com.android.externalstorage.documents/tree/primary%3ABooks

I used a recursive function like this to fetch that Uri, but the function run very slow for result:

 DocumentFile F0 = DocumentFile.fromTreeUri(context, inputUri);
                if (F0 != null) {
                    DocumentFile[] F1 = F0.listFiles();
                    for (DocumentFile F2 : F1) {
                        recuisiveUri(F2);
                    }
                }

private void recuisiveUri(DocumentFile F2) {
        Log.d(TAG, "recuisiveUri: F2 name = " + F2.getName()) ;
        if(F2.canRead()
                && F2.exists()
                && F2.canWrite()
                && F2.canRead()
                && !F2.getName().contains("Android")
                && !F2.getName().contains("data")
                && !F2.getName().contains("DCIM")
                ){

            if (F2.isDirectory()) {
                DocumentFile[] F3 = F2.listFiles();
                for (DocumentFile F4 : F3) {
                    recuisiveUri(F4);
                }
            } else {
                if (Objects.requireNonNull(F2.getName()).endsWith(".epub"))
                    listUri.add(F2.getUri());
            }
        }
    }

Anyone if you have better solution (faster) or found something wrong in my code, please help me, thank very a lot!

The problem is DocumentFile it is even specified in documentation that it is very slow:

It offers a simplified view of a tree of documents, but it has substantial overhead. For optimal performance and a richer feature set, use the DocumentsContract methods and constants directly.

also read this blog post: Scoped Storage Stories: DocumentsContract

As this blog entry and android documentation (above) suggests you should work directly with DocumentsContract. Actually what you should do is to write your own listFiles but optimized - see answer here https://stackoverflow.com/a/66296597/471160

[edit]

Below is a code which minimizes use of ContentProvider to list recursively file tree (but it does not use recursion - but a stack collection).

In your code replace:

DocumentFile[] F1 = F0.listFiles();
for (DocumentFile F2 : F1) {
  recuisiveUri(F2);
}

with:

listUri = traverseDirectoryEntries(resultUri);

Code is as follows (its based on Issues traversing through directory hierarchy with Android Storage Access Framework / DocumentProvider using MTP ). I have not tested it on huge folder hierarchies - only on emulator. I am not sure if it properly filters for ".epub" - I think you should be able to handle it.

ArrayList<Uri> traverseDirectoryEntries(Uri rootUri) {
    ArrayList<Uri> listUri = new ArrayList<>();

    ContentResolver contentResolver = this.getContentResolver();
    Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri,
            DocumentsContract.getTreeDocumentId(rootUri));

    // Keep track of our directory hierarchy
    List<Uri> dirNodes = new LinkedList<>();
    dirNodes.add(childrenUri);

    while(!dirNodes.isEmpty()) {
        childrenUri = dirNodes.remove(0); // get the item from top
        Log.d(LOG_TAG, "node uri: " + childrenUri);
        Cursor c = contentResolver.query(childrenUri, new String[]{
                DocumentsContract.Document.COLUMN_DOCUMENT_ID,
                DocumentsContract.Document.COLUMN_DISPLAY_NAME,
                DocumentsContract.Document.COLUMN_MIME_TYPE},
                null, null, null);
        try {
            while (c.moveToNext()) {
                final String docId = c.getString(0);
                final String name = c.getString(1);
                final String mime = c.getString(2);
                Log.d(LOG_TAG, "docId: " + docId + ", name: " + name + ", mime: " + mime);
                if(isDirectory(mime)) {
                    final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId);
                    dirNodes.add(newNode);
                }
                else {
                    if (name.contains(".epub")) { // maybe you should check mime here?
                        final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId);
                        listUri.add(newNode);
                    }
                }
            }
        } finally {
            closeQuietly(c);
        }
    }
    return listUri;
}

// Util method to check if the mime type is a directory
private static boolean isDirectory(String mimeType) {
    return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
}

// Util method to close a closeable
private static void closeQuietly(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (RuntimeException re) {
            throw re;
        } catch (Exception ignore) {
            // ignore exception
        }
    }
}

I think your problem is fairly simple provided you are comfortable with Kotlin.

Here's how you can get all.epub files in a directory:

val rootDir = File(uri.path) // uri pointing towards the chosen directory
val allEpubFiles = root.list { dir, name -> name.endsWith(".epub") }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM