Hi AccA Team,
I’m a PhD student researching Android thread-related 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 AccA.
Checked target
- App: AccA (
mattecarra.accapp)
- Repository:
https://github.com/MatteCarra/AccA
- Source-level caller:
mattecarra.accapp.activities.AccConfigEditorActivity.onCreate(...)
- Additional helper:
mattecarra.accapp.acc.Acc.isInstalledAccOutdated()
- Detected API / pattern:
kotlinx.coroutines.runBlocking(...)
- Observed context in our scan: Android main/UI thread
- Expected behavior: avoid blocking Activity startup/UI setup paths
What I found
The latest develop source still calls runBlocking directly from AccConfigEditorActivity.onCreate(...) while preparing the editor state:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
val config = when {
savedInstanceState?.containsKey(Constants.ACC_CONFIG_KEY) == true ->
savedInstanceState.getSerializable(Constants.ACC_CONFIG_KEY) as AccConfig
intent.hasExtra(Constants.ACC_CONFIG_KEY) ->
intent.getSerializableExtra(Constants.ACC_CONFIG_KEY) as AccConfig
else -> try {
runBlocking {
Acc.instance.readConfig()
}
} catch (ex: Exception) {
ex.printStackTrace()
showConfigReadError()
runBlocking {
Acc.instance.readDefaultConfig()
}
}
}
...
}
The ACC helper also has a synchronous bridge:
fun isInstalledAccOutdated(): Boolean = runBlocking {
instance.getAccVersion()?.let { it < bundledVersion } ?: true
}
The important point is that runBlocking blocks the current thread until the coroutine completes. Therefore, when it is invoked from Activity.onCreate(...), the main/UI thread waits synchronously for the ACC configuration or version operation to complete.
Verified bug trace
User opens ACC config editor
-> AccConfigEditorActivity.onCreate(...)
-> load profile/config from Intent or saved state
-> fallback path calls runBlocking { Acc.instance.readConfig() }
-> error path calls runBlocking { Acc.instance.readDefaultConfig() }
-> UI thread is blocked until the ACC config read completes
-> possible slow Activity startup, jank, or ANR risk
Additional helper path:
UI/service path checking bundled ACC version
-> Acc.isInstalledAccOutdated()
-> runBlocking { instance.getAccVersion() }
-> caller thread waits synchronously
I did not dynamically reproduce an ANR. This should be treated as a source-confirmed main-thread blocking risk rather than a guaranteed crash. The risk depends on how fast the ACC backend/shell/config operations complete on a given rooted device.
I also searched the current GitHub issues for terms such as runBlocking, AccConfigEditorActivity, readConfig onCreate, and main thread readConfig, and did not find an existing matching issue.
Why this matters
onCreate(...) is part of Activity startup and normally runs on the main thread. Blocking it while reading configuration or querying the ACC backend can delay UI rendering and make the app appear frozen, especially on slow devices, after root/shell delays, or if the ACC backend is unavailable.
The coroutine body may call suspending APIs, but runBlocking still blocks the caller until the coroutine completes.
Possible fix
Avoid synchronously reading ACC state in onCreate(...). Instead, initialize the UI with a loading state and load the config from a lifecycle-aware coroutine:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
lifecycleScope.launch {
val config = withContext(Dispatchers.IO) {
try {
Acc.instance.readConfig()
} catch (ex: Exception) {
ex.printStackTrace()
null
}
}
if (config == null) {
showConfigReadError()
val defaultConfig = withContext(Dispatchers.IO) {
Acc.instance.readDefaultConfig()
}
initializeEditor(defaultConfig)
} else {
initializeEditor(config)
}
}
}
Similarly, isInstalledAccOutdated() could be made suspend, or expose an async API:
suspend fun isInstalledAccOutdated(): Boolean =
withContext(Dispatchers.IO) {
instance.getAccVersion()?.let { it < bundledVersion } ?: true
}
References
- Kotlin
runBlocking documentation: runBlocking blocks the current thread until completion.
- Android ANR guidance: blocking the main/UI thread can lead to unresponsiveness or ANRs.
Hi AccA Team,
I’m a PhD student researching Android thread-related 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 AccA.
Checked target
mattecarra.accapp)https://github.com/MatteCarra/AccAmattecarra.accapp.activities.AccConfigEditorActivity.onCreate(...)mattecarra.accapp.acc.Acc.isInstalledAccOutdated()kotlinx.coroutines.runBlocking(...)What I found
The latest
developsource still callsrunBlockingdirectly fromAccConfigEditorActivity.onCreate(...)while preparing the editor state:The ACC helper also has a synchronous bridge:
The important point is that
runBlockingblocks the current thread until the coroutine completes. Therefore, when it is invoked fromActivity.onCreate(...), the main/UI thread waits synchronously for the ACC configuration or version operation to complete.Verified bug trace
Additional helper path:
I did not dynamically reproduce an ANR. This should be treated as a source-confirmed main-thread blocking risk rather than a guaranteed crash. The risk depends on how fast the ACC backend/shell/config operations complete on a given rooted device.
I also searched the current GitHub issues for terms such as
runBlocking,AccConfigEditorActivity,readConfig onCreate, andmain thread readConfig, and did not find an existing matching issue.Why this matters
onCreate(...)is part of Activity startup and normally runs on the main thread. Blocking it while reading configuration or querying the ACC backend can delay UI rendering and make the app appear frozen, especially on slow devices, after root/shell delays, or if the ACC backend is unavailable.The coroutine body may call suspending APIs, but
runBlockingstill blocks the caller until the coroutine completes.Possible fix
Avoid synchronously reading ACC state in
onCreate(...). Instead, initialize the UI with a loading state and load the config from a lifecycle-aware coroutine:Similarly,
isInstalledAccOutdated()could be madesuspend, or expose an async API:References
runBlockingdocumentation:runBlockingblocks the current thread until completion.