Skip to content

Scan the file chooser's directories off the UI thread - #267

Merged
xroche merged 2 commits into
masterfrom
fix-filechooser-anr
Oct 31, 2026
Merged

xroche merged 2 commits into
masterfrom
fix-filechooser-anr

Conversation

@xroche

@xroche xroche commented Oct 31, 2026

Copy link
Copy Markdown
Owner

Tapping a directory listed it on the UI thread, and stated every entry. A large or slow directory held the thread past the ANR budget. Play has one such ANR on vc107, a Pixel 9a parked in UnixFileSystem.list0 under FileChooserActivity.refreshDir.

The listing moves to a worker thread. Each scan takes a ticket, and a later tap stops the earlier scan before it lists or draws. Otherwise a slow listing draws over the directory just opened.

xroche and others added 2 commits October 31, 2026 21:12
listFiles() plus a stat on every entry ran on the UI thread, so a large
or slow directory held it past the ANR budget. Play reports one such ANR
on vc107, a Pixel 9a parked in UnixFileSystem.list0 under
FileChooserActivity.refreshDir.

DirScanner does the listing on a worker thread and hands the result back
through the activity's handler. Each scan takes a ticket, so a scan the
next tap has superseded neither lists nor draws: a slow listing started
earlier would otherwise draw over the directory that tap opened.

The parent entry no longer compares the parent's path with the
directory's. getParentFile() answers null at the root, which is the only
case that comparison caught.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <xroche@gmail.com>
The review found theChooserNeverListsOnItsOwn vacuous. It greps the
activity for listFiles(. A refreshDir body of
showDir(scanner.list(...)) passes that grep and lists on the UI thread
again, which is the bug this PR exists to remove. So list() is private
now, and that mutant stops at the compiler. The tests reach a listing
through scan() with a direct executor instead.

The counting seam moves with it. No test subclasses DirScanner any
more, so the class is final like its peers here. A CountedDir over
java.io.File counts the listings a superseded scan never does. It also
fixes what one listing returns, so a test can pin the order the chooser
offers.

Three smaller findings. The two null guards no caller can reach are
gone. The root fallback is one named constant. The draw is private,
because a public onListing let any class in the package drive the views
past the ticket check.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <xroche@gmail.com>
@xroche
xroche enabled auto-merge (squash) October 31, 2026 20:28
@xroche
xroche merged commit 2a12b8e into master Oct 31, 2026
7 checks passed
@xroche
xroche deleted the fix-filechooser-anr branch October 31, 2026 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant