在 android 的 onResume 之后使用 ActivityResultRegistry 注册合同有多安全?

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

以下代码给我错误,因为在 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
            )
        }

    }

但是,如果我使用 activityResultRegistry,我不会收到任何错误。密码是

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
            )
        }

    }

后面的代码运行没有任何问题,并启动了相应的意图。我只想知道后一个有多安全,是否有任何我应该注意的不良行为?

它给你一个错误,因为你在 Activity 进入其生命周期后 有条件地 注册合同。

guide 说:

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.

很明显,如果你在创建Activity之后注册一些东西,并且只有在满足条件(本例中是点击事件)时才会发生,无法保证注册的顺序。

更好的解决方案是在创建 Activity 之前注册合约,并在需要时调用 launch()。该指南再次表示它是完全安全的:

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.

所以在你的情况下,Activity 看起来像这样:

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
            )
        }
    }
}

进一步说明:

registerForActivityResult() 是一种方便的方法,它使用自动创建的密钥在内部调用注册表的 register 方法。密钥派生自内部 AtomicInteger,每次调用 registerForActivityResult() 时都会检索并递增该密钥。由于此键用于查找将处理结果的回调,因此对 registerForActivityResult 的每次调用必须以相同的顺序进行,否则可能会发生您按 A (key=0), B (key=1) 然后你称它为 B (key=0), A (key=1),或者甚至不调用其中一个合约的 register 方法(这正是您在 OnClickListener 中注册时发生的情况)。

在您的特定情况下,如果在您等待启动合约到 return 时重新创建 Activity(例如,发生配置更改或系统简单地终止应用程序),则callback 将从注册表中删除(尽管密钥保留在那里),这意味着它不会与结果一起调用。

因此,总结一下整个事情:您可以(应该)安全地将任何合约注册为您的Activity或[=20=中的成员字段],你永远不应该注册合同 on-the-fly(a.k.a。有条件地)。注册合约没有什么特别的,真正的交易发生在你启动它时。