Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ public class ReactHostImpl(
private val reactInstanceEventListeners: MutableList<ReactInstanceEventListener> =
CopyOnWriteArrayList()
private val beforeDestroyListeners: MutableList<() -> Unit> = CopyOnWriteArrayList()
private val pendingActivityResults: MutableList<PendingActivityResult> = CopyOnWriteArrayList()

internal var reactHostInspectorTarget: ReactHostInspectorTarget? = null
private var frameTimingsObserver: FrameTimingsObserver? = null
Expand Down Expand Up @@ -667,7 +668,9 @@ public class ReactHostImpl(
if (currentContext != null) {
currentContext.onActivityResult(activity, requestCode, resultCode, data)
} else {
raiseSoftException(method, "Tried to access onActivityResult while context is not ready")
stateTracker.enterState(method, "Queuing activity result until context is ready")
pendingActivityResults.add(
PendingActivityResult(WeakReference(activity), requestCode, resultCode, data))
}
}

Expand Down Expand Up @@ -893,10 +896,33 @@ public class ReactHostImpl(
@ThreadConfined(ThreadConfined.UI)
private fun moveToHostDestroy(currentContext: ReactContext?) {
reactLifecycleStateManager.moveToOnHostDestroy(currentContext)
pendingActivityResults.clear()
currentActivity = null
frameTimingsObserver?.setCurrentWindow(null)
}

private fun replayPendingActivityResults(reactContext: ReactContext) {
val activityResults = pendingActivityResults.toList()
pendingActivityResults.clear()

for (activityResult in activityResults) {
val activity = activityResult.activity.get()
if (activity != null) {
reactContext.onActivityResult(
activity,
activityResult.requestCode,
activityResult.resultCode,
activityResult.data,
)
} else {
raiseSoftException(
"replayPendingActivityResults()",
"Dropping queued activity result because activity was garbage collected",
)
}
}
}

private fun raiseSoftException(
callingMethod: String,
message: String,
Expand Down Expand Up @@ -1010,6 +1036,13 @@ public class ReactHostImpl(
val isReloading: Boolean,
)

private class PendingActivityResult(
val activity: WeakReference<Activity>,
val requestCode: Int,
val resultCode: Int,
val data: Intent?,
)

@ThreadConfined("ReactHost")
private fun getOrCreateReactInstanceTask(): Task<ReactInstance> {
val method = "getOrCreateReactInstanceTask()"
Expand Down Expand Up @@ -1121,6 +1154,8 @@ public class ReactHostImpl(
reactLifecycleStateManager.resumeReactContextIfHostResumed(reactContext, currentActivity)
}

replayPendingActivityResults(reactContext)

stateTracker.enterState(method, "Executing ReactInstanceEventListeners")
for (listener in reactInstanceEventListeners) {
listener.onReactContextInitialized(reactContext)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.facebook.react.runtime

import android.app.Activity
import android.content.Intent
import com.facebook.react.MemoryPressureRouter
import com.facebook.react.bridge.UIManager
import com.facebook.react.common.LifecycleState
Expand All @@ -31,6 +32,7 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
Expand Down Expand Up @@ -168,4 +170,29 @@ class ReactHostTest {
reactHost.onHostDestroy(activityController.get())
assertThat(reactHost.lifecycleState).isEqualTo(LifecycleState.BEFORE_CREATE)
}

@Test
fun onActivityResult_beforeContextIsReady_replaysAfterStart() {
val activity = activityController.get()
val data = Intent("test.intent")

reactHost.onActivityResult(activity, 100, Activity.RESULT_OK, data)
reactHost.start()

val reactContext = mockedBridgelessReactContextCtor.constructed().first()
verify(reactContext).onActivityResult(activity, 100, Activity.RESULT_OK, data)
}

@Test
fun onActivityResult_beforeContextIsReady_dropsOnHostDestroy() {
val activity = activityController.get()
val data = Intent("test.intent")

reactHost.onActivityResult(activity, 100, Activity.RESULT_OK, data)
reactHost.onHostDestroy()
reactHost.start()

val reactContext = mockedBridgelessReactContextCtor.constructed().first()
verify(reactContext, never()).onActivityResult(activity, 100, Activity.RESULT_OK, data)
}
}
Loading