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

有什么替代方案吗?


当前回答

另一种方法是分3步完成。(考虑到你有一个startActivityForResult(0和onActivityResult()))

创建一个形式为var resultLauncher的变量:ActivityResultLauncher<Intent> 创建一个私有函数,在其中以这种基本格式初始化resultLauncher

resultLauncher=registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result ->  

// copy paste the code from the onActivityResult replacing resultcode to result.resultCode  

if(result.resultcode==Activity.Result_OK){
val data=result.data // this data variable is of type intent and you can use it 

}else{
//code if you do not get the data 
}
}

使用startActivityForResult()返回行,并将其替换为resultLauncher.launch(intent)

其他回答

分享我找到的解决方法

首先,使用registerForActivityResult为结果注册这个活动 这将返回一个类型为ActivityResultLauncher<Intent!> 像这样,

private val getResult =
        registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) {
            if (it.resultCode == Activity.RESULT_OK) {
                val value = it.data?.getStringExtra("input")
            }
        }

现在,无论我们想在哪里启动result活动我们都可以使用getresult。launch(intent)

你可以在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();
    }
}

新方法(Java):

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

// 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();
                }
            }
        });

新方法(Kotlin):

fun openSomeActivityForResult() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}

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

编辑。一个更好的方法是使它更一般化,这样我们就可以重用它。下面的代码片段用于我的一个项目,但请注意,它没有经过良好的测试,可能无法涵盖所有情况。

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

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

BaseActivity.java

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

之后,你可以简单地从任何子活动中启动一个活动,就像这样:

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    activityLauncher.launch(intent, result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            // There are no request codes
            Intent data = result.getData();
            doSomeOperations();
        }
    })
}

因为你可以在Intent的同时设置回调函数,所以你可以在任何活动中重用它。

类似地,您也可以使用其他两个构造函数来使用其他活动契约。

在我的情况下,我试图使用意图,我直接移动到下一个活动,而不使用谷歌登录。

对我有用的是:

在OnCreate中为登录按钮设置onClickListener:

     btnSignIn.setOnClickListener {
        signIn()
        }

    private fun signIn() {
        val intent = client.signInIntent
        mainActivityResultLauncher.launch(intent)
    }

在上面的代码中,我写了去下一个活动的意图,但我必须写client.signInIntent

    var mainActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result ->

        if(result.resultCode == Activity.RESULT_OK){
            val data = result.data
            val task = GoogleSignIn.getSignedInAccountFromIntent(data)
            try {
                // Google Sign In was successful, authenticate with Firebase
                val account = task.getResult(ApiException::class.java)!!
                Log.d(TAG, "firebaseAuthWithGoogle:" + account.id)
                firebaseAuthWithGoogle(account.idToken!!)
            } catch (e: ApiException) {
                // Google Sign In failed, update UI appropriately
                Log.w(TAG, "Google sign in failed", e)
            }
        }
    }

对于那些具有多个requestCode的片段,并且如果您不确定如何处理这些requestCode的多个结果,那么您需要了解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
            }
        }
    }
}

在新的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
    }
}

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

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

通过这样做,你可以在你的项目中摆脱数百个const val REQUEST_值。

您可以为Koltin使用扩展函数。例如:

//random utils file
fun Fragment.buildGetContentRequest(function: (Uri) -> Unit): ActivityResultLauncher<String> {
    return this.registerForActivityResult(ActivityResultContracts.GetContent()) {
        function(it)
    }
}

fun Fragment.buildTakePhotoRequest(function: (Boolean) -> Unit): ActivityResultLauncher<Uri> {
    return this.registerForActivityResult(ActivityResultContracts.TakePicture()) {
        function(it)
    }
}

fun Fragment.buildSelectMultipleContentRequest(function: (MutableList<Uri>?) -> Unit): ActivityResultLauncher<String> {
    return this.registerForActivityResult(ActivityResultContracts.GetMultipleContents()) {
        function(it)
    }
}

然后在你的片段中像这样

//your actual fragment logic
class YourFragment : Fragment() {
    //we can assign our request in init process
    private val mRequestSelectFiles = buildSelectMultipleContentRequest { 
        onFilesSelected(it) 
    }


    fun onSelectFiles() {
        val mime = "*/*"
        mRequestSelectFiles.launch(mime)
    }

    fun onFilesSelected(list: MutableList<Uri>?) {
        //your logic
    }
}