Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear happens after onViewCreated #116

Open
Miartg opened this issue Jun 2, 2023 · 5 comments
Open

Clear happens after onViewCreated #116

Miartg opened this issue Jun 2, 2023 · 5 comments
Assignees
Labels
bug Something isn't working
Milestone

Comments

@Miartg
Copy link

Miartg commented Jun 2, 2023

Description:

If you quickly add and remove a fragment from the back stack, then there is a situation where onViewDestroyed is called after onViewCreated. This causes onViewCreated to use the old view. The problem is floating and reproduces over time.

I think the problem is that postClear of onDestroy from viewLifecycle doesn't guarantee that clear will be called before onViewCreated.

Source code:

class MainActivity : AppCompatActivity(R.layout.activity_main) {
    fun onFragmentAClick() {
        supportFragmentManager.executePendingTransactions()
        supportFragmentManager.beginTransaction()
            .replace(R.id.fragment_container, FragmentB())
            .setTransition(TRANSIT_FRAGMENT_FADE)
            .addToBackStack(null)
            .commit()
    }

    fun onFragmentBClick() {
        supportFragmentManager.executePendingTransactions()
        supportFragmentManager.popBackStack()
    }
}

class FragmentA : Fragment(R.layout.fragment_a) {
    private val viewBinding by viewBinding(
        vbFactory = FragmentABinding::bind,
        onViewDestroyed = { Log.d(LOG_TAG, "FragmentA onViewDestroyed binding = $it") }
    )

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.d(LOG_TAG, "FragmentA onViewCreated binding = $viewBinding")
        viewBinding.root.text = "Fragment A onViewCreated"
        viewBinding.root.setOnClickListener { (context as? MainActivity)?.onFragmentAClick() }
    }
}

class FragmentB : Fragment(R.layout.fragment_b) {
    private val viewBinding by viewBinding(
        vbFactory = FragmentBBinding::bind,
        onViewDestroyed = { Log.d(LOG_TAG, "FragmentB onViewDestroyed binding = $it") }
    )

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.d(LOG_TAG, "FragmentB onViewCreated binding = $viewBinding")
        viewBinding.root.text = "Fragment B onViewCreated"
        viewBinding.root.setOnClickListener { (context as? MainActivity)?.onFragmentBClick() }
    }
}

private const val LOG_TAG = "ViewBindingDelegate"
<!--activity_main.xml-->
<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container"
    android:name=".FragmentA"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

<!--fragment_a.xml-->
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?colorPrimary"
    android:gravity="center"
    android:text="Fragment A"
    android:textColor="?colorOnPrimary"
    />

<!--fragment_b.xml-->
<TextView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?colorSecondary"
    android:gravity="center"
    android:text="Fragment B"
    android:textColor="?colorOnSecondary"
    />

Logs:

FragmentB onViewCreated binding = FragmentBBinding@d1b9dfd
FragmentA onViewDestroyed binding = FragmentABinding@a96db92
FragmentA onViewCreated binding = FragmentABinding@10bb7ab
FragmentB onViewDestroyed binding = FragmentBBinding@d1b9dfd
FragmentB onViewCreated binding = FragmentBBinding@51e195a
FragmentA onViewCreated binding = FragmentABinding@10bb7ab
FragmentA onViewDestroyed binding = FragmentABinding@10bb7ab
FragmentB onViewDestroyed binding = FragmentBBinding@51e195a

It can be seen that the FragmentA onViewCreated binding = FragmentABinding@10bb7ab was called twice

@LeafyLappa
Copy link

LeafyLappa commented Aug 29, 2023

I'm having the same problem. Took me hours already and I haven't even been able to solve it.

upd: in case you want to know how I fixed it.

The presentation logic was very poorly written (we had to publish the application ASAP) which is why in some cases there was a fragment opening and instantly closing without displaying anything. I changed the logic so that the fragment wouldn't be needlessly created and the whole problem was gone.

This sort of implies that this issue won't really need a fix.

@kirich1409
Copy link
Collaborator

Hi. It's interesting case. Need to add additional check of Fragment View state. Will be investigated

@kirich1409 kirich1409 self-assigned this Oct 8, 2023
@kirich1409 kirich1409 added the bug Something isn't working label Oct 8, 2023
@xloger
Copy link

xloger commented Nov 8, 2023

I had the same problem when I used Navigation and fast forward and back. Let me first describe my situation:
My general logic in MainFragment is as follows, and there are no other ViewBinding operations. Returned in BFragment via findNavController().popBackStack().

//MainFragment
private val binding by viewBinding(FragmentMainBinding::bind, onViewDestroyed = {
        printer.debug("onViewDestroyed;binding:${it.idPrint()}")
})

fun onViewCreated() {
	printer.info("onViewCreated")
	// set background is red,then background is white when happen bug。
	binding.root.setBackgroundColor(Color.RED)
	binding.root.setOnClickListener {
            printer.debug("navigate to BFragment")
            findNavController().navigate(R.id.bFragment, null, animOptions)
        }
}

