I’m building a React Native for Vega app (targeting the Fire TV Stick 4K Select) and traced some app-state behaviour on the Vega Virtual Device to what looks like a root-tag binding problem in useKeplerAppStateManager (VegaAppState in the 0.83 docs). Measurements and a repro are below. I’d like to know whether this is intended and what the supported pattern is.
Setup. One interactive component, launch-type = "singleton". A debug row re-renders every 10 s and shows: getCurrentState() from the hook’s manager; live/replayed counters from addEventListenerWithReplay on change/focus/blur/reconfigure; the tree’s RootTagContext; and, calling the KeplerAppState TurboModule directly, getCurrentState(tag) plus a change listener for tags 1, 11, 21 and 31.
Home is sent with inputd-cli button_press KEY_HOMEPAGE and the return with vmsgr send pkg://<component> from the device shell (returning through the launcher UI gives identical results). vlcm list shows the same pid and instance id across every cycle, so these are genuine warm returns.
Cold start:
mnt 1 · kas active · chg 1/0 · fcs 0/0 · blr 0/0 · rec 0/0
tag 1 · kas@ 1:active 11:unknown 21:unknown 31:unknown · xchg 11:- 21:- 31:-
After Home + return (cycle 1):
mnt 1 · kas background · chg 2/0 · fcs 1/0 · blr 1/0 · rec 0/0
tag 1 · kas@ 1:background 11:active 21:unknown 31:unknown · xchg 11:active 21:- 31:-
After a second Home + return on the same process (cycle 2):
mnt 1 · kas background · chg 2/0 · fcs 2/0 · blr 2/0 · rec 0/0
tag 1 · kas@ 1:background 11:background 21:active 31:unknown · xchg 11:background 21:active 31:-
Device log for that process:
stop tracking surface 1 (Home)
begin tracking surface 11 (return)
stop tracking surface 11 (Home)
begin tracking surface 21 (return)
What this shows
- Backgrounding destroys the surface and foregrounding creates a new one on the next root tag, as the AppState docs describe. But the existing React tree is re-attached rather than remounted (component state and a mount counter survive), and its
RootTagContextstill reads 1. useKeplerAppStateManagermemoizes a manager on that context value, so after the first resume it is bound to a surface that no longer exists.getCurrentState()reportsbackgroundindefinitely, andchangeis delivered only to the live tag: after cycle 1 the original tag never receives anotherchangein either direction, not evenbackgroundon the next Home press.focusandblurdo still reach the original tag on every cycle, which is whyfocuslooked like the resume signal in my earlier tests.reconfigurewithhomePressednever fired on any Home press, on any tag I listened to.- Back at the app root terminates the process, as the BackHandler docs say for a singleton, rather than backgrounding it.
Questions
- Is recreating the surface on a new root tag while preserving the React tree the intended behaviour? If so, should
RootTagContextupdate, and shoulduseKeplerAppStateManagerre-bind? - What is the supported way to read the component’s app state after a resume? Is there an API for the current live root tag, or is
moduleChange/currentModuleStateson the deprecatedAppStatethe intended process-level signal? - Should
reconfigure: homePressedfire on the VVD? An injectedKEY_HOMEPAGEbackgrounds the app exactly as the lifecycle guide describes, but no reconfigure arrives. - Does the physical Fire TV Stick 4K Select behave the same way?
- For “pause polling in background, refresh once on resume”, what should an app depend on, given that
changestops arriving on the hook’s manager after the first cycle?
Current workaround: treat focus as resume and blur as leaving, and don’t trust getCurrentState() or change from the hook after the first background. I’d rather build on the intended contract.
App Name: In Development
App Link on Amazon Appstore N/A
SDK Version: 0.24.9914
React Native Version: 0.83