From 8695980e1efdb28e772deccdbd164d3a0fa06a3b Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Wed, 20 Jan 2021 10:05:06 -0800 Subject: [PATCH] Add experiment to clean up resources when the app is backgrounded Summary: Changelog: [internal] Setup an experiment to measure impact of releasing resources when the app enters background. Reviewed By: JoshuaGross, shergin Differential Revision: D25952690 fbshipit-source-id: acfaa4a1689cc03c6020fdee62d3594859c9da3d --- React/Base/RCTConstants.h | 6 +++++ React/Base/RCTConstants.m | 15 +++++++++++ React/CxxBridge/RCTCxxBridge.mm | 25 +++++++++++++++++++ .../Mounting/RCTComponentViewRegistry.mm | 11 ++++++++ React/Fabric/RCTSurfacePresenter.mm | 4 +++ 5 files changed, 61 insertions(+) diff --git a/React/Base/RCTConstants.h b/React/Base/RCTConstants.h index c34cbc52df4..9f2297a1ad4 100644 --- a/React/Base/RCTConstants.h +++ b/React/Base/RCTConstants.h @@ -28,3 +28,9 @@ RCT_EXTERN void RCTExperimentSetOptimizedHitTesting(BOOL value); */ RCT_EXTERN BOOL RCTExperimentGetPreemptiveViewAllocationDisabled(void); RCT_EXTERN void RCTExperimentSetPreemptiveViewAllocationDisabled(BOOL value); + +/* + * Release resources when app enters background + */ +RCT_EXTERN BOOL RCTExperimentGetReleaseResourcesWhenBackgrounded(void); +RCT_EXTERN void RCTExperimentSetReleaseResourcesWhenBackgrounded(BOOL value); diff --git a/React/Base/RCTConstants.m b/React/Base/RCTConstants.m index e701ea81e7b..13128f54940 100644 --- a/React/Base/RCTConstants.m +++ b/React/Base/RCTConstants.m @@ -54,3 +54,18 @@ void RCTExperimentSetPreemptiveViewAllocationDisabled(BOOL value) { RCTExperimentPreemptiveViewAllocationDisabled = value; } + +/* + * Release resources when app enters background + */ +static BOOL RCTExperimentReleaseResourcesWhenBackgrounded = NO; + +BOOL RCTExperimentGetReleaseResourcesWhenBackgrounded() +{ + return RCTExperimentReleaseResourcesWhenBackgrounded; +} + +void RCTExperimentSetReleaseResourcesWhenBackgrounded(BOOL value) +{ + RCTExperimentReleaseResourcesWhenBackgrounded = value; +} diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index 7984501c806..05cd58cff70 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -13,6 +13,7 @@ #import #import #import +#import #import #import #import @@ -292,6 +293,10 @@ struct RCTInstanceCallback : public InstanceCallback { selector:@selector(handleMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleApplicationDidEnterBackgroundNotification) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; } return self; } @@ -350,6 +355,26 @@ struct RCTInstanceCallback : public InstanceCallback { } } +- (void)handleApplicationDidEnterBackgroundNotification +{ + if (!RCTExperimentGetReleaseResourcesWhenBackgrounded()) { + return; + } + + // We only want to run garbage collector when the loading is finished + // and the instance is valid. + if (!_valid || _loading) { + return; + } + + // We need to hold a local retaining pointer to react instance + // in case if some other tread resets it. + auto reactInstance = _reactInstance; + if (reactInstance) { + reactInstance->handleMemoryPressure(40 /* TRIM_MEMORY_BACKGROUND */); + } +} + /** * Ensure block is run on the JS thread. If we're already on the JS thread, the block will execute synchronously. * If we're not on the JS thread, the block is dispatched to that thread. Any errors encountered while executing diff --git a/React/Fabric/Mounting/RCTComponentViewRegistry.mm b/React/Fabric/Mounting/RCTComponentViewRegistry.mm index a8d21829d23..5deb2375bb9 100644 --- a/React/Fabric/Mounting/RCTComponentViewRegistry.mm +++ b/React/Fabric/Mounting/RCTComponentViewRegistry.mm @@ -35,6 +35,10 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024; selector:@selector(handleApplicationDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleApplicationDidEnterBackgroundNotification) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // Calling this a bit later, when the main thread is probably idle while JavaScript thread is busy. @@ -162,4 +166,11 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024; _recyclePool.clear(); } +- (void)handleApplicationDidEnterBackgroundNotification +{ + if (RCTExperimentGetReleaseResourcesWhenBackgrounded()) { + _recyclePool.clear(); + } +} + @end diff --git a/React/Fabric/RCTSurfacePresenter.mm b/React/Fabric/RCTSurfacePresenter.mm index a1febeac3c7..191622f7210 100644 --- a/React/Fabric/RCTSurfacePresenter.mm +++ b/React/Fabric/RCTSurfacePresenter.mm @@ -333,6 +333,10 @@ static BackgroundExecutor RCTGetBackgroundExecutor() RCTExperimentSetPreemptiveViewAllocationDisabled(YES); } + if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:release_resources_when_backgrounded_ios")) { + RCTExperimentSetReleaseResourcesWhenBackgrounded(YES); + } + auto componentRegistryFactory = [factory = wrapManagedObject(_mountingManager.componentViewRegistry.componentViewFactory)]( EventDispatcher::Weak const &eventDispatcher, ContextContainer::Shared const &contextContainer) {