Search code examples
androidandroid-fragmentsandroid-activityandroid-bundleandroid-screen

The bundle i sent to Fragment from Activity is still alive when i rotate the screen. WHY?


I have an Activity and there are a TextView, an EditText, a button and, a FrameLayout. I put some words in the EditText and I push the send button, the Activity's TextView is gonna be changed with the word and also makes a bundle that has the word and starts a Fragment. in the onCreate method of the fragment, I can get arguments that I just sent from Activity. and I put the word in Fragment's TextView. but the problem begins when I rotate the screen.

I learned all attributes will reset when the screen rotated. so, Activity's TextView is reset. it's fine. I knew it. but the Fragment's TextView is even still alive there! it's not reset.

WHY?

I checked the Logs. this is so weird. This is all of the order of the App.

2020-09-17 11:53:16.569 15207-15207/com.example.screenlotateex D/TEST: Activity onCreate()

2020-09-17 11:53:33.541 15207-15207/com.example.screenlotateex D/TEST: send Bundle from Activity

2020-09-17 11:53:33.623 15207-15207/com.example.screenlotateex D/TEST: Fragment onCreate()

2020-09-17 11:53:33.623 15207-15207/com.example.screenlotateex D/TEST: get Bundle from Activity : it's raining now.

2020-09-17 11:53:37.331 15207-15207/com.example.screenlotateex D/TEST: Activity onPause()

2020-09-17 11:53:37.340 15207-15207/com.example.screenlotateex D/TEST: Activity onStop()

2020-09-17 11:53:37.366 15207-15207/com.example.screenlotateex D/TEST: Fragment onDestroyView()

2020-09-17 11:53:37.378 15207-15207/com.example.screenlotateex D/TEST: Fragment onDestroy()

2020-09-17 11:53:37.378 15207-15207/com.example.screenlotateex D/TEST: Fragment onDetach()

2020-09-17 11:53:37.381 15207-15207/com.example.screenlotateex D/TEST: Activity onDestroy()

2020-09-17 11:53:37.526 15207-15207/com.example.screenlotateex D/TEST: Fragment onCreate()

2020-09-17 11:53:37.526 15207-15207/com.example.screenlotateex D/TEST: get Bundle from Activity : it's raining now.

2020-09-17 11:53:37.780 15207-15207/com.example.screenlotateex D/TEST: Activity onCreate()

How come the fragment's onCreate method runs automatically when I didn't press the button again? the weirdest thing is the fragment's onCreate starts before the activity's onCreate!!! and the bundle is still alive!! it's scary.

this is my code.

Activity

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    Log.d("TEST", "Activity onCreate()")

    button.setOnClickListener {
        text.text = editText.text.toString()
        val bundle = Bundle()
        Log.d("TEST", "send Bundle from Activity")
        bundle.putString("key", editText.text.toString())
        val fragment = TestFragment()
        fragment.arguments = bundle
        supportFragmentManager.beginTransaction().replace(R.id.frameLayout, fragment).commit()
    }
}

override fun onPause() {
    super.onPause()
    Log.d("TEST", "Activity onPause()")
}

override fun onStop() {
    super.onStop()
    Log.d("TEST", "Activity onStop()")
}

override fun onDestroy() {
    super.onDestroy()
    Log.d("TEST", "Activity onDestroy()")
}
}

Activity XML

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
    android:id="@+id/text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<EditText
    android:id="@+id/editText"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toBottomOf="@id/text"
    android:hint="write here."/>

<Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="SEND"
    app:layout_constraintTop_toBottomOf="@id/editText"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

<FrameLayout
    android:id="@+id/frameLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Fragment

class TestFragment : Fragment() {

private var text: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Log.d("TEST", "Fragment onCreate()")
    arguments?.let {
        text = it.getString("key")
        Log.d("TEST", "get Bundle from Activity : $text")
    }
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_test, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    frag_text.text = text
}

override fun onDestroyView() {
    super.onDestroyView()
    Log.d("TEST", "Fragment onDestroyView()")
}

override fun onDestroy() {
    super.onDestroy()
    Log.d("TEST", "Fragment onDestroy()")
}

override fun onDetach() {
    super.onDetach()
    Log.d("TEST", "Fragment onDetach()")
}

}

Fragment XML

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".TestFragment">

<TextView
    android:id="@+id/frag_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_blank_fragment"
    android:textColor="@color/colorAccent"
    android:textSize="30sp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.564"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.243" />
  </androidx.constraintlayout.widget.ConstraintLayout>

I really hope somebody helps me.


Solution

  • A core part of all of Android is saving state, putting the user back exactly at the same state as they were after you rotate your device, switch to another app and switch back, etc. This is why classes like Activity have the onSaveInstanceState() method.

    As part of this libraries, such as Fragments, automatically plug into those methods. This means that the FragmentManager automatically saves and restores its state - including what Fragments exist and their bundle of arguments.

    In the case of Fragments, they are restored as part of the call to super.onCreate() - if you had moved your log message before super.onCreate(), you'd see it run before the Fragment's onCreate().