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

fix(Android): drop zero frames in release mode #607

Merged
merged 9 commits into from
Jan 3, 2025

Conversation

boringcactus
Copy link
Member

Summary

Ticket: 🤖 | Nearby | Sheet / navigation, tangentially

#605 mentions that the animation doesn't play because frames are lost. This fixes that issue by running some of our most CPU-intensive computations on a background thread.

iOS

  • If you added any user-facing strings on iOS, are they included in Localizable.xcstrings?
    • Add temporary machine translations, marked "Needs Review"
      android
  • All user-facing strings added to strings resource

Testing

Manually verified that Logcat shows zero Choreographer warnings about skipped frames in release mode. (Some are still shown in debug mode on launch, but from the profiler it looks like the code running in those slow frames is mostly not ours.)

@boringcactus boringcactus requested a review from a team as a code owner December 23, 2024 17:56
Copy link
Collaborator

@KaylaBrady KaylaBrady left a comment

Choose a reason for hiding this comment

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

Nice! Appreciate you breaking this out separately. A couple of thoughts for discussion on thinking about how to adopt the right approach consistently going forward.

@@ -242,11 +244,11 @@ fun HomeMapView(
LaunchedEffect(railRouteShapes, globalResponse, globalMapData) {
viewModel.refreshRouteLineData(now)
}
LaunchedEffect(railRouteLineData) {
LaunchedOnDispatcherEffect(Dispatchers.Default, railRouteLineData) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I like the way you broke out reusable helpers, but I'm concerned about how we would consistently use these new helpers when appropriate.

Wrapping my head more on coroutines, I found a couple of snippets from this blog post helpful:

a good practice is to use withContext to make sure every function is safe to be called on any Dispatcher including Main — that way the caller never has to think about what thread will be needed to execute the function.

It’s a really good idea to make every suspend function main-safe. If it does anything that touches the disk, network, or even just uses too much CPU, use withContext to make it safe to call from the main thread. This is the pattern that coroutines based libraries like Retrofit and Room follow. If you follow this style throughout your codebase your code will be much simpler and avoid mixing threading concerns with application logic. When followed consistently, coroutines are free to launch on the main thread and make network or database requests with simple code while guaranteeing users won’t see “jank.”

What do you think of pushing the Dispatcher context to a lower level, such as within refreshRouteLineSource and refreshStopFeatures so that the view doesn't need to worry about thread management?

Aside from making any changes to the implementation here, I think it would be worth adding a note to the PR template to check that expensive computations are run on the Default thread as appropriate.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's definitely a more robust approach, good catch.

@@ -56,7 +58,8 @@ fun NearbyTransitView(
val (pinnedRoutes, togglePinnedRoute) = managePinnedRoutes()

val nearbyWithRealtimeInfo =
remember(
rememberOnDispatcher(
Copy link
Collaborator

Choose a reason for hiding this comment

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

question: Similar to above, I'm concerned about how we'll approach using this consistently, especially where we'll want rememberSaveable as well. Could the use of Dispatchers.Default be pushed to a lower level?

Copy link
Member Author

Choose a reason for hiding this comment

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

Making withRealtimeInfo suspend in shared is a mess I'd rather not touch right now - it implies that StopDetailsDepartures.fromData needs to also be suspend, but then code generated by SKIE fails to compile due to ambiguity between shared as in shared.framework and shared as in StopDetailsDepartures.Companion.shared, and fixing that would require replacing every import shared in iosApp/. That might fix at least some of our iOS UI thread jank, though, so we should probably do it at some point.

Android has a set of thread annotations to keep track of functions which must be called from the UI thread or from a worker thread, but it looks like it doesn't actually check those in a way that would let us error on calling withRealtimeInfo from the UI thread.

Copy link
Member Author

Choose a reason for hiding this comment

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

SKIE bug reported. I'm not sure SKIE will be able to fix this, so we may wind up needing to rename the framework (not sure if Shared or MbtaGoShared makes more sense) and s/import shared/import Shared/ in iosApp/; if we'd be neutral-to-positive on doing that anyway, I wouldn't mind going ahead and picking that up, but either way this PR should be set to go now.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice catch on the SKIE bug! I don't think it is too pressing to pick up since it would improve iOS performance which isn't a major issue right now, but worth making a ticket & bringing up at the next refinement!

Copy link
Collaborator

@KaylaBrady KaylaBrady left a comment

Choose a reason for hiding this comment

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

Awesome!

@boringcactus boringcactus merged commit 1995375 into main Jan 3, 2025
5 checks passed
@boringcactus boringcactus deleted the mth-android-frames branch January 3, 2025 16:40
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.

2 participants