Skip to content

Android: PictureInPictureHelperFragment crashes app on activity restoration (NoSuchMethodException) #4917

Description

@ashot-israelyan

What happened?

Summary

PictureInPictureHelperFragment declares only a constructor that takes a VideoView. It has no public no-arg constructor. After process death or with developer setting "Don't keep activities" enabled, FragmentManager re-instantiates persisted fragments via reflection (clazz.getConstructor()), finds no no-arg constructor, and throws java.lang.NoSuchMethodException. The host activity fails to launch.

(verified the same code on master at android/src/main/java/com/twg/video/core/fragments/PictureInPictureHelperFragment.kt line 16)

Stack trace (production, deobfuscated)

java.lang.RuntimeException: Unable to start activity ComponentInfo{...MainActivity}:
  androidx.fragment.app.Fragment$k: Unable to instantiate fragment
  com.twg.video.core.fragments.PictureInPictureHelperFragment:
  could not find Fragment constructor

  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:4710)
  ...
Caused by: androidx.fragment.app.Fragment$k: Unable to instantiate fragment
  com.twg.video.core.fragments.PictureInPictureHelperFragment:
  could not find Fragment constructor

  at androidx.fragment.app.FragmentFactory.instantiate (FragmentFactory.java:131)
  at com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory.instantiate (RNScreensFragmentFactory.kt:11)
  at androidx.fragment.app.FragmentState.instantiate (FragmentState.java:87)
  at androidx.fragment.app.FragmentStateManager.<init> (FragmentStateManager.java:92)
  at androidx.fragment.app.FragmentManager.restoreSaveStateInternal (FragmentManager.java:2849)
  at androidx.fragment.app.FragmentManager.attachController (FragmentManager.java:3008)
  at androidx.fragment.app.FragmentController.attachHost (FragmentController.java:117)
  at androidx.fragment.app.FragmentActivity.lambda$init$3 (FragmentActivity.java:139)
  at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable (ContextAwareHelper.kt:84)
  at androidx.activity.ComponentActivity.onCreate (ComponentActivity.kt:331)
  at androidx.fragment.app.FragmentActivity.onCreate (FragmentActivity.java:216)
  at com.facebook.react.ReactActivity.onCreate (ReactActivity.java:60)
  at com.shanttv.golive.MainActivity.onCreate (MainActivity.kt:24)
  ...
Caused by: java.lang.NoSuchMethodException:
  com.twg.video.core.fragments.PictureInPictureHelperFragment.<init> []

  at java.lang.Class.getConstructor0 (Class.java:3387)
  at java.lang.Class.getConstructor (Class.java:2479)
  at androidx.fragment.app.FragmentFactory.instantiate (FragmentFactory.java:121)

(Reproduces with or without react-native-screensRNScreensFragmentFactory here just delegates to the default FragmentFactory.instantiate for non-RNScreens fragments.)

Impact

On our shipped 4.0.x line (~10k DAU app), this issue was the #1 fatal crash:

  • 742 events / 428 distinct users in 7 days
  • ~10.5% of users on the affected build
  • 99% of crashes happen in the first second of session (consistent with crash-at-launch on restoration)
  • Triggers any time the OS recreates the activity while a <Video /> was previously mounted: low-memory background kill, "Don't keep activities", process death.

Root cause

PictureInPictureHelperFragment.kt:16:

class PictureInPictureHelperFragment(private val videoView: VideoView) : Fragment() {

Per the Android Fragment docs, all Fragment subclasses must have a public empty constructor — the framework needs it for state restoration. The fragment is added via:

// VideoView.kt, setupPipHelper()
val fragment = PictureInPictureHelperFragment(this)
it.supportFragmentManager.beginTransaction()
    .add(fragment, fragment.id)
    .commitAllowingStateLoss()

Because the fragment is committed (rather than retained out-of-band), Android persists its state across activity recreation and tries to rebuild it reflectively. There is no constructor for the framework to call, so launch fails.

Suggested fix

Give the fragment a no-arg primary constructor and treat the restored case as an orphan to be removed (the VideoView reference is gone after restoration, and React Native will re-mount its own helper when JS reattaches the <Video />):

class PictureInPictureHelperFragment() : Fragment() {
  private var videoView: VideoView? = null
  val id: String = UUID.randomUUID().toString()

  constructor(videoView: VideoView) : this() {
    this.videoView = videoView
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    if (videoView == null) {
      // Restored after process death — the original VideoView is gone and React Native
      // will re-mount its own helper. Drop this orphan instead of holding a dead fragment.
      Handler(Looper.getMainLooper()).post {
        if (isAdded) {
          parentFragmentManager.beginTransaction().remove(this).commitAllowingStateLoss()
        }
      }
    }
  }

  override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode)
    val videoView = videoView ?: return
    // ... existing body unchanged ...
  }
}

We're shipping this as a patch-package patch locally and can open a PR if helpful.

Steps to reproduce

  1. Mount a <Video /> component on a screen.
  2. Background the app.
  3. Enable Developer Options → "Don't keep activities" (or wait for the OS to kill the process under memory pressure).
  4. Bring the app back to the foreground.
  5. MainActivity.onCreate throws NoSuchMethodException and the app crashes immediately.

Reproduction repository

No response

react-native-video version

7.0.0-beta.9

react-native version

0.85.2 (new architecture enabled)

react-native-nitro-modules version

No response

Platforms

Android

OS version

Android 16

Device

Real device (Samsung SM-A336B and many other devices in production logs)

Architecture

New Architecture

Expo

No response

Last working version

No response

Media / source type

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    To Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions