OnActivityResult 方法已弃用,有什么替代方法?

Ami*_*emi 192 android android-fragments android-activity onactivityresult

我最近发现 onActivityResult已被弃用。我们应该怎么处理呢?

为此引入了任何替代方案?

显示带有删除 onActivityResult 的代码的图像,表示已弃用

Mun*_*kon 302

developer.android.com上提供了基本培训。

这是一个关于如何使用新代码转换现有代码的示例:

旧方法:

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    startActivityForResult(intent, 123);
}

@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
    if (resultCode == Activity.RESULT_OK && requestCode == 123) {
        doSomeOperations();
    }
}
Run Code Online (Sandbox Code Playgroud)

新方式(Java):

// You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    // There are no request codes
                    Intent data = result.getData();
                    doSomeOperations();
                }
            }
        });

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    someActivityResultLauncher.launch(intent);
}
Run Code Online (Sandbox Code Playgroud)

新方式(Kotlin):

var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // There are no request codes
        val data: Intent? = result.data
        doSomeOperations()
    }
}

fun openSomeActivityForResult() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}
Run Code Online (Sandbox Code Playgroud)

编辑。更好的方法是让它更通用,以便我们可以重用它。下面的代码片段在我的一个项目中使用,但请注意它没有经过良好测试,可能无法涵盖所有​​情况。

更好的活动结果.java

var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // There are no request codes
        val data: Intent? = result.data
        doSomeOperations()
    }
}

fun openSomeActivityForResult() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}
Run Code Online (Sandbox Code Playgroud)

使用上述方法,您仍然需要在启动活动或片段附件之前或期间注册它。一旦定义,它就可以在活动或片段中重复使用。例如,如果你需要在大部分活动中开始新的活动,你可以像这样定义一个BaseActivity并注册一个新的BetterActivityResult

基本活动.java

import android.content.Intent;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCaller;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class BetterActivityResult<Input, Result> {
    /**
     * Register activity result using a {@link ActivityResultContract} and an in-place activity result callback like
     * the default approach. You can still customise callback using {@link #launch(Object, OnActivityResult)}.
     */
    @NonNull
    public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
            @NonNull ActivityResultCaller caller,
            @NonNull ActivityResultContract<Input, Result> contract,
            @Nullable OnActivityResult<Result> onActivityResult) {
        return new BetterActivityResult<>(caller, contract, onActivityResult);
    }

    /**
     * Same as {@link #registerForActivityResult(ActivityResultCaller, ActivityResultContract, OnActivityResult)} except
     * the last argument is set to {@code null}.
     */
    @NonNull
    public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
            @NonNull ActivityResultCaller caller,
            @NonNull ActivityResultContract<Input, Result> contract) {
        return registerForActivityResult(caller, contract, null);
    }

    /**
     * Specialised method for launching new activities.
     */
    @NonNull
    public static BetterActivityResult<Intent, ActivityResult> registerActivityForResult(
            @NonNull ActivityResultCaller caller) {
        return registerForActivityResult(caller, new ActivityResultContracts.StartActivityForResult());
    }

    /**
     * Callback interface
     */
    public interface OnActivityResult<O> {
        /**
         * Called after receiving a result from the target activity
         */
        void onActivityResult(O result);
    }

    private final ActivityResultLauncher<Input> launcher;
    @Nullable
    private OnActivityResult<Result> onActivityResult;

    private BetterActivityResult(@NonNull ActivityResultCaller caller,
                                 @NonNull ActivityResultContract<Input, Result> contract,
                                 @Nullable OnActivityResult<Result> onActivityResult) {
        this.onActivityResult = onActivityResult;
        this.launcher = caller.registerForActivityResult(contract, this::callOnActivityResult);
    }

    public void setOnActivityResult(@Nullable OnActivityResult<Result> onActivityResult) {
        this.onActivityResult = onActivityResult;
    }

    /**
     * Launch activity, same as {@link ActivityResultLauncher#launch(Object)} except that it allows a callback
     * executed after receiving a result from the target activity.
     */
    public void launch(Input input, @Nullable OnActivityResult<Result> onActivityResult) {
        if (onActivityResult != null) {
            this.onActivityResult = onActivityResult;
        }
        launcher.launch(input);
    }

    /**
     * Same as {@link #launch(Object, OnActivityResult)} with last parameter set to {@code null}.
     */
    public void launch(Input input) {
        launch(input, this.onActivityResult);
    }

    private void callOnActivityResult(Result result) {
        if (onActivityResult != null) onActivityResult.onActivityResult(result);
    }
}
Run Code Online (Sandbox Code Playgroud)

之后,您可以简单地从任何子活动中启动一个活动,如下所示:

public class BaseActivity extends AppCompatActivity {
    protected final BetterActivityResult<Intent, ActivityResult> activityLauncher = BetterActivityResult.registerActivityForResult(this);
}
Run Code Online (Sandbox Code Playgroud)

由于您可以与 一起设置回调函数Intent,因此您可以将其重用于任何活动。

同样,您也可以使用其他两个构造函数来使用其他活动合约。

  • 与旧方式相比,新方式看起来不必要地复杂...... (295认同)
  • 新方法比旧方法差得多。它破坏了代码模块化,并迫使您使用更多的代码行来覆盖以前版本的用例。当你提供更好的 API 设计时,应该使用弃用,但在 Google,人们会根据可操作的决策任意弃用某些东西,这些决策在技术上不合理,而且看起来基于少数用例。 (38认同)
  • 戏剧性的改变真的有必要吗?谷歌不断改变一些东西,比如某种婴儿尿布! (16认同)
  • @drmrbrewer 它消除了维护请求代码的麻烦并增加了运行测试的能力。但我认为它的主要问题是可重用性。在我看来,“ActivityResultCallback”应该是“ActivityResultLauncher#launch()”的一部分,以便我们可以将其重用于其他目的,例如,如果我需要检查多个操作的存储权限,我可以使用所有这些都只有一个“ActivityResultContracts.RequestPermission()”。您可以实现“ActivityResultCallback”来尝试解决此问题,但这会比旧方法更糟糕,因为您需要类似于请求代码的内容。 (8认同)
  • @drmrbrewer,同意。他们制作了一个不必要的工具,破坏了通常的多活动应用程序(尤其是 MVC)。现在我们必须创建许多回调,而不是一个“onActivityResult”。一个Activity包含10个Fragment,其中一些Fragment可以启动其他Activity并返回结果。之前我们只是在片段中返回“setResult()”并在“onActivityResult”中处理。现在我们必须创建许多回调。 (6认同)
  • 哇谷歌!,感谢这种新的复杂方式!以前的代码要容易得多。改进了结果代码的可重用性... (5认同)
  • 请注意,[根据文档](https://developer.android.com/training/basics/intents/result#register),绝对不应该使用“BetterActivityResult”解决方案 - `onActivityResult` 的全部原因是单独的回调而不是您在“启动”时设置的 lambda 是它需要在配置更改或进程死亡/重新创建后存在(这两种情况都可能在其他活动打开时发生 - 只需旋转您的设备以实现简单的情况) 。基于 lambda 的方法永远无法正确处理这些情况。 (4认同)
  • 我很惊讶这个 API 不叫“startActivityForResult2”。如果您认为处理结果代码很乏味,那么请等到您遇到这些混乱的情况为止。 (4认同)
  • @AndreaF 是的,当 Google 弃用“ViewPager”而转而使用“ViewPager2”时,我感到非常震惊,这造成的问题比解决任何问题都多。 (3认同)
  • 试图弄清楚如何将这个已弃用的代码迁移到 Kotlin 中的新方法。然而,我仍然不明白当没有替代品或者至少我没有看到明确的替代品时,您如何迁移“requestCode”情况。@ShubhamGupta 我正在使用 Kotlin。愿意解释一下如何替换对“requestCode”的依赖吗? (3认同)
  • 如果我的活动正在启动 2 个新活动来获取结果,那么我如何在 onActivityResult 上区分它? (2认同)
  • 我看到的唯一便利是删除“结果代码”。我们必须为每个活动添加不同的常量。有时我们可以混合使用,两个结果代码变得相等。这可能会导致不同的错误。 (2认同)
  • @MuntashirAkon 这个列表很长,不谈论 SAF-maggedon,而且不仅在 Android 平台上,而且不考虑 API 接口的向后兼容性,以至于现在我尽可能避免使用任何 Google 支持的框架,避免在代码库迁移中面临未来的麻烦和浪费时间。 (2认同)
  • 我如何传递请求代码? (2认同)

小智 41

替换已弃用的方法时,需要遵循 4 个简单的步骤startActivityForResult(...)

  1. 代替重写的方法onActivityResult(..)-

     ActivityResultLauncher<Intent> activityResultLaunch = registerForActivityResult(
             new ActivityResultContracts.StartActivityForResult(),
             new ActivityResultCallback<ActivityResult>() {
                 @Override
                 public void onActivityResult(ActivityResult result) {
                     if (result.getResultCode() == 123) {
                         // ToDo : Do your stuff...
                     } else if(result.getResultCode() == 321) {
                         // ToDo : Do your stuff...
                     }
                 }
    });
    
    Run Code Online (Sandbox Code Playgroud)

对于多个自定义请求,将条件附加为

if (result.getResultCode() == 123) {
..
} else if(result.getResultCode() == 131){
..
} // so on..
Run Code Online (Sandbox Code Playgroud)
  1. 进口:

     import androidx.activity.result.ActivityResult;
     import androidx.activity.result.ActivityResultCallback;
     import androidx.activity.result.ActivityResultLauncher;
     import androidx.activity.result.contract.ActivityResultContracts;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 代替 startActivityForResult(intent, 123),使用

     Intent intent = new Intent(this, SampleActivity.class);
     activityResultLaunch.launch(intent);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 在 SampleActivity.java 类中,返回源活动时,代码将保持不变,例如 -

    Intent intent = new Intent();
    setResult(123, intent);
    finish();
    
    Run Code Online (Sandbox Code Playgroud)

快乐编码!:)


Har*_*ara 25

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

Kotlin 示例

    fun openActivityForResult() {
        startForResult.launch(Intent(this, AnotherActivity::class.java))
    }


    val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { 
    result: ActivityResult ->
        if (result.resultCode == Activity.RESULT_OK) {
            val intent = result.data
            // Handle the Intent
            //do stuff here
        }
    }
Run Code Online (Sandbox Code Playgroud)


Mar*_*ler 22

在Java 8中可以这样写:

ActivityResultLauncher<Intent> startActivityForResult = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
    result -> {
        if (result.getResultCode() == AppCompatActivity.RESULT_OK) {
            Intent data = result.getData();
            // ...
        }
    }
);

Intent intent = new Intent( ... );
startActivityForResult.launch(intent);
Run Code Online (Sandbox Code Playgroud)

  • 如何处理多个requestCode?请帮忙。提前致谢。 (6认同)

San*_*inh 20

新方法是: registerForActivityResult

优势 :

  1. 新方法是降低我们在从片段或另一个活动中调用活动时所面临的复杂性
  2. 轻松请求任何许可并获得回调

在科特林:

var launchSomeActivity = registerForActivityResult(StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val data: Intent? = result.data
        // your operation...
    }
}

fun openYourActivity() {
    val intent = Intent(this, SomeActivity::class.java)
    launchSomeActivity.launch(intent)
}
Run Code Online (Sandbox Code Playgroud)

在 Java 中:

 // Create lanucher variable inside onAttach or onCreate or global
 ActivityResultLauncher<Intent> launchSomeActivity = registerForActivityResult(
     new ActivityResultContracts.StartActivityForResult(),
     new ActivityResultCallback<ActivityResult>() {
              @Override
              public void onActivityResult(ActivityResult result) {
                   if (result.getResultCode() == Activity.RESULT_OK) {
                         Intent data = result.getData();
                         // your operation....
                    }
               }
      });
    
      public void openYourActivity() {
            Intent intent = new Intent(this, SomeActivity.class);
            launchSomeActivity.launch(intent);
      }
Run Code Online (Sandbox Code Playgroud)


Lui*_*eno 14

在 KOTLIN 我改变了我的代码

startActivityForResult(intent, Constants.MY_CODE_REQUEST)
Run Code Online (Sandbox Code Playgroud)

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == Activity.RESULT_OK) {
        when (requestCode) {
            Constants.MY_CODE_REQUEST -> {
            ...
}
Run Code Online (Sandbox Code Playgroud)

registerForActivityResult(StartActivityForResult()) { result ->
    onActivityResult(Constants.MY_CODE_REQUEST, result)
}.launch(intent)
Run Code Online (Sandbox Code Playgroud)

private fun onActivityResult(requestCode: Int, result: ActivityResult) {
    if(result.resultCode == Activity.RESULT_OK) {
        val intent = result.data
        when (requestCode) {
            Constants.MY_CODE_REQUEST -> {
            ...
Run Code Online (Sandbox Code Playgroud)

我希望这个对你有用。:D

  • 新方式的 requestCode 似乎几乎没有用。 (5认同)
  • `registerForActivityResult` 上的第三个代码片段中的 `onActivityResult` 已弃用。 (3认同)
  • @FilipeBrito onActivityResult 不是一个覆盖的方法,它是我自己的方法,名称可以是任何名称;) (2认同)
  • 这不是正确的做法。如果您将启动与 registerForActivityResult 一起设置,我们可能会遇到初始化错误。最好先创建一个变量并在那里进行注册。 (2认同)

Ege*_*tçu 10

对于那些片段有多个 s 的人requestCode,如果您不确定如何处理这些requestCodes 的多个结果,您需要了解这requestCode在新方法中是无用的。

我想象你的旧代码是这样的:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == Activity.RESULT_CODE) {
        when (requestCode) {
            REQUEST_TAKE_PHOTO -> {
                // handle photo from camera
            }
            REQUEST_PICK_IMAGE_FROM_GALLERY -> {
                // handle image from gallery
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在新的 API 中,您需要在单独的中实现每个请求的结果ActivityResultContract

val takePhotoForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->
    if (result.resultCode == Activity.RESULT_OK) {
        val intent = result.data
        // handle photo from camera
    }
}

val pickImageFromGalleryForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->
    if (result.resultCode == Activity.RESULT_OK) {
        val intent = result.data
        // handle image from gallery
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你需要像这样开始这些活动/意图:

private fun startTakePhotoActivity() {
    takePhotoForResult.launch(Intent(requireActivity(), TakePhotoActivity::class.java))
}

private fun pickImageFromGallery() {
    val pickIntent = Intent(Intent.ACTION_PICK)
    pickIntent.setDataAndType(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        "image/*"
    )
    pickImageFromGalleryForResult.launch(pickIntent)
}
Run Code Online (Sandbox Code Playgroud)

通过这样做,您可以删除const val REQUEST_项目中的数百个值。

  • 您可以通过将项目中的数百个 const val REQUEST_ 值替换为数百个 ActivityResultContracts 来摆脱它们。优势在哪里? (4认同)

小智 7

参考:Kotlin - 从图库中选择图像

到目前为止我发现的最简单的选择

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

    var ivPhoto = findViewById<ImageView>(R.id.ivPhoto)
    var btnChoosePhoto = findViewById<Button>(R.id.btnChoosePhoto)

    

val getContent = registerForActivityResult(ActivityResultContracts.GetContent())  { uri: Uri? ->
            ivPhoto.setImageURI(uri)    // Handle the returned Uri
        }


    btnChoose.setOnClickListener {
        getContent.launch("image/*")
    }
    
    }
Run Code Online (Sandbox Code Playgroud)


小智 7

这里我解释一下新方法

private val scan =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult())
        { result: ActivityResult ->
            if (result.resultCode == AppCompatActivity.RESULT_OK && result.data != null) {

                var selected_hub = result!!.data!!.getParcelableExtra<ExtendedBluetoothDevice>(Utils.EXTRA_DEVICE)
                Log.d(TAG,"RECONNECT PROCESS "+selected_hub!!.name)
                reconnect(selected_hub!!)

            }
        }
Run Code Online (Sandbox Code Playgroud)

从活动或片段中调用它

private fun callScan() {
        val intent = Intent(requireActivity(), ScanningMeshDevices::class.java)
        scan.launch(intent)
    }
Run Code Online (Sandbox Code Playgroud)


khc*_*tro 6

onActivityResultstartActivityForResultrequestPermissions,和onRequestPermissionsResult弃用androidx.fragment1.3.0-alpha04,不是android.app.Activity
相反,您可以使用Activity Result APIswith registerForActivityResult


san*_*dip 5

以下代码在 Kotlin 片段中运行以检查蓝牙权限。

val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)

registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // There are no request codes
        val data: Intent? = result.data
        bluetoothAdapter.enable()
        Toast.makeText(context, "Permission Granted: ", Toast.LENGTH_SHORT).show()
        dynamicButton()
    }
    else{Toast.makeText(context, "You have to enable bluetooth to use this app.", Toast.LENGTH_SHORT).show()}
    
}.launch(intent)
Run Code Online (Sandbox Code Playgroud)