如何用活动结果 API 替换 startActivityForResult?

Posted

技术标签:

【中文标题】如何用活动结果 API 替换 startActivityForResult?【英文标题】:How to replace startActivityForResult with Activity Result APIs? 【发布时间】:2020-08-10 19:14:11 【问题描述】:

我有一个主要活动作为调用不同活动的入口点,具体取决于条件。其中,我使用 Firebase Auth 来管理用户登录:

startActivityForResult(
            AuthUI.getInstance().createSignInIntentBuilder()
                    .setAvailableProviders(providers)
                    .build(),
            RC_SIGN_IN)

我覆盖onActivityResult()来区分返回的intent/data,例如:

 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) 
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) 

        REQUEST_CODE_1 -> 
          // update UI and stuff
        

        RC_SIGN_IN -> 
          // check Firebase log in
        
        // ...
    

使用documentation 强烈推荐的活动结果API,我知道我应该在ActivityResultLauncher 之前创建prepareCall(),并确保在我启动时活动处于创建状态,但我仍然没有不了解如何优雅地处理多个活动结果(至少在一个地方),例如 onActivityResult()

看着this article,看来我需要实现多个 ActivityResultContract 类型的子内部类(因此多个prepareCall() 的?),因为它们应该是不同的合同,我说的对吗?有人可以给我看一些反映上述onActivityResult()逻辑的骨架示例吗?

【问题讨论】:

【参考方案1】:

您可以根据需要为结果调用任意数量的活动,并且每个活动都有单独的回调:

    val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult())
     result: ActivityResult ->
        if (result.resultCode == Activity.RESULT_OK) 
            //  you will get result here in result.data
            

        
    

    startForResult.launch(Intent(activity, CameraCaptureActivity::class.java))

您只需要指定 Activity 类 - CameraCaptureActivity::class.java

更新:

The prepareCall() method has been renamed to registerForActivityResult() in Activity 1.2.0-alpha04 and Fragment 1.3.0-alpha04. And it should be startForResult.launch(...) in the last line

感谢 Rafael Tavares 更新

【讨论】:

prepareCall() 方法已在 Activity 1.2.0-alpha04 和 Fragment 1.3.0-alpha04 中重命名为 registerForActivityResult()。最后一行应该是startForResult.launch(...) 如果将'android.content.Intent intent, int requestCode' 作为参数传递呢? 但是,为了从Activity2发回结果,和往常一样吗?:setResult(Bundle)??? 它返回result.data为null【参考方案2】:

从现在开始,startActivityForResult() 已被弃用,因此请改用新方法。

示例

public void openActivityForResult() 
    
 //Instead of startActivityForResult use this one
        Intent intent = new Intent(this,OtherActivity.class);
        someActivityResultLauncher.launch(intent);
    


//Instead of onActivityResult() method use this one

    ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            new ActivityResultCallback<ActivityResult>() 
                @Override
                public void onActivityResult(ActivityResult result) 
                    if (result.getResultCode() == Activity.RESULT_OK) 
                        // Here, no request code
                        Intent data = result.getData();
                        doSomeOperations();
                    
                
            );

【讨论】:

用谷歌登录 onActivityForResult 试试这个代码,没有运气,有什么建议吗? 这段代码对我有用;不过不得不翻译成kotlin。谢谢 感谢您不使用“var”和 lambdas :) @paulsm4 不客气!我没有写它是因为它很容易理解。 更简洁明了的描述✓【参考方案3】:

首先,不要忘记将它添加到您的 Gradle 依赖项中

implementation 'androidx.activity:activity-ktx:1.2.0-alpha05'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha05'

其次,通过扩展一个名为 ActivityResultContract&lt;I, O&gt; 的抽象类来创建结果协定。我的意思是输入的类型,O 的意思是输出的类型。然后你只需要重写2个方法

class PostActivityContract : ActivityResultContract<Int, String?>() 

    override fun createIntent(context: Context, input: Int): Intent 
        return Intent(context, PostActivity::class.java).apply 
            putExtra(PostActivity.ID, postId)
        
    

    override fun parseResult(resultCode: Int, intent: Intent?): String? 
        val data = intent?.getStringExtra(PostActivity.TITLE)
        return if (resultCode == Activity.RESULT_OK && data != null) data
        else null
    

最后,最后一步是将合约注册到Activity。您需要将自定义合约和回调传递给registerForActivityResult

class MainActivity : AppCompatActivity() 

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
      
        start_activity_contract.setOnClickListener 
            openPostActivityCustom.launch(1)
        
    
  
    // Custom activity result contract
    private val openPostActivityCustom =
        registerForActivityResult(PostActivityContract())  result ->
            // parseResult will return this as string?                                              
            if (result != null) toast("Result : $result")
            else toast("No Result")
        

更多信息请查看Post

【讨论】:

正在寻找依赖项,感谢添加。获得支持:) 很高兴听到它对你有帮助 :) 指定请求Intent的好方法!谢谢!【参考方案4】:

