1

I have a composable that is not recomposing in a Floating ComposeView but in an Activity, it works. In the Floating, only LazyColumn recomposes its items but outside of that. I've tried with mutableStateOf and MutableLiveData, MutableStateFlow in a ViewModel.

I think something is missing in the FloatingService's LifeCycle

Here is simple testable code: Composable

@Composable
fun Test() {
 var count by remember { mutableStateOf(0) }
 Log.d("TestComposable", "Recomposing: Count = $count")
 Column(
 modifier = Modifier.padding(16.dp)
 ) {
 Text("Count: $count")
 Button(onClick = {
 count++
 Log.d("TestComposable", "Button clicked: Count = $count")
 }) {
 Text("Increment")
 }
 }
}

Floating Service

class FloatingExecutionService : Service(), LifecycleOwner, ViewModelStoreOwner,
 SavedStateRegistryOwner {
 private val _lifecycleRegistry: LifecycleRegistry by lazy { LifecycleRegistry(this) }
 private val _store by lazy { ViewModelStore() }
 private lateinit var windowManager: WindowManager
 private lateinit var floatingView: ComposeView
 override val lifecycle: Lifecycle
 get() = _lifecycleRegistry
 override val viewModelStore: ViewModelStore
 get() = _store
 private val savedStateRegistryController = SavedStateRegistryController.create(this)
 override val savedStateRegistry: SavedStateRegistry =
 savedStateRegistryController.savedStateRegistry
 private fun handleLifecycleEvent(event: Lifecycle.Event) {
 Log.d("FloatingExecutionService", "Lifecycle Event: $event")
 _lifecycleRegistry.handleLifecycleEvent(event)
 }
 @CallSuper
 override fun onCreate() {
 super.onCreate()
 savedStateRegistryController.performRestore(null)
 handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
 windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
 floatingView = ComposeView(this).apply {
 setViewTreeSavedStateRegistryOwner(this@FloatingExecutionService)
 setViewTreeLifecycleOwner(this@FloatingExecutionService)
 setViewTreeViewModelStoreOwner(this@FloatingExecutionService)
 setContent {
 Test()
 }
 }
 val params = WindowManager.LayoutParams(
 WindowManager.LayoutParams.WRAP_CONTENT,
 WindowManager.LayoutParams.WRAP_CONTENT,
 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
 PixelFormat.TRANSLUCENT
 )
 params.gravity = Gravity.TOP or Gravity.START
 params.x = 0
 params.y = 0
 windowManager.addView(floatingView, params)
 floatingView.setOnTouchListener(object : View.OnTouchListener {
 private var initialX = 0
 private var initialY = 0
 private var initialTouchX = 0f
 private var initialTouchY = 0f
 override fun onTouch(v: View?, event: MotionEvent): Boolean {
 when (event.action) {
 MotionEvent.ACTION_DOWN -> {
 initialX = params.x
 initialY = params.y
 initialTouchX = event.rawX
 initialTouchY = event.rawY
 return true
 }
 MotionEvent.ACTION_MOVE -> {
 params.x = initialX + (event.rawX - initialTouchX).toInt()
 params.y = initialY + (event.rawY - initialTouchY).toInt()
 windowManager.updateViewLayout(floatingView, params)
 return true
 }
 }
 return false
 }
 })
 }
 override fun onDestroy() {
 handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
 super.onDestroy()
 if (::floatingView.isInitialized) {
 windowManager.removeView(floatingView)
 }
 }
 override fun onBind(intent: Intent?): IBinder? {
 return null
 }
 @CallSuper
 override fun onTaskRemoved(rootIntent: Intent?) {
 super.onTaskRemoved(rootIntent)
 stopSelf()
 }
}

AndroidManifest.xml

...
 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
 <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
...
 <service
 android:name=".service.FloatingExecutionService"
 android:enabled="true"
 android:exported="false"
 android:permission="android.permission.SYSTEM_ALERT_WINDOW" />

Libs

agp = "8.8.0"
kotlin = "2.0.0"
coreKtx = "1.15.0"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
composeBom = "2024年12月01日"
gradle-8.11.1-bin.zip
asked Feb 21, 2025 at 16:55
1

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.