fun onDestroyView() {
	printer.debug("onDestroyView")
}

Then I quickly switch MainFragment and BFragment, which is easier to reproduce .
When an exception is encountered, its log is like this:

2023-11-08 14:56:42.769 I  onViewCreated
2023-11-08 14:56:42.770 D  useBinding:FragmentMainBinding@32292761
2023-11-08 14:56:42.770 I  onViewCreated end
2023-11-08 14:56:42.770 V  MainFragment@166602538 onStart
2023-11-08 14:56:42.772 V  MainFragment@166602538 onResume
2023-11-08 14:56:42.918 D  navigate to BFragment
2023-11-08 14:56:42.933 V  MainFragment@166602538 onPause
2023-11-08 14:56:42.934 V  MainFragment@166602538 onStop
2023-11-08 14:56:42.936 V  BFragment@148110504 onCreate
2023-11-08 14:56:42.946 V  BFragment@148110504 onStart
2023-11-08 14:56:42.961 V  BFragment@227944255 onDestroy
2023-11-08 14:56:42.962 V  BFragment@148110504 onResume
2023-11-08 14:56:43.085 D  navigate popBackStack to AFragment
2023-11-08 14:56:43.096 V  BFragment@148110504 onPause
2023-11-08 14:56:43.098 V  BFragment@148110504 onStop
2023-11-08 14:56:43.098 V  MainFragment@166602538 onStart
2023-11-08 14:56:43.103 V  MainFragment@166602538 onResume
2023-11-08 14:56:43.273 V  BFragment@148110504 onDestroy
2023-11-08 14:56:43.281 D  navigate to BFragment
2023-11-08 14:56:43.298 V  MainFragment@166602538 onPause
2023-11-08 14:56:43.299 V  MainFragment@166602538 onStop
2023-11-08 14:56:43.301 V  BFragment@248523369 onCreate
2023-11-08 14:56:43.313 V  BFragment@248523369 onStart
2023-11-08 14:56:43.314 V  BFragment@248523369 onResume
2023-11-08 14:56:43.453 D  navigate popBackStack to AFragment
2023-11-08 14:56:43.457 V  BFragment@248523369 onPause
2023-11-08 14:56:43.458 V  BFragment@248523369 onStop
2023-11-08 14:56:43.459 V  MainFragment@166602538 onStart
2023-11-08 14:56:43.463 V  MainFragment@166602538 onResume
2023-11-08 14:56:43.632 V  BFragment@248523369 onDestroy
2023-11-08 14:56:43.642 D  navigate to BFragment
2023-11-08 14:56:43.657 V  MainFragment@166602538 onPause
2023-11-08 14:56:43.659 V  MainFragment@166602538 onStop
2023-11-08 14:56:43.661 V  BFragment@199799963 onCreate
2023-11-08 14:56:43.673 V  BFragment@199799963 onStart
2023-11-08 14:56:43.674 V  BFragment@199799963 onResume
2023-11-08 14:56:43.827 D  navigate popBackStack to AFragment
2023-11-08 14:56:43.829 D  onDestroyView
2023-11-08 14:56:43.831 V  BFragment@199799963 onPause
2023-11-08 14:56:43.832 V  BFragment@199799963 onStop
2023-11-08 14:56:43.849 I  onViewCreated
2023-11-08 14:56:43.850 D  useBinding:FragmentMainBinding@32292761
2023-11-08 14:56:43.850 I  onViewCreated end
2023-11-08 14:56:43.850 V  MainFragment@166602538 onStart
2023-11-08 14:56:43.852 V  MainFragment@166602538 onResume
2023-11-08 14:56:43.853 D  onViewDestroyed;binding:FragmentMainBinding@32292761
2023-11-08 14:56:44.028 V  BFragment@199799963 onDestroy

It can be found that after using FragmentMainBinding@32292761 last time, after frequent switching, onDestroyView was called correctly, but this time onViewCreated used the last FragmentMainBinding@32292761 before it happened onViewDestroyed of ViewBindingPropertyDelegate.

I read several previous issues and learned that you have fixed similar problems. My testing came to the following conclusions:

  1. If you do not use ViewBindingPropertyDelegate, but use _binding and onDesotoryView { _binding = null }, it is normal
  2. If animation is not used when switching Fragments (animOptions is set to null), it is also normal.
val animOptions = navOptions {
            anim {
                enter = androidx.navigation.ui.R.anim.nav_default_enter_anim
                exit = androidx.navigation.ui.R.anim.nav_default_exit_anim
                popEnter = androidx.navigation.ui.R.anim.nav_default_pop_enter_anim
                popExit = androidx.navigation.ui.R.anim.nav_default_pop_exit_anim
            }
        }

I believe the problem lies with Fragment's transition animation, but I have no idea how to solve this problem on ViewBindingPropertyDelegate.

@alexatmythical
Copy link

Same (( Decided to use pure ViewBinding as a temporary solution

@kirich1409
Copy link
Collaborator

Will be fixed in 2.0.0

@kirich1409 kirich1409 added this to the 2.0.0 milestone Jan 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants