Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compose as LifecycleOwner #1198

Merged
merged 6 commits into from
Mar 22, 2024
Merged

Conversation

MatkovIvan
Copy link
Member

@MatkovIvan MatkovIvan commented Mar 16, 2024

Proposed Changes

Testing

Test: Observe lifecycle events in mpp

Fixed Issues

Fixes JetBrains/compose-multiplatform#2915

@MatkovIvan MatkovIvan requested a review from igordmn March 16, 2024 12:02

This comment was marked as resolved.

@MatkovIvan MatkovIvan force-pushed the ivan.matkov/compose-lifecycle-owner branch 2 times, most recently from f8fdda2 to 8aa573d Compare March 18, 2024 13:04
Copy link
Collaborator

@igordmn igordmn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks!

I agree with the states that we need to set on different events, but we need to set them carefully to avoid bugs. See my comments, I suggest to create a common class that routes events the right way.

}

companion object {
fun logWarning(message: String) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fun logWarning(message: String) {
private fun logWarning(message: String) {

fail("Invalid '$action' for 'State.Disposed'")
}
Action.APPLICATION_DID_ENTER_BACKGROUND, Action.APPLICATION_WILL_ENTER_FOREGROUND -> {
// no-op
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we fail as well? These actions shouldn't be fired because we unsubscribe from them:

    fun dispose() {
        handleAction(Action.DISPOSE)
        applicationStateListener.dispose()
    }

import androidx.lifecycle.LifecycleRegistry
import kotlin.test.fail

internal class ViewControllerBasedLifecycleOwner : LifecycleOwner {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should write a unit test for this class, as here can be lot of bugs regarding non-symmetric events.

Comment on lines +56 to +62
if (isApplicationForeground) {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
}

lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)

Created(isApplicationForeground = isApplicationForeground, lifecycle = lifecycle)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (isApplicationForeground) {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
}
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
Created(isApplicationForeground = isApplicationForeground, lifecycle = lifecycle)
this

It doesn't sound right to emit Pause/Stop from Created state. If we in this state, it means we didn't receive VIEW_WILL_APPEAR, and so - didn't emit Start/Resume before.

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import kotlin.test.fail
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the test library in the dependencies?

We should remove it, and use error() instead

}

init {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the class itself is created after view is visible? We need to send ON_START or ON_RESUME

@@ -227,9 +255,13 @@ internal class ComposeContainer(
// Re-checking the actual size if it wasn't available during init.
onChangeWindowSize()
onChangeWindowPosition()

lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We set started state on 2 different states:

  • when it is iconified (minimized)
  • when it is added to window hierarchy.

So, we have 2 sources of truth and races between them.

To avoid that, we should either choose one source of truth, or combine isIconified and isAddedToHirarchy states.

lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
})

lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not call ON_CREATE and ON_START in the same method init, and just call ON_START straight away in the end of init, if JS doesn't have meaning for CREATED state.

@@ -170,6 +170,8 @@ if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
api project(":compose:ui:ui-unit")
api project(":compose:ui:ui-util")
implementation(project(":annotation:annotation"))
implementation(project(":lifecycle:lifecycle-common"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we add a new dependency, this change requires 2 approves, please ask someone else to review this commit (about adding dependency).

It looks fine for me, because we either will release a stable version of lifecycle, or we just depend on stable API of it right now.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eymar, no need to review the whole PR, only this commit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having these dependencies added, we can't avoid publishing lifecycle libraries (there was an idea that maybe we don't need to publish lifecyclce right now - #1204 (comment)). User projects won't work with this ui library if we don't publish lifecycle.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there was an idea

If you talk about my comment:

If we don't need it urgently

I meant that "if don't need it right now". But we definitely need it for the 1.6.10 release

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even dev builds won't work untill lifecycle is published if this PR is merged before that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Then let's wait until we publish

@igordmn
Copy link
Collaborator

igordmn commented Mar 20, 2024

We agreed to push it as is, and fix comments separately

@igordmn
Copy link
Collaborator

igordmn commented Mar 20, 2024

We agreed to push it as is, and fix comments separately

I will create separate tasks for each platform later.

@MatkovIvan MatkovIvan force-pushed the ivan.matkov/compose-lifecycle-owner branch from 8aa573d to 926ec3f Compare March 21, 2024 07:59
MatkovIvan and others added 6 commits March 22, 2024 09:49
After lifecycle modules commonization, `LifecycleOwner` is available in the common source set. Move it via `expect`/`actual` to keep binary compatibility on Android, which is based on filename - `AndroidCompositionLocals_androidKt`.

Test: N/A
Change-Id: I4b0ed6087e494a0b30cb6a890496fa17ebfda19c
@MatkovIvan MatkovIvan force-pushed the ivan.matkov/compose-lifecycle-owner branch from 926ec3f to 4c4b1ac Compare March 22, 2024 08:49
@MatkovIvan MatkovIvan merged commit 4c4b1ac into jb-main Mar 22, 2024
6 checks passed
@MatkovIvan MatkovIvan deleted the ivan.matkov/compose-lifecycle-owner branch March 22, 2024 09:33
kropp added a commit that referenced this pull request Apr 11, 2024
It cannot be handled anyway because ON_STARTED/ON_RESUME will be called
immediately after and web target is single-threaded

Fixes comment from #1198

## Testing

Test: added ComposeWindowLifecycleTest
MatkovIvan pushed a commit that referenced this pull request Apr 18, 2024
## Proposed Changes

Fixed iOS-related comments from #1198

## Testing

Test: added ViewControllerBasedLifecycleOwnerTest
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

LocalLifecycleOwner not declared on Desktop
6 participants