Skip to content

Commit 8e39f76

Browse files
authored
Ensure isAppInForegroundAsState lifecycle observer removal occurs on the main thread (#6033)
* Ensure isAppInForegroundAsState lifecycle observer removal occurs on the main thread. * Update CHANGELOG.md.
1 parent 0e47e4e commit 8e39f76

File tree

2 files changed

+31
-2
lines changed

2 files changed

+31
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575

7676
## stream-chat-android-compose
7777
### 🐞 Fixed
78+
- Ensure `isAppInForegroundAsState` lifecycle observer removal occurs on the main thread. [#6033](https://github.com/GetStream/stream-chat-android/pull/6033)
7879

7980
### ⬆️ Improved
8081
- Fix `StrictMode` violations in the `AttachmentsPicker`. [#6029](https://github.com/GetStream/stream-chat-android/pull/6029)

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/util/IsAppInForegroundAsState.kt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616

1717
package io.getstream.chat.android.compose.util
1818

19+
import android.os.Handler
20+
import android.os.Looper
1921
import androidx.compose.runtime.Composable
2022
import androidx.compose.runtime.State
2123
import androidx.compose.runtime.produceState
2224
import androidx.lifecycle.Lifecycle
2325
import androidx.lifecycle.LifecycleEventObserver
2426
import androidx.lifecycle.compose.LocalLifecycleOwner
27+
import kotlinx.coroutines.Dispatchers
28+
import kotlinx.coroutines.withContext
2529

2630
/**
2731
* Produces a [State] that indicates whether the app is currently in the foreground.
@@ -38,7 +42,31 @@ internal fun isAppInForegroundAsState(): State<Boolean> {
3842
else -> value
3943
}
4044
}
41-
lifecycle.addObserver(observer)
42-
awaitDispose { lifecycle.removeObserver(observer) }
45+
withContext(Dispatchers.Main.immediate) {
46+
lifecycle.addObserver(observer)
47+
}
48+
awaitDispose {
49+
lifecycle.removeObserverOnMainThread(observer)
50+
}
51+
}
52+
}
53+
54+
/**
55+
* Removes a lifecycle observer on the main thread.
56+
*
57+
* [Lifecycle.removeObserver] must be called on the main thread. During normal app execution
58+
* this is typically the case, but in instrumentation tests this might be called on the
59+
* Compose test dispatcher thread. To avoid IllegalStateException, we ensure the removal
60+
* happens on the main thread.
61+
*
62+
* @param observer The [LifecycleEventObserver] to remove.
63+
*/
64+
private fun Lifecycle.removeObserverOnMainThread(observer: LifecycleEventObserver) {
65+
if (Looper.myLooper() == Looper.getMainLooper()) {
66+
removeObserver(observer)
67+
} else {
68+
Handler(Looper.getMainLooper()).post {
69+
removeObserver(observer)
70+
}
4371
}
4472
}

0 commit comments

Comments
 (0)