Reputation: 10270
Let's assume a fragment has this ActivityResultLauncher
:
class MyFragment : Fragment(R.layout.my_fragment_layout) {
companion object {
private const val EXTRA_ID = "ExtraId"
fun newInstance(id: String) = MyFragment().apply {
arguments = putString(EXTRA_ID, id)
}
}
private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
Timber.i("Callback successful")
}
}
...
This Fragment a wrapped in an Activity for temporary architectural reasons, it will eventually be moved into an existing coordinator pattern.
class FragmentWrapperActivity : AppCompatActivity() {
private lateinit var fragment: MyFragment
private lateinit var binding: ActivityFragmentWrapperBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFragmentWrapperBinding.inflate(this)
setContentView(binding.root)
fragment = MyFragment.newInstance("blah")
supportFragmentManager.transact {
replace(R.id.fragment_container, fragment)
}
}
}
And we use that launcher to start an Activity, expecting a result:
fun launchMe() {
val intent = Intent(requireContext(), MyResultActivity::class.java)
launcher.launch(intent)
}
On a normal device with plenty of available memory, this works fine. MyResultActivity
finishes with RESULT_OK
, the callback is called and I see the log line.
However, where memory is an issue and the calling fragment is destroyed, the launcher (and its callback) is destroyed along with it. Therefore when MyResultActivity
finishes, a new instance of my fragment is created which is completely unaware of what's just happened. This can be reproduced by destroying activities as soon as they no longer have focus (System -> Developer options -> Don't keep activities
).
My question is, if my fragment is reliant on the status of a launched activity in order to process some information, if that fragment is destroyed then how will the new instance of this fragment know where to pick up where the old fragment left off?
Upvotes: 0
Views: 1268
Reputation: 199880
Your minimal fragment is unconditionally replacing the existing fragment with a brand new fragment everytime it is created, thus causing the previous fragment, which has had its state restored, to be removed.
As per the Create a Fragment guide, you always need to wrap your code to create a fragment in onCreate
in a check for if (savedInstanceState == null)
:
In the previous example, note that the fragment transaction is only created when savedInstanceState is null. This is to ensure that the fragment is added only once, when the activity is first created. When a configuration change occurs and the activity is recreated, savedInstanceState is no longer null, and the fragment does not need to be added a second time, as the fragment is automatically restored from the savedInstanceState.
So your code should actually look like:
fragment = if (savedInstanceState == null) {
// Create a new Fragment and add it to
// the FragmentManager
MyFragment.newInstance("blah").also { newFragment ->
supportFragmentManager.transact {
replace(R.id.fragment_container, newFragment)
}
}
} else {
// The fragment already exists, so
// get it from the FragmentManager
supportFragmentManager.findFragmentById(R.id.fragment_container) as MyFragment
}
Upvotes: 2