Skip to content

Commit e4fdb53

Browse files
committed
Queue activity results before React context is ready
1 parent eedfdef commit e4fdb53

2 files changed

Lines changed: 63 additions & 1 deletion

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public class ReactHostImpl(
150150
private val reactInstanceEventListeners: MutableList<ReactInstanceEventListener> =
151151
CopyOnWriteArrayList()
152152
private val beforeDestroyListeners: MutableList<() -> Unit> = CopyOnWriteArrayList()
153+
private val pendingActivityResults: MutableList<PendingActivityResult> = CopyOnWriteArrayList()
153154

154155
internal var reactHostInspectorTarget: ReactHostInspectorTarget? = null
155156
private var frameTimingsObserver: FrameTimingsObserver? = null
@@ -667,7 +668,9 @@ public class ReactHostImpl(
667668
if (currentContext != null) {
668669
currentContext.onActivityResult(activity, requestCode, resultCode, data)
669670
} else {
670-
raiseSoftException(method, "Tried to access onActivityResult while context is not ready")
671+
stateTracker.enterState(method, "Queuing activity result until context is ready")
672+
pendingActivityResults.add(
673+
PendingActivityResult(WeakReference(activity), requestCode, resultCode, data))
671674
}
672675
}
673676

@@ -893,10 +896,33 @@ public class ReactHostImpl(
893896
@ThreadConfined(ThreadConfined.UI)
894897
private fun moveToHostDestroy(currentContext: ReactContext?) {
895898
reactLifecycleStateManager.moveToOnHostDestroy(currentContext)
899+
pendingActivityResults.clear()
896900
currentActivity = null
897901
frameTimingsObserver?.setCurrentWindow(null)
898902
}
899903

904+
private fun replayPendingActivityResults(reactContext: ReactContext) {
905+
val activityResults = pendingActivityResults.toList()
906+
pendingActivityResults.clear()
907+
908+
for (activityResult in activityResults) {
909+
val activity = activityResult.activity.get()
910+
if (activity != null) {
911+
reactContext.onActivityResult(
912+
activity,
913+
activityResult.requestCode,
914+
activityResult.resultCode,
915+
activityResult.data,
916+
)
917+
} else {
918+
raiseSoftException(
919+
"replayPendingActivityResults()",
920+
"Dropping queued activity result because activity was garbage collected",
921+
)
922+
}
923+
}
924+
}
925+
900926
private fun raiseSoftException(
901927
callingMethod: String,
902928
message: String,
@@ -1010,6 +1036,13 @@ public class ReactHostImpl(
10101036
val isReloading: Boolean,
10111037
)
10121038

1039+
private class PendingActivityResult(
1040+
val activity: WeakReference<Activity>,
1041+
val requestCode: Int,
1042+
val resultCode: Int,
1043+
val data: Intent?,
1044+
)
1045+
10131046
@ThreadConfined("ReactHost")
10141047
private fun getOrCreateReactInstanceTask(): Task<ReactInstance> {
10151048
val method = "getOrCreateReactInstanceTask()"
@@ -1121,6 +1154,8 @@ public class ReactHostImpl(
11211154
reactLifecycleStateManager.resumeReactContextIfHostResumed(reactContext, currentActivity)
11221155
}
11231156

1157+
replayPendingActivityResults(reactContext)
1158+
11241159
stateTracker.enterState(method, "Executing ReactInstanceEventListeners")
11251160
for (listener in reactInstanceEventListeners) {
11261161
listener.onReactContextInitialized(reactContext)

packages/react-native/ReactAndroid/src/test/java/com/facebook/react/runtime/ReactHostTest.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package com.facebook.react.runtime
99

1010
import android.app.Activity
11+
import android.content.Intent
1112
import com.facebook.react.MemoryPressureRouter
1213
import com.facebook.react.bridge.UIManager
1314
import com.facebook.react.common.LifecycleState
@@ -31,6 +32,7 @@ import org.mockito.kotlin.any
3132
import org.mockito.kotlin.doNothing
3233
import org.mockito.kotlin.doReturn
3334
import org.mockito.kotlin.mock
35+
import org.mockito.kotlin.never
3436
import org.mockito.kotlin.spy
3537
import org.mockito.kotlin.verify
3638
import org.mockito.kotlin.whenever
@@ -168,4 +170,29 @@ class ReactHostTest {
168170
reactHost.onHostDestroy(activityController.get())
169171
assertThat(reactHost.lifecycleState).isEqualTo(LifecycleState.BEFORE_CREATE)
170172
}
173+
174+
@Test
175+
fun onActivityResult_beforeContextIsReady_replaysAfterStart() {
176+
val activity = activityController.get()
177+
val data = Intent("test.intent")
178+
179+
reactHost.onActivityResult(activity, 100, Activity.RESULT_OK, data)
180+
reactHost.start()
181+
182+
val reactContext = mockedBridgelessReactContextCtor.constructed().first()
183+
verify(reactContext).onActivityResult(activity, 100, Activity.RESULT_OK, data)
184+
}
185+
186+
@Test
187+
fun onActivityResult_beforeContextIsReady_dropsOnHostDestroy() {
188+
val activity = activityController.get()
189+
val data = Intent("test.intent")
190+
191+
reactHost.onActivityResult(activity, 100, Activity.RESULT_OK, data)
192+
reactHost.onHostDestroy()
193+
reactHost.start()
194+
195+
val reactContext = mockedBridgelessReactContextCtor.constructed().first()
196+
verify(reactContext, never()).onActivityResult(activity, 100, Activity.RESULT_OK, data)
197+
}
171198
}

0 commit comments

Comments
 (0)