Maven Centralcom.lazygeniouz:dfc

DocumentFile, minus the wait.

A drop-in for AndroidX DocumentFile on Storage Access Framework trees. It reads metadata while listing, so getName(), length() and findFile() stop costing a provider query each.

implementation "com.lazygeniouz:dfc:<version>"
faster listing
13.7×
down from 48s
3.5s
minimum SDK
API 21+
query per listing
1

Benchmark

Large folders are where the gap opens.

The sample app lists the same SAF tree with both libraries. The bars below race at the real ratio from one run: about 48 seconds for DocumentFile, about 3.5 for DocumentFileCompat.

listFiles() on a large directory
DocumentFile48.0s
DocumentFileCompat3.5s
13.7×

48 ÷ 3.5 on that listing

Numbers depend on the provider, device and folder size. DFC isn't competing with java.io.File; it makes SAF-backed access less painful.

How it works

Every getter is a round trip.

With DocumentFile, each getter on each child goes back to the provider. DFC asks for those columns in the listing cursor, so the data is already there when you read it.

DocumentFile81

1 listing query, then name, size, modified and type per file.

DocumentFileCompat1

1 listing query that returns those columns together.

20 files
  • listing query
  • per-file getter query
  • DFC listing with metadata
  • Illustrative; real counts depend on what your code reads.

Migrate

Change the factory. Keep the rest.

The API mirrors AndroidX. The main change is how you create the first instance.

  • fromTreeUri, fromSingleUri and fromFile entry points
  • Familiar getters and methods: listFiles, findFile, length
  • Extras when you want them: count(), copyTo(), copyFrom()
FolderRepository.kt
-import androidx.documentfile.provider.DocumentFile+import com.lazygeniouz.dfc.file.DocumentFileCompat -val dir = DocumentFile.fromTreeUri(context, treeUri) ?: return+val dir = DocumentFileCompat.fromTreeUri(context, treeUri) ?: return val files = dir.listFiles() val report = dir.findFile("report.pdf")  // lighter queries when you only need a few columns val slim = dir.listFiles(projection)

What it supports

The API you know, plus a few extras.

Everything in one small Kotlin library, no extra dependencies to wire up.

fromTreeUri(context, treeUri)

Tree URIs

Open a folder from ACTION_OPEN_DOCUMENT_TREE. Names, sizes and types arrive with the listing.

fromSingleUri(context, uri)

Single documents

Wrap one picked file and read its metadata without extra lookups.

fromFile(context, file)

Raw File access

The same interface over java.io.File, so callers don't care what backs it.

observe { event, doc -> }

Directory observation

Event-driven watching of a tree's direct children, no polling loop.

listFiles(projection)

Custom projections

Ask only for the columns you need and keep queries lighter still.

count() · copyTo(destination) · copyFrom(source)

Helpers

Count a folder's children, or copy a document to or from another one in a single call.

observe()

Watch a folder without polling.

Create, delete, move and change events for a SAF tree's direct children, driven by provider change notifications.

Downloads/watching
Observer.ktworker thread
val observer = directory.observe { event, document ->
    println("$event: ${document.name}")
}

observer.startWatching(
    onError = { error -> /* why watching stopped */ },
    onReady = { /* initial snapshot is ready */ },
)

// later, from the owning lifecycle
observer.close()
  • The initial scan isn't emitted. onReady runs once it finishes.
  • Rapid changes may be combined into one refresh.
  • Moves need stable document IDs; otherwise a rename arrives as delete plus create.
Copied to clipboard