Home > Enterprise >  How safe is registering contracts with ActivityResultRegistry after onResume in android?
How safe is registering contracts with ActivityResultRegistry after onResume in android?

Time:05-06

The following code gives me error as registering occurs after onResume:

class TempActivity: AppCompatActivity(){
      private lateinit var binding: ActivityTempBinding
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityTempBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.tempBtn.setOnClickListener {
            val a = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
                //SomeCode
            }
            a.launch(
                //SomeIntent
            )
        }

    }

However, if I use activityResultRegistry, I am not getting any errors. The code is

class TempActivity: AppCompatActivity(){
      private lateinit var binding: ActivityTempBinding
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityTempBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.tempBtn.setOnClickListener {
            val a = activityResultRegistry.register("key", ActivityResultContracts.StartActivityForResult()){
            // SomeCode
            }
            a.launch(
               //Some Intent
            )
        }

    }

The latter code run without any problem and launches the corresponding intent. I just want to know how safe is latter one and is there any unwanted behaviors I should be aware of?

CodePudding user response:

It gives you an error because you are registering the contract conditionally after the Activity is well into its lifecycle.

The guide says:

You must always call registerForActivityResult() in the same order for each creation of your fragment or activity to ensure that the inflight results are delivered to the correct callback.

It's clear that if you register something after the Activity is created and it only happens when a condition (click event in this case) is met, the order of registration cannot be ensured.

A better solution would be to register the contract before the Activity is created and just call launch() when you need it. The guide, once again, says it is completely safe:

registerForActivityResult() is safe to call before your fragment or activity is created, allowing it to be used directly when declaring member variables for the returned ActivityResultLauncher instances.

So in your case, the Activity would look like this:

class TempActivity: AppCompatActivity() {
    private lateinit var binding: ActivityTempBinding
    
    // registering the contract here
    private val a = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
        //SomeCode
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityTempBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.tempBtn.setOnClickListener {
            // launching the registered contract
            a.launch(
                //SomeIntent
            )
        }
    }
}

Further explanation:

The registerForActivityResult() is a convenience method that internally calls the registry's register method with an automatically created key. The key is derived from an internal AtomicInteger that is retrieved and incremented every time you call registerForActivityResult(). Since this key is used to look up the callback that will handle the result, every call to the registerForActivityResult must be in the same order, otherwise it might happen that you once call it in the order of A (key=0), B (key=1) but then you call it B (key=0), A (key=1), or not even call the register method for one of the contracts (this is exactly what happens when you register in the OnClickListener).

In your specific case if the Activity gets recreated while you're waiting for the launched contract to return (for example, configuration change happens or the system simply kills the app), the callback will be removed from the registry (the key remains there though), meaning that it will not be called with the results.

So, to summarize the whole thing: you can (should) safely register any contract as a member field in your Activity or in the onCreate(...), and you should never register a contract on-the-fly (a.k.a. conditionally). Registering the contract will do nothing special, the real deal happens when you launch it.

  • Related