Repository: expo/expo
File: packages/expo-updates/android/src/main/java/expo/modules/updates/errorrecovery/ErrorRecovery.kt (Kotlin)

Code before:

    unregisterContentAppearedListener()

    // wait 10s before unsetting error handlers; even though we won't try to relaunch if our
    // handlers are triggered after now, we still want to give the app a reasonable window of time
    // to start the WAIT_FOR_REMOTE_UPDATE task and check for a new update is there is one
    //
    // it's safe to use the handler thread for this since nothing else
    // touches this class's fields
    handler.postDelayed({ unregisterErrorHandler() }, 10000)
  }

  private val contentAppearedListener = MarkerListener { name, _, _ ->
    if (name == ReactMarkerConstants.CONTENT_APPEARED) {
      handleContentAppeared()
    }
  }

  private fun registerContentAppearedListener() {
    ReactMarker.addListener(contentAppearedListener)
  }

  private fun unregisterContentAppearedListener() {
    ReactMarker.removeListener(contentAppearedListener)
  }

  private fun registerErrorHandler(devSupportManager: DevSupportManager) {
    if (enableBridgelessArchitecture) {
      registerErrorHandlerImplBridgeless()
    } else {
      registerErrorHandlerImplBridge(devSupportManager)
    }
  }

  private fun registerErrorHandlerImplBridgeless() {
    shouldHandleReactInstanceException = true
  }

  private fun registerErrorHandlerImplBridge(devSupportManager: DevSupportManager) {
    if (devSupportManager !is ReleaseDevSupportManager) {
      logger.debug("Unexpected type of ReactInstanceManager.DevSupportManager. expo-updates error recovery will not behave properly.")
      return
    }

    val defaultJSExceptionHandler = JSExceptionHandler { e -> this@ErrorRecovery.handleException(e) }

    val devSupportManagerClass = devSupportManager.javaClass
    previousExceptionHandler = devSupportManagerClass.getDeclaredField("defaultJSExceptionHandler").let { field ->
      field.isAccessible = true
      val previousValue = field[devSupportManager]
      field[devSupportManager] = defaultJSExceptionHandler

Review comment (suggestion):
[💡 suggestion] After this change, the `devSupportManager` parameter of `registerErrorHandler` is unused (only `registerErrorHandlerImplBridgeless()` is called, which doesn't need it). This flows up to `startMonitoring(devSupportManager)` which also no longer needs the parameter.

Additionally, since `registerErrorHandlerImplBridgeless()` is just `shouldHandleReactInstanceException = true` and `unregisterErrorHandlerImplBridgeless()` is just `shouldHandleReactInstanceException = false`, the indirection through these methods is no longer necessary — the logic could be inlined directly.

Code after:
    unregisterContentAppearedListener()

    // wait 10s before unsetting error handlers; even though we won't try to relaunch if our
    // handlers are triggered after now, we still want to give the app a reasonable window of time
    // to start the WAIT_FOR_REMOTE_UPDATE task and check for a new update is there is one
    //
    // it's safe to use the handler thread for this since nothing else
    // touches this class's fields
    handler.postDelayed({ unregisterErrorHandler() }, 10000)
  }

  private val contentAppearedListener = MarkerListener { name, _, _ ->
    if (name == ReactMarkerConstants.CONTENT_APPEARED) {
      handleContentAppeared()
    }
  }

  private fun registerContentAppearedListener() {
    ReactMarker.addListener(contentAppearedListener)
  }

  private fun unregisterContentAppearedListener() {
    ReactMarker.removeListener(contentAppearedListener)
  }

  private fun registerErrorHandler(devSupportManager: DevSupportManager) {
    registerErrorHandlerImplBridgeless()
  }

  private fun registerErrorHandlerImplBridgeless() {
    shouldHandleReactInstanceException = true
  }

  private fun registerErrorHandlerImplBridge(devSupportManager: DevSupportManager) {
    if (devSupportManager !is ReleaseDevSupportManager) {
      logger.debug("Unexpected type of ReactInstanceManager.DevSupportManager. expo-updates error recovery will not behave properly.")
      return
    }

    val defaultJSExceptionHandler = JSExceptionHandler { e -> this@ErrorRecovery.handleException(e) }

    val devSupportManagerClass = devSupportManager.javaClass
    previousExceptionHandler = devSupportManagerClass.getDeclaredField("defaultJSExceptionHandler").let { field ->
      field.isAccessible = true
      val previousValue = field[devSupportManager]
      field[devSupportManager] = defaultJSExceptionHandler
      return@let previousValue as DefaultJSExceptionHandler
    }
    weakDevSupportManager = WeakReference(devSupportManager)
  }