在这种情况下,AuthUI 返回的已经是一个 Intent,所以,我们像下面的例子一样使用它。

private val startForResult =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult())  result ->
            when(result.resultCode)
                RESULT_OK -> 
                    val intent = result.data
                    // Handle the Intent...
                    mUser = FirebaseAuth.getInstance().currentUser
                
                RESULT_CANCELED -> 

                 else -> 
             
        

使用以下方式从任何地方(例如单击按钮)启动活动:

 AuthUI.getInstance().createSignInIntentBuilder().setAvailableProviders(providers)
            .build().apply 
                startForResult.launch(this)
            

【讨论】:

【参考方案5】:
List<AuthUI.IdpConfig> providers = Arrays.asList(
                    new AuthUI.IdpConfig.EmailBuilder().build(),
                    new AuthUI.IdpConfig.GoogleBuilder().build());

            ActivityResultLauncher<Intent> launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> 
                if (result.getResultCode() == Activity.RESULT_OK) 
                    Log.v("LOGIN OK", "OK Result for Login");
                
            );
            
            launcher.launch(AuthUI.getInstance()
                    .createSignInIntentBuilder()
                    .setIsSmartLockEnabled(false)
                    .setAvailableProviders(providers)
                    .build());

查看更多详情: https://githubmemory.com/repo/firebase/FirebaseUI-Android/issues?cursor=Y3Vyc29yOnYyOpK5MjAyMS0wMy0wNVQyMjoxNzozMyswODowMM4xEAQZ&pagination=next&page=2

【讨论】:

【参考方案6】:

将此用于 Firebase AuthUI;

final ActivityResultLauncher<Intent> launcher = registerForActivityResult(
            new FirebaseAuthUIActivityResultContract(), this::onSignInResult);

    binding.loginSignup.setOnClickListener(view -> 
        List<AuthUI.IdpConfig> provider = Arrays.asList(new AuthUI.IdpConfig.EmailBuilder().build(),
                new AuthUI.IdpConfig.GoogleBuilder().build(),
                new AuthUI.IdpConfig.PhoneBuilder().build());
        Intent intent = AuthUI.getInstance()
                .createSignInIntentBuilder()
                .setIsSmartLockEnabled(false)
                .setAlwaysShowSignInMethodScreen(true)
                .setAvailableProviders(provider)
                .build();
        launcher.launch(intent);
    );


private void onSignInResult(FirebaseAuthUIAuthenticationResult result) 

    if (result.getResultCode() == RESULT_OK) 
        FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
        if (user != null) 
            if (user.getMetadata() != null) 
                if (user.getMetadata().getCreationTimestamp() != user.getMetadata().getLastSignInTimestamp()) 
                    Toast.makeText(this, "Welcome Back", Toast.LENGTH_SHORT).show();
                 else 
                    Toast.makeText(this, "Welcome", Toast.LENGTH_SHORT).show();
                
                startMainActivity();
            
        
     else 
        IdpResponse response = result.getIdpResponse();
        if (response == null)
            Toast.makeText(this, "Canceled By You", Toast.LENGTH_SHORT).show();
        else Log.d(TAG, "onCreate: ActivityResult" + response.getError());
    


private void startMainActivity() 
    Intent intent = new Intent(LoginActivity.this, MainActivity.class);
    startActivity(intent);
    finish();

像这样。

【讨论】:

【参考方案7】:

如果您从片段开始活动并将结果返回给片段,请执行此操作。

在片段中:

private lateinit var activityResult: ActivityResultLauncher<Intent>

activityResult = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult())  result ->
    if (result.resultCode == RESULT_OK) 
        val data = result.data
        doSomeOperations(data)
    


SomeActivity.showScreen(activityResult, requireContext())

活动中:

// activity?.setResult(Activity.RESULT_OK) doesn't change.

companion object 

    fun showScreen(activityResult: ActivityResultLauncher<Intent>, context: Context) 
        val intent = Intent(context, SomeActivity::class.java)
        activityResult.launch(intent)
    

【讨论】:

什么是 SomeActivity.showScreen(activityResult, requireContext()) 我的意思是什么是 showScreen ???你能再描述一下吗? @YogiArifWidodo, fun showScreen(activityResult: ActivityResultLauncher&lt;Intent&gt;, context: Context) 在答案的第二部分。您可以从片段中打开活动SomeActivity,执行一些操作并将该活动的结果返回给片段。

以上是关于如何用活动结果 API 替换 startActivityForResult?的主要内容,如果未能解决你的问题,请参考以下文章

使用动态上下文数据时如何用新的 React 上下文 api 替换旧的 React contextTypes?

如何用python中的方程替换数据框中的缺失值

如何用 Promise.all 替换多个 async/await 调用?

如何用总和替换文件中的数字?

如何用新的 Blob 构造函数替换已弃用的 BlobBuilder?

如何用dict pandas python替换分组数据框