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 @@ -660,11 +660,10 @@ public class ReactHostImpl(
val method =
"onActivityResult(activity = \"$activity\", requestCode = \"$requestCode\", resultCode = \"$resultCode\", data = \"$data\")"

val currentContext = currentReactContext
if (currentContext != null) {
currentContext.onActivityResult(activity, requestCode, resultCode, data)
} else {
raiseSoftException(method, "Tried to access onActivityResult while context is not ready")
callWithExistingReactContext(method) { reactContext ->
if (!activity.isDestroyed) {
reactContext.onActivityResult(activity, requestCode, resultCode, data)
}
}
}

Expand Down Expand Up @@ -930,6 +929,32 @@ public class ReactHostImpl(
executor,
)

/** Schedule UI work after an already requested ReactInstance has finished initializing. */
private fun callWithExistingReactContext(
callingMethod: String,
runnable: (reactContext: ReactContext) -> Unit,
): Task<Boolean> =
// Read the task on the host executor, after any previously scheduled start work.
Task.call({ createReactInstanceTaskRef.get() }, bgExecutor)
.onSuccess(
{ instanceTask ->
val reactContext = currentReactContext
if (instanceTask.getResult() == null || reactContext == null) {
raiseSoftException(
"callWithExistingReactContext($callingMethod)",
"Execute: ReactContext is null. Dropping work.",
)
false
} else if (instanceTask.getResult() !== reactInstance) {
false
} else {
runnable(reactContext)
true
}
},
uiExecutor,
)

/** Create a ReactInstance if it doesn't exist already, and schedule work on it. */
private fun callAfterGetOrCreateReactInstance(
callingMethod: String,
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 @@ -143,6 +145,89 @@ class ReactHostTest {
assertThat(reactHost.currentReactContext).isNull()
}

@Test
fun testActivityResultWithInitializedContext() {
reactHost.start()
val intent = Intent("test-result")

reactHost.onActivityResult(activityController.get(), 7, Activity.RESULT_OK, intent)

verify(checkNotNull(reactHost.currentReactContext))
.onActivityResult(activityController.get(), 7, Activity.RESULT_OK, intent)
}

@Test
fun testActivityResultsWaitForScheduledInitialization() {
val bgTasks = ArrayDeque<Runnable>()
reactHost =
spy(
ReactHostImpl(
activityController.get().application,
reactHostDelegate,
mock(),
{ bgTasks.add(it) },
Task.IMMEDIATE_EXECUTOR,
false /* allowPackagerServerAccess */,
false /* useDevSupport */,
),
)
doReturn(null).whenever(reactHost).getOrCreateReactHostInspectorTarget()
doNothing().whenever(reactHost).unregisterInstanceFromInspector(any())

reactHost.start()
val intent = Intent("test-result")
reactHost.onActivityResult(activityController.get(), 7, Activity.RESULT_OK, intent)
reactHost.onActivityResult(activityController.get(), 8, Activity.RESULT_CANCELED, null)
assertThat(reactHost.currentReactContext).isNull()

while (bgTasks.isNotEmpty()) bgTasks.removeFirst().run()

val reactContext = checkNotNull(reactHost.currentReactContext)
val ordered = org.mockito.Mockito.inOrder(reactContext)
ordered
.verify(reactContext)
.onActivityResult(activityController.get(), 7, Activity.RESULT_OK, intent)
ordered
.verify(reactContext)
.onActivityResult(activityController.get(), 8, Activity.RESULT_CANCELED, null)
}

@Test
fun testActivityResultDoesNotStartAnUnusedHost() {
reactHost.onActivityResult(activityController.get(), 7, Activity.RESULT_OK, null)

assertThat(reactHost.currentReactContext).isNull()
assertThat(mockedReactInstanceCtor.constructed()).isEmpty()
}

@Test
fun testActivityResultIsNotDeliveredToDestroyedActivity() {
val bgTasks = ArrayDeque<Runnable>()
reactHost =
spy(
ReactHostImpl(
activityController.get().application,
reactHostDelegate,
mock(),
{ bgTasks.add(it) },
Task.IMMEDIATE_EXECUTOR,
false /* allowPackagerServerAccess */,
false /* useDevSupport */,
),
)
doReturn(null).whenever(reactHost).getOrCreateReactHostInspectorTarget()
doNothing().whenever(reactHost).unregisterInstanceFromInspector(any())

val activity = activityController.get()
reactHost.start()
reactHost.onActivityResult(activity, 7, Activity.RESULT_OK, null)
activityController.pause().stop().destroy()
while (bgTasks.isNotEmpty()) bgTasks.removeFirst().run()

verify(checkNotNull(reactHost.currentReactContext), never())
.onActivityResult(activity, 7, Activity.RESULT_OK, null)
}

@Test
fun testReload() {
reactHost.start()
Expand Down
Loading