Checked for duplicates?
What are the steps to reproduce this bug?
Hi AnkiDroid Team,
I’m a PhD student researching Android performance issues. My research group recently ran a static analysis scan for thread-affinity and main-thread blocking bugs in real-world Android apps, and our prototype flagged a potential issue in AnkiDroid.
This report has been strictly re-audited against the latest default branch source listed below, rather than only relying on the older commit URL from the initial static-analysis export. I did not dynamically reproduce an ANR/crash, so this should be treated as a source-confirmed main-thread blocking risk rather than a reproduced runtime failure.
App version
Current default branch: ankidroid/Anki-Android@main
Checked target
- Repository:
ankidroid/Anki-Android
- Corrected caller:
MultimediaImageFragment#handleImageUri/internalizeUri -> MultimediaUtils#getImageNameFromUri
- Detected API / pattern:
ContentResolverCompat#query(...) and Cursor#moveToFirst()
- Underlying platform APIs:
ContentResolver#query(...), Cursor#moveToFirst()
- Observed context: multimedia image selection/import UI path
- Expected context: worker/background thread before content-provider metadata lookup
What I found
The latest multimedia image flow still resolves a selected content URI’s display name synchronously. MultimediaImageFragment calls internalizeUri(...) from UI/lifecycle paths, and internalizeUri(...) calls MultimediaUtils.getImageNameFromUri(...). For content/document URIs, that function queries the content resolver and moves the cursor to the first row.
Verified source trace
User selects or passes an image into MultimediaImageFragment
-> MultimediaImageFragment.handleImageUri()
-> processExternalImage(uri)
-> internalizeUri(uri)
-> MultimediaUtils.getImageNameFromUri(requireContext(), uri)
-> getImageNameFromContentResolver(...)
-> ContentResolverCompat.query(...)
-> cursor.moveToFirst()
-> display name is used to create cached/internal file
Why this matters
Image import often involves external document providers, downloads, gallery providers, or cloud-backed providers. Resolving display names synchronously can block the multimedia editor while the user is waiting for the selected image to appear.
Android’s ANR guidance lists slow I/O, long calculations, and synchronous Binder calls on the main thread as common ANR patterns. In this case, the risky operation is user-visible because it runs from an Activity/Fragment/View/service lifecycle path or a direct UI action path.
Possible fix
Move display-name resolution and file internalization into Dispatchers.IO. The fragment should show loading/progress state and update preview only after the URI has been copied/resolved.
A typical Android pattern is to move the blocking work to an IO/background dispatcher or executor, then publish only the final UI state on the main thread, for example:
lifecycleScope.launch {
val result = withContext(Dispatchers.IO) {
// perform database/content-provider/file/media work here
}
// update UI here on the main thread
}
For non-UI classes, a dedicated executor, repository-level coroutine scope, or existing background worker would also work. The important point is to avoid performing the blocking operation synchronously on the main thread.
Reference
Android ANR guidance:
https://developer.android.com/topic/performance/vitals/anr
Source references
Research
Checked for duplicates?
What are the steps to reproduce this bug?
Hi AnkiDroid Team,
I’m a PhD student researching Android performance issues. My research group recently ran a static analysis scan for thread-affinity and main-thread blocking bugs in real-world Android apps, and our prototype flagged a potential issue in AnkiDroid.
This report has been strictly re-audited against the latest default branch source listed below, rather than only relying on the older commit URL from the initial static-analysis export. I did not dynamically reproduce an ANR/crash, so this should be treated as a source-confirmed main-thread blocking risk rather than a reproduced runtime failure.
App version
Current default branch:
ankidroid/Anki-Android@mainChecked target
ankidroid/Anki-AndroidMultimediaImageFragment#handleImageUri/internalizeUri -> MultimediaUtils#getImageNameFromUriContentResolverCompat#query(...)andCursor#moveToFirst()ContentResolver#query(...), Cursor#moveToFirst()What I found
The latest multimedia image flow still resolves a selected content URI’s display name synchronously.
MultimediaImageFragmentcallsinternalizeUri(...)from UI/lifecycle paths, andinternalizeUri(...)callsMultimediaUtils.getImageNameFromUri(...). For content/document URIs, that function queries the content resolver and moves the cursor to the first row.Verified source trace
Why this matters
Image import often involves external document providers, downloads, gallery providers, or cloud-backed providers. Resolving display names synchronously can block the multimedia editor while the user is waiting for the selected image to appear.
Android’s ANR guidance lists slow I/O, long calculations, and synchronous Binder calls on the main thread as common ANR patterns. In this case, the risky operation is user-visible because it runs from an Activity/Fragment/View/service lifecycle path or a direct UI action path.
Possible fix
Move display-name resolution and file internalization into
Dispatchers.IO. The fragment should show loading/progress state and update preview only after the URI has been copied/resolved.A typical Android pattern is to move the blocking work to an IO/background dispatcher or executor, then publish only the final UI state on the main thread, for example:
lifecycleScope.launch { val result = withContext(Dispatchers.IO) { // perform database/content-provider/file/media work here } // update UI here on the main thread }For non-UI classes, a dedicated executor, repository-level coroutine scope, or existing background worker would also work. The important point is to avoid performing the blocking operation synchronously on the main thread.
Reference
Android ANR guidance:
https://developer.android.com/topic/performance/vitals/anr
Source references
Research