Repository navigation
Conversation
peachbits
force-pushed
the
matthew/android-drawer-perf
branch
from
October 29, 2026 20:19
021209a to
406b25e
Compare
Patch releases on the 0.10 line, which Reanimated 4.5 pairs with. On Android the UI runtime's frame loop kept running while the app was in the background (0.10.4), and worklet functions could be destroyed after their runtime was gone, crashing on teardown (0.10.1). The other fixes are in the Babel plugin, Android bundle mode, and iOS background worklet runtimes, which the app does not use.
Patch releases on the 4.5 line. 4.5.4 fixes a crash when an event arrives for a view that has already unmounted, an iOS launch crash when the UI queue flushes before Reanimated has registered with it, and an Android props merge that let the oldest of several updates to one view in a commit win, so scroll-driven animations could jump back a frame. It also stops taking over React Native's LayoutAnimation driver; the only LayoutAnimation caller in the app's dependencies is gifted-charts' BarChart, which the app does not use. 4.5.5 only fixes upstream CI. Neither release has the layout animation final-frame fix, which the next commit patches in.
Reanimated 4.5's legacy layout animation proxy, which the app uses, drops an animation's last pending frame once the animation settles. The final progress update and the end callback arrive in the same frame, and the flush that follows skips settled animations, so views stop one frame short of their target. On a busy device the first frame can already be the last one, and the view stays where it started. On Home, the rows below the promo carousel stayed where they were before the carousel appeared, so the carousel drew over the Buy and Sell tiles. Patch in the upstream fix (Reanimated PR 10171, released in 4.6.0): skip a settled animation's pending update only when its view is exiting. The experimental proxy, used only with the shared element transitions flag, is left alone. On an Android emulator with a large account and the carousel forced in 4 s late, the Buy tile stopped 1 px short of its target on 4 of 4 launches before and landed on it on 6 of 6 after. Across cold launches the carousel covered Buy and Sell on 3 of 10 before, and on none of 8 under in-emulator CPU load after.
The side menu hosts Services and re-renders whenever the drawer opens or closes, which re-ran every service, including the SortedWalletList sort, before the drawer could start moving. Memoize Services so a drawer toggle only re-renders the menu itself.
useRefresher restarts its timer, and calls its callback immediately, whenever the callback changes. Services passed an inline function, so every Services render re-ran updateExchangeInfo, updateGiftCardInfo and updateHoudiniChains, each dispatching a fresh object to every subscriber. Services re-renders on its own selectors too, not only when the drawer toggles, so the memo alone cannot cover this. On a large account on an Android emulator, the two slowest dispatches took 0.5-1.1 s each per drawer toggle, all before the drawer's effect could start the open animation.
The toggle waited on the core's settings write before updating the UI, which can take seconds while a large account syncs. Update redux right away and roll back if the write fails.
The drawer overlay animated zIndex from -1 to 0 as the drawer started to open, sending it from behind the screen to in front of it. Under the new architecture that reorders the native views, which drew a fully blank frame of the app on most opens. The overlay's animated pointerEvents already keep the closed overlay from taking touches, so leave it in front and let opacity alone fade it. On an Android emulator with a large account, opens went from a blank frame plus a 4-11 frame fade to a 10-12 frame fade with no blank frame. Upstream removed the same zIndex for a white flicker under the new architecture (react-native-drawer-layout 4.0.4, commit f662e274). That fix only reached the v7 drawer; 6.7.2 is the last 6.x release.
1 of 6 tasks
peachbits
force-pushed
the
matthew/android-drawer-perf
branch
from
October 29, 2026 22:02
406b25e to
e116728
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
@react-navigation/drawer 6.7.2 wraps the app in a View whose
accessibilityElementsHidden and importantForAccessibility props flip
with the drawer's open state. Under the new architecture a View that
gains those props stops being flattened, and one that loses them is
flattened away, so every open and close moved the whole app to a new
native parent. A Pixel 9 Pro drew a frame of that move with the app
missing: the screen outside the menu went flat dark as the menu opened
or closed.
Mark the wrapper collapsable={false} in the existing drawer patch so it
is always a real native view. The v7 drawer avoids this by using an
Animated.View, which Reanimated never flattens.
On the Pixel, with the menu opened and closed 12 times on the Assets
tab, the empty frame showed up on 12 of 12 cycles before and on none
after.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Asana: Android sidebar blur doesn't fade in and out
On a large account on Android, the side menu was slow to start opening, its dimming overlay didn't fade, and hiding or showing balances was slow to flip. I profiled release builds on an API 35 emulator with a large account, using temporary trace logging and frame-by-frame screen recordings (not part of this PR), and reworked this PR around what they showed:
Servicesrender. This is the main cause of the slow open.ServicespassesuseRefresheran inline callback, and the hook restarts and fires immediately whenever its callback changes. The side menu hostsServices, so every open and close re-ranupdateExchangeInfo,updateGiftCardInfoandupdateHoudiniChains, each dispatching a fresh object to every subscriber. React ran them before the drawer's own effect could start the animation.useHandlerkeeps the callback stable.Services. This also skips re-rendering every service, including the wallet sort, on each toggle.zIndexfrom -1 to 0 as it opened. Under the new architecture that reorders the native views and draws a fully blank frame of the app. The existing@react-navigation/drawerpatch now leaves the overlay in front, and its animatedpointerEventskeep it from blocking touches while closed.Dropped from the first version:
EdgeAppStackmemo: the stack never re-rendered on a drawer toggle.ANDROID_SYNCHRONOUSLY_UPDATE_UI_PROPS: it applies the overlay's opacity directly, but thezIndexchange still waits for a commit, so the overlay faded behind the screen and popped in after the drawer had opened, on 5 of 8 opens even on a quiet device. Without thezIndexanimation, the fade is the same with or without the flag.Results (API 35 emulator, large account, release builds):
CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
none
Requirements
If you have made any visual changes to the GUI. Make sure you have: