registerForActivityResult方法介绍以及封装,解决点击事件中无法调用问题

registerForActivityResult 方法介绍

1.1 过时的 startActivityForResult 方法

在API 29 之前的版本中,跳转 Activity 获取返回结果使用的是startActivityForResult 方法,而 startActivityForResult 方法在API 29 中废弃,为过时方法,而谷歌推出 registerForActivityResult 方法来替代。
startActivityForResult 方法用法如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        ...
        findViewById(R.id.btn_start).setOnClickListener(v -> {
    
    
            startActivityForResult(new Intent(MainActivity.this, SecondActivity.class), 1);
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    
    
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1 && resultCode == RESULT_OK) {
    
    

        }
    }

这种方式需要重写 onActivityResult 方法,然后判断 requestCode 和 resultCode 的方式处理回调,流程麻烦了一点且不合时宜。而 registerForActivityResult 方法简化了回调的流程,在开发中更合理的处理回调。

1.2 registerForActivityResult 错误的调用方式

调用 registerForActivityResult 方法时,可能有人会像下面的错误方式在点击事件中调用:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        ...
        findViewById(R.id.btn_start).setOnClickListener(v -> {
    
    
            ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
    
    
                @Override
                public void onActivityResult(ActivityResult result) {
    
    

                }
            });
            activityResultLauncher.launch(new Intent(MainActivity.this, SecondActivity.class));
        });
    }

这样的调用方式会出现如下报错:

java.lang.IllegalStateException: LifecycleOwner com.example.testapp.MainActivity@f5102ac is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.

LifecycleOwners 必须在状态为 STARTED 之前调用,LifecycleOwners 也就是 Activity,意思为 registerForActivityResult 方法需要在 onCreate 和 onStart 或者 onRestart 的生命周期之间调用,在其他生命周期调用包含点击事件中调用会出现抛出异常。

在 registerForActivityResult 方法调用栈中,ActivityResultRegistry 的 register 方法会检查当前的 lifecycle 状态是否为 STARTED 之前的状态,不符合则抛出异常。

//ActivityResultRegistry.java
@NonNull
public final <I, O> ActivityResultLauncher<I> register(
        @NonNull final String key,
        @NonNull final LifecycleOwner lifecycleOwner,
        @NonNull final ActivityResultContract<I, O> contract,
        @NonNull final ActivityResultCallback<O> callback) {
    
    
    Lifecycle lifecycle = lifecycleOwner.getLifecycle();
    if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
    
    
        throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is "
                + "attempting to register while current state is "
                + lifecycle.getCurrentState() + ". LifecycleOwners must call register before "
                + "they are STARTED.");
    }
    ...
}

1.3 registerForActivityResult 正确的调用方式

为了避免如上异常,正确用法如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        ...
        ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
    
    
            @Override
            public void onActivityResult(ActivityResult result) {
    
    

            }
        });
        findViewById(R.id.btn_start).setOnClickListener(v -> {
    
    
            activityResultLauncher.launch(new Intent(MainActivity.this, SecondActivity.class));
        });
    }

registerForActivityResult 的封装

在非 onCreate 、onStart、onRestart 的生命周期方法回调中调用 registerForActivityResult 方法会跑吹异常,如果想在点击事件或者其他时序中调用 registerForActivityResult 方法,可以利用 Fragment 的创建周期实现对 registerForActivityResult 方法的调用。封装如下:

public class ActivityResultFragment extends Fragment {
    
    
    private ActivityResultCallback<ActivityResult> mCallback;
    private Intent mIntent;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
    
    
            if (mCallback != null) {
    
    
                mCallback.onActivityResult(result);
            }
            getParentFragmentManager().beginTransaction()
                    .remove(ActivityResultFragment.this)
                    .commitAllowingStateLoss();

        });
        if (mIntent != null) {
    
    
            activityResultLauncher.launch(mIntent);
        }
    }

    public static void launchActivityResult(FragmentManager fragmentManager, ActivityResultCallback<ActivityResult> callback, Intent intent) {
    
    
        ActivityResultFragment fragment = new ActivityResultFragment();
        fragment.setCallback(callback);
        fragment.setIntent(intent);
        fragmentManager.beginTransaction()
                .add(fragment, "ActivityResultFragment")
                .commitAllowingStateLoss();
    }
    
    private void setIntent(Intent intent) {
    
    
        mIntent = intent;
    }

    private void setCallback(ActivityResultCallback<ActivityResult> callback) {
    
    
        mCallback = callback;
    }

    @Override
    public void onDestroy() {
    
    
        mCallback = null;
        super.onDestroy();
    }
}

用法如下:

        findViewById(R.id.btn_start).setOnClickListener(v -> {
    
    
            ActivityResultFragment.launchActivityResult(getSupportFragmentManager(),
                    this, new Intent(MainActivity.this, SecondActivity.class));
        });

如上即可在非 onCreate 、 onStart 或者 onRestart 之间调用 registerForActivityResult 方法。当然ActivityResultLauncher 的 launch 方法是可以正常在点击事件中调用的,如果嫌麻烦直接用上述最简单的方式进行调用:registerForActivityResult 正确的调用方式

猜你喜欢

转载自blog.csdn.net/CJohn1994/article/details/125153006