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

Major: Bump underlying Android and iOS native Rive runtimes #149

Merged
merged 22 commits into from
Mar 14, 2023
Merged

Conversation

zplata
Copy link
Contributor

@zplata zplata commented Nov 22, 2022

Goal: Get this runtime which relies on underlying iOS and Android runtimes up-to-date, which involves major bump changes on each platform, while keeping the JS API unchanged.

A lot of the files changed here are automatic changes (esp in Android Gradle files) from bumping the Gradle version. Most of the notable changes are in ios/ and android/ respective RiveReactNativeView and RiveReactNativeViewManager files with how we bridge to the native runtimes

Some notable breaking changes (will outline in help docs as a follow on with migration guide steps):

  • iOS (v2.0.12 -> v3.1.4)
    • Only plays one linear animation at a time (no longer mixing multiple individual animations if passed in), encouraging consumers to use state machines for this behavior
    • Plays a state machine by default if one exists on the Rive file unless animationName is specified
    • Uses the RiveViewModel underneath the hood (should be transparent to consumers though)
    • Minimum iOS target bump to 14.0 to match iOS RiveRuntime requirements
    • TODO: Have to for some reason call riveViewModel.stop() before replacing the RiveViewModel with an updated one (perhaps based on updated props from React side) or else consumer apps will crash on the render loop in the advance() method from the iOS side. No meaningful insight as to why yet. This means that the onStop() callback gets surfaced to users and subsequently 2 onPlay() callbacks. Need to figure out how to prevent the initial stop() and play() call.
  • Android (v3.0.9 -> v4.2.1)
    • Need to sync the RiveReactNativeView with the underlying RiveAnimationView from native Android runtime to remove view and reset onDetachFromWindow and coordinate how attributes are persisted
    • Bump in Gradle properties (i.e Kotlin version and Build Tools version)

Other notes:

  • Changing the publish workflow to be manual like the iOS runtime for now, and fixing some funky logic around auto-version bumping
  • Bumping the React Native dependency has some side effect changes we had to make here, but should be transparent to user (v0.63.4 -> v0.65.0)
  • We might want to consider removing the MultipleAnimations example since on iOS, we don't allow running multiple individual linear animations by passing in an array (have to go through state machine).

@duncandoit
Copy link
Contributor

duncandoit commented Nov 22, 2022

We might want to consider removing the MultipleAnimations example since on iOS, we don't allow running multiple individual linear animations by passing in an array (have to go through state machine).

This goes back to the discussion about the runtime API unification(ish). I'd say that, at least within the context of react native, both iOS and Android should behave similarly

Copy link
Contributor

@umberto-sonnino umberto-sonnino left a comment

Choose a reason for hiding this comment

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

I think this looks good!
Did it solve #125?

// RiveReactNativeView may attach multiple times for any reason, so using
// a flag here isAttached to determine when it was initially attached to
// only addView once
if (childCount == 0 && isAttached == false) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

guessing we're single threaded so this doesn't have race-conditions? thank you js! is this solving some specific issues we were seeing before?

Copy link
Contributor

Choose a reason for hiding this comment

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

What conditions cause it to be attached multiple times?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Specifically with our Example App, it uses this navigation lib @react-navigation/stack which seems to be responsible for the attach/detach multiple times behavior.

// On selecting an example in our example app
attach -> detach -> attach

// On hitting the back button to go back to the examples homepage
detach -> attach -> detach

But this could be the case for a different scenario created by a consumer too, so this is kind of a failsafe to try and coordinate the attach/detach logic of RiveReactNativeView here with RiveAnimationView of Android.

Tracking the riveAnimationView.isAttachedToWindow does not seem to be reliable here, so tracking the childCount to determine whether to addView seems to do the trick here, but open to other alternatives to try out!

With detaching in particular, this is the problematic bit because detaching on the Android native side cleans up cpp refs. When we detach, we need to remove the view. When it calls (for whatever reason) the attach method again, we want to make sure we're not adding the view again (thus why we don't set isAttached back to false).

All this to say - we need to explicitly removeView on detach now with the cpp cleanup logic being handled in the Android native's onDetatchedFromWindow as there is no guarantee the detachFromWindow on this RiveReactNativeView will be in sync with the native one

Copy link
Contributor

@simontreny simontreny Mar 16, 2023

Choose a reason for hiding this comment

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

@zplata This change breaks the rendering of Rive animations in our app. I guess it's because we are not using the same React Native version (0.68.5 on our side), but in our app, onAttachedToWindow() is called only once when we push a screen with a Rive animation and on onDetachedFromWindow() is called once when the screen is popped (as you would expect).

With this change, the update() function is no longer called because:

  • init calls addView(riveAnimationView)
  • onAttachedToWindow() checks if childCount == 0. It's not the case as init has added the view already. So, update() is not called and the animation is never loaded

To me, the previous version relying on onAfterUpdateTransaction() to call the update() method was the proper way to do it as you might want to reload the animation if the url changes as well and not only when the animation view is attached to the window.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @simontreny - we noticed this too.. we're looking into patching this. There's also some potential underlying rive-android fixes coming along that might make the coordination between the React Native view and the Android RiveNativeView a little simpler.

// Starting in Rive Android 4.0+, we need to coordinate removing this view with
// the underlying RiveAnimationView when resources are cleaned up
removeView(riveAnimationView)
super.onDetachedFromWindow();
Copy link
Collaborator

Choose a reason for hiding this comment

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

should this set isAttached=false?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Setting to false would cause some kind of cyclical attach/detach (since after detaching, it would go to attach and then add the view again) and specifically when officially detaching this view, this would cause the cpp memory reference error when everything has been cleaned up on Android side of things.

I think perhaps a better name for this would be hasMounted to be more distinctive from the detach/attach handlers here?

@duncandoit
Copy link
Contributor

Might as well bump this to iOS runtime's 3.1.3

@arasrezaei
Copy link

bump

@umberto-sonnino
Copy link
Contributor

@zplata @HayesGordon is this ready to merged?

@HayesGordon
Copy link
Contributor

@umberto-sonnino I'm adding in another fix soon. If you're familiar with this runtime another pair of eyes on the final PR will be appreciated.

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

Successfully merging this pull request may close these issues.

7 participants