mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-13 22:03:13 -06:00
Clear ProvisioningActivity's service callbacks (the white-flash relaunch loop)
Reported on #234 as a screen that flashes white "over and over", unkillable — "there is nothing we can do on the tablet". It is a leaked listener. ProvisioningActivity installs onRegistered/onUnpaired/onPaired on WebSocketService and then finish()es. The service outlives it and nothing ever clears them: MainActivity assigns neither of those three, so nothing overwrites them either. onPaired therefore stays wired to a destroyed Activity for the life of the process — keeping it alive, and still firing. And it fires often. The server sends device:paired on EVERY register, not only the first. So: register -> paired -> the stale callback starts MainActivity with CLEAR_TASK -> new Activity binds and registers -> paired -> again. Measured on an Android 12 tablet with a bare paired device and nothing assigned: 240 activity starts in 180 seconds, about 1.3 a second, indefinitely. Android 12 is where it becomes intolerable rather than merely wasteful: every launch draws a splash screen there, so each iteration is a visible white flash. The same loop on Android 9 has no splash and reads as an occasional glitch — which is why it was originally dismissed as unreproducible after a clean reinstall. A clean reinstall starts MainActivity directly and never runs ProvisioningActivity, so the callback is never installed and the loop never begins. Pairing is what arms it. onPaired is now one-shot — the hand-off to MainActivity is all it was ever for — and all three are dropped in onDestroy too, which covers backing out before pairing completes. Same device, same pairing flow, 180s: 240 activity starts and 240 splash screens before, 0 and 0 after, with registrations falling from 240 to 2. ⚠️ No other callback is ever nulled either (there are ~20). MainActivity's are overwritten by the next MainActivity so they self-heal, but each one leaks the previous Activity until then. Worth a sweep; this commit fixes only the three that never get overwritten.
This commit is contained in:
parent
ce626c7e8e
commit
83c9bc5aa6
|
|
@ -288,6 +288,13 @@ class ProvisioningActivity : AppCompatActivity() {
|
|||
|
||||
wsService?.onPaired = { deviceId, name ->
|
||||
runOnUiThread {
|
||||
// ONE-SHOT. This callback's only job is the hand-off to MainActivity, but it was
|
||||
// being left installed on a service that OUTLIVES this Activity — and the server
|
||||
// sends device:paired on EVERY register, not just the first. So each register
|
||||
// relaunched MainActivity with CLEAR_TASK, which re-registered, which paired again:
|
||||
// a self-sustaining loop, ~1.3 relaunches/second measured on Android 12. On 12+ every
|
||||
// launch draws a splash screen, which is the "white screen flashing" users report.
|
||||
clearServiceCallbacks()
|
||||
cancelStuckTimer()
|
||||
stopRepairTicker()
|
||||
statusText.text = "Paired as: $name"
|
||||
|
|
@ -298,6 +305,19 @@ class ProvisioningActivity : AppCompatActivity() {
|
|||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the callbacks this Activity installed on the long-lived service. MainActivity never
|
||||
* assigns onRegistered/onUnpaired/onPaired, so nothing else would ever overwrite them — they
|
||||
* would keep firing into a destroyed Activity for the life of the process (and keep it alive).
|
||||
*/
|
||||
private fun clearServiceCallbacks() {
|
||||
try {
|
||||
wsService?.onPaired = null
|
||||
wsService?.onUnpaired = null
|
||||
wsService?.onRegistered = null
|
||||
} catch (_: Throwable) { }
|
||||
|
||||
// Re-pair path: the socket is usually already up (service kept running). Make sure it's
|
||||
// connecting, then render any pairing code the service already issued (race-free). If we're
|
||||
|
|
@ -315,6 +335,10 @@ class ProvisioningActivity : AppCompatActivity() {
|
|||
override fun onDestroy() {
|
||||
cancelStuckTimer()
|
||||
stopRepairTicker()
|
||||
// Before unbinding: the service keeps running, so a callback left pointing here would both
|
||||
// leak this Activity and keep firing. Matters when pairing never completes and the user
|
||||
// backs out, where the one-shot clear above never runs.
|
||||
clearServiceCallbacks()
|
||||
if (bound) {
|
||||
unbindService(connection)
|
||||
bound = false
|
||||
|
|
|
|||
Loading…
Reference in a new issue