Activity Result API 前世今生

startActivityForResult()和onActivityResult()的上位替代

Posted by BlackDn on January 6, 2023

“小云朵像棉花糖,长颈鹿嫌自己脖子不够长。”

Activity Result API 前世今生

前言

起因是看到以前的代码中,ComponentActivity中的startActivityForResult()onActivityResult()被弃用了(但是startActivity()没有被弃用),然后点进去一看,发现他们在androidxactivity:1.2.0-alpha02fragment:1.3.0-alpha02中被弃用(在 appcompat 库中则是 1.3.0 被弃用)。并且官方推荐了Activity Result API作为替代方法。
正好看到社团的小朋友也写了一篇,就顺便拿别人的砖抛自己的砖啦~

Old API:startActivityForResult() 和 onActivityResult()

在探究为什么弃用之前,我们先来回顾一下他们的使用方法吧。
因为弃用的两个方法名字有点长,所以我们统称Old API好了

基本用法

startActivityForResult()startActivity()的功能一样, 都是通过启动Intent来进行页面跳转,不同点在于它的第二个参数接收了一个Request Code,用来表示启动的这个 Intent。
onActivityResult()则可以通过Request CodeResult Code(通常在新的 Activity 中,在finish()前通过setResult()设置)来对某次跳转或某种结果进行特定的后续操作。

//in MainActivity
//第一个Activity中,启动intent进行页面跳转
startActivityForResult(intent, RequestCode);

//回到初始页面处理结果
onActivityResult(requestCode, resultCode, intent) {
  if (resultCode == RESULT_OK) {  //通过resultCode判断是否进行后续操作
    switch (requestCode) {  //通过requestCode针对跳转不同进行不同的后续操作
      case 1:
		//do something
      case 2:
		//do something
    }
  }
}

startActivityForResult()调用的时候,我们会跳到第二个 Activity,在其中操作结束后,利用setResult()设置要传回的Result Code和数据(包裹在Intent里),最后finish()结束第二个 Activity 并回到第一个 Activity:

//in SecondActivity
//第二个Activity中,点击按钮表示结束,传回Result Code和数据
public void onClick(View view) {
	Intent goBackIntent = new Intent(SecondActivity.this, MainActivity.class);
	setResult(RESULT_OK, goBackIntent);
	finish();
}

回到第一个 Activity 后,就会自动调用onActivityResult()方法,以此对回传的数据进行处理,并进行后续操作。(这里的RESULT_OK是 Android 自带的)

可以看出Old API实际上是想模仿一个前后端的交互的过程,Request Code用来区分请求来源,而Result Code就好比后端返回的状态码。

小坑点

项目中偶遇一个 Fragment 和关联它的 Activity 都是实现了onActivityResult(),而当我们在 Fragment 中调用startActivityForResult()之后,理所应当地想要在 Fragment 中的onActivityResult()处理回传结果。但是很遗憾,Fragment 中的onActivityResult()没能接收到结果,反而是给到了 Activity 的onActivityResult()

在 Fragment 和 Activity 同时实现了onActivityResult()的情况下,如果想要从 Fragment 的startActivityForResult()出发的请求回到 Fragment 的onActivityResult(),需要满足以下条件:

  1. Fragment 应直接调用startActivityForResult(),而不是调用getActivity().startActivityForResult()
  2. 如果 Activity 有自己的onActivityResult(),那么其中要加上super.onActivityResult()(可以在方法最开始,也可以在最后)。

缺点

和大部分被弃用的方法不同,Old API 的方法并没有功能上的问题,它并不是因为有线程安全、内存泄漏等隐患而被弃用的,更多的是因为——随着应用的扩展,onActivityResult()会陷入各种嵌套,耦合严重且难以维护
也就是说,Old API 可以用,但是用起来很难受(除非程序的体量比较小,功能比较简洁)。

一个很直观的表现就是我们需要维护越来越多的常量标识,除了Intent本身putExtragetExtra所用到的标识外,不同的 Activity 还要有不同的Request Code;目标 Activity 则需要根据处理结果的不同,返回不同的Result Code。在某些场景下,一个 Activiy 不同的功能还得持有不同的Request Code
而大量的常量标识,尤其是Request Code,使得onActivityResult()不得不使用更多的if-else或者switch来进行区分,让其越来越臃肿,更别说那些还得针对不同Result Code进行处理的情况了。
于是,Activity Result API出现了。

Activity Result API

基本用法

Activity Result API 主要在androidx.activity.result这个包中
它提供了一个启动类ActivityResultLauncher,我们可以通过调用registerForActivityResult()来获得一个Launcher,这个方法接收三个参数:

  1. ActivityResultContract<I, O> contract:Contract,称为协议约束,用于规定输入类型(I)和输出类型(O),其内部通过构造 Intent 实现页面之间的跳转。
  2. ActivityResultRegistry registry:Registry,协议注册器,通常在 Activity / Fragment 以外的地方接收回传的Result时使用。
  3. ActivityResultCallback<O> callback:回调方法,收到Result后进行后续操作,相当于 Old API 中的onActivityResult()方法

我们先看比较简单的情况,大部分情况下我们是在 Activity / Fragment 之间进行交互时使用 Activity Result API,因此不需要传入 Registry(registerForActivityResult()有一个只用传入Contract回调方法的重载);而 Android 本身已经有了一些预定义的协议,所以简单的调用如下:

//in MainActivity
private ActivityResultLauncher<Intent> toSecondActivityLauncher = registerForActivityResult(
  new StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
	@Override
	public void onActivityResult(ActivityResult result) {
	  if (result.getResultCode() == RESULT_OK) {
		Intent resultIntent = result.getData();
		//todo things...
	  }
	}
  });
//......
//点击事件触发跳转
public void onClick(View view) {
	Intent intent = new Intent(MainActivity.this, SecondActivity.class);
	toSecondActivityLauncher.launch(intent);
}

我们通过Launcher.launch()来启动 Intent 并实现跳转,当我们跳转到 SecondActivity 后,其中不需要任何改动——仍是通过setResult(resultCode, intent)finish()回到 FirstActivity(MainActivity)。

这里我们使用了StartActivityForResult这个预定义的约束,它规定我们的输入类型是Intent,输出类型是ActivityResult。因此我们调用Launcher.launch()的时候传入的就是Intent;而回调接口的范型和回调方法的参数则是ActivityResult,表示输出回来的结果,拿到这个结果后我们就可以在回调方法中进一步处理。
当然,这里作为输出类型的ActivityResult也是 Android 提供给我们的,主要就是通过result.getResultCode()来获取resultCode,通过result.getData()来获取Intent(来自 setResult(resultCode, intent))。(ActivityResult只有这两个属性)

回调函数用的是也自带的ActivityResultCallback,其通过onActivityResult()进行结果的处理(虽然这个方法和 Old API 的方法重名,实际上是不同的方法)。
更多时候,我们喜欢用lambda来实现回调方法:

//in MainActivity
  private ActivityResultLauncher<Intent> toSecondActivityLauncher = registerForActivityResult(
      new StartActivityForResult(), result -> {
        if (result.getResultCode() == RESULT_OK) {
          Intent resultIntent = result.getData();
          //todo things...
        }
      });

因为参数和范型有点多,所以这里小小总结一下:起决定作用的还是约束ActivityResultContract<I, O>,我们用其中的<I, O>分别表示输入类型输出类型

  • 输入类型I:决定了ActivityResultLauncher<I>的范型,以及Launcher.launch(I)的参数类型;
  • 输出类型O:决定了ActivityResultCallback<O>的范型,以及回调函数onActivityResult(O)的参数

Contract 约束

预定义的 Contract

之前提到,StartActivityForResult是一个预定义的Contract,当然除了它之外,还有许多其他给定的 Contract 供我们使用,这里列一些比较常见的:

Contract 功能
StartActivityForResult<Intent, ActivityResult> 多用于 App 內 Activity 的跳转
RequestPermission<String, Boolean> 用于请求单个权限
RequestMultiplePermissions<String[], Map<String, Boolean>> 用于请求一组权限
TakePicturePreview<Void, Bitmap> 拍照,返回 Bitmap 图片
TakePicture<Uri, Boolean> 拍照,保存至 Uri 处,保存成功返回 true
TakeVideo<Uri, Boolean> 拍摄视频,保存至 Uri 处,返回一张缩略图
PickContact<Void, Uri> 从通讯录获取联系人
GetContent<String, Uri> 选择内容,返回其 Uri 地址
CreateDocument<String, Uri> 创建一个文档,返回其 Uri
OpenDocument<String[], Uri> 选择一个文档,返回其 Uri
OpenMultipleDocuments<String[], List<Uri>> 选择多个文档,返回它们 Uri 的 List
OpenDocumentTree<Uri, Uri> 选择一个目录,返回其 Uri

当看到 OpenDocument<String[], Uri>的时候我还有些疑问,为啥打开一个文件要传入String数组。事实上,当启动这个 Contract 之后,会打开类似文件管理器的页面,然后让我们选择文件:

//In MainActivity
private ActivityResultLauncher<String[]> openDocumentLauncher = registerForActivityResult(new OpenMultipleDocuments(), result -> {
	Log.d("TAG", "uri: " + result.getPath());
	//选择一张图片后输出:/document/image:15649
});

openDocumentLauncher.launch(new String[]{"image/*", "video/*"});

可以看到,如果 String 数组中有"image/*",则会打开图片的目录,可以选择其中的图片文件;如果带上"video/*",则会打开视频的目录等。当然还有可以传入“*/*”,表示打开所有的目录。因此,上传头像等功能就能用它来帮助实现啦。
GetContentOpenMultipleDocumentsOpenDocumentTree等也是相似的操作流程。

由于运行时请求权限的场景比较多,所以来看看相关的 Contract:
返回结果代表着权限申请的成功与否,因此我们可以根据结果来判断是继续申请/退出,还是进一步执行操作。

private ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(new RequestPermission(), result -> {
  String resultMessage = result ? "Permission grated." : "Permission denied.";
  Log.d("Callback:", resultMessage);
});

private ActivityResultLauncher<String[]> requestMultiPermissionsLauncher = registerForActivityResult(new RequestMultiplePermissions(), result -> {
  for (Map.Entry<String, Boolean> entry : result.entrySet()) {
    Log.d("Callback:", "Request Permission of " + entry.getKey() + " " + entry.getValue());
  }
});
//申请权限:
requestPermissionLauncher.launch(permission.ACCESS_FINE_LOCATION);
//输出:true

requestMultiPermissionsLauncher.launch(new String[] {
    permission.ACCESS_FINE_LOCATION,
    permission.BLUETOOTH,
    permission.NFC});
//输出:
//Request Permission of android.permission.ACCESS_FINE_LOCATION true
//Request Permission of android.permission.BLUETOOTH true
//Request Permission of android.permission.NFC true

其实用到最多的还是StartActivityForResult和几个请求权限相关的 Contract,而其他的 Contract 都是在和其他 App(系统 App)交互的时候才用到,使用场景比较受限(打开相机、通讯录、文件管理器啥的)。
如果没有额外的需求,这些预定义的 Contract 完全够我们使用的,而其中的实现过程对我们来说是透明的,不需要关心,因此,整个操作流程就变的更加方便和简洁。

Contract 的内部操作

Contract既然规定了输入类型和输出类型,那么它内部应该是进行了一系列操作来进行转换的。我们就更进一步,看看其内部进行了哪些操作。(Result API中的Contract其实是由Kotlin实现的,Contract 约束的概念也是在 Kotlin 中引入的,我这里 Decompile 成 Java 展示)

以预定义的StartActivityForResult为例,我们看看其内部是如何实现的:

public static final class StartActivityForResult extends ActivityResultContract<Intent, ActivityResult> {
    @Override
    public Intent createIntent(Context context, Intent input) {
        return input;
    }
    @Override
    public ActivityResult parseResult(int resultCode, Intent intent) {
        return new ActivityResult(resultCode, intent);
    }
}

可以看到,StartActivityForResult是抽象类ActivityResultContract的一个终类(final class)。事实上,所有预定义的 Contract 都是它的终类,区别就是输入类型和输出类型的范型不同。我们先瞟一眼ActivityResultContract这个大家的抽象父类:

public abstract class ActivityResultContract<I, O> {
	public abstract Intent createIntent(Context context, I input);
	public abstract O parseResult(int resultCode, Intent intent);
	//...
}

可以看到,主要方法就是接收输入的createIntent()和返回输出的parseResult()两个方法。
再回来看StartActivityForResult,当我们调用launch()的时候,createIntent()会被执行,其生成的Intent会被启动从而实现页面的跳转。
当页面返回,则会调用parseResult()接收来自第二个 Activity 回传的数据(setResult(resultCode, intent)),并将其转变为输出类型 O,在这里就是ActivityResult。最后我们在外面,通过ActivityResultCallback来接收parseResult()的结果,进行后续处理。

也就是说,从launch()开始算起,我们的数据流向大致是这样的: [FirstActivity]: launch() -> [Contract]createIntent() -> [SecondActivity] -> setResult() -> [Contract]parseResult() -> [FirstActivity]: callback()

Contract成为了两个Activity之间信息传递的桥梁。

自定义的 Contract

既然有ActivityResultContract是个抽象类,那么当然,只要继承它,我们就可以自定义 Contract,创造自己的Custom Contract了——只要实现createIntent()parseResult()这两个抽象方法就好了嘛。

class CustomContract extends ActivityResultContract<String, String> {
  @Override
  public Intent createIntent(@NonNull Context context, String s) {
    Intent intent = new Intent(context, SecondActivity.class);
    intent.putExtra("createIntentStringKey", s);
    return intent;
  }
  @Override
  public String parseResult(int i, @Nullable Intent intent) {
    String parseResultStringKey = intent.getStringExtra("parseResultStringKey");
    if (parseResultStringKey != null) {
      return parseResultStringKey;
    } else {
      return "no string in result";
    }
  }
}

这里我们定义了输入(I)和输出(O)都是 String的 Contract。它将携带输入的 String 内容,并跳转到SecondActivity。而当我们从SecondActivity返回的时候,则会将返回Intent中的 String 提取出来。
接下来我们就可以在MainActivity中利用它来进行跳转了:

//in MainActivity
private ActivityResultLauncher<String> mLauncher = registerForActivityResult(new CustomContract(), result -> {
  Log.d("Callback", result);
});
//点击启动
public void onClick(View view) {
  mLauncher.launch("hello");
}

而在SecondActivity中,我们可以通过之前设定的Key"createIntentStringKey")来获取Intent中的 String 内容;同时给返回的Intent带上相应Key"parseResultStringKey")的 String,以便Contract获取到我们返回的内容:

//in SecondActivity
	String message = getIntent().getStringExtra("createIntentStringKey");
	Log.e("SecondActivity: ", message);
//点击返回MainActivity
public void onClick(View view) {
	Intent goBackIntent = new Intent();
	goBackIntent.putExtra("parseResultStringKey", "world");
	setResult(RESULT_OK, goBackIntent);
	finish();
}

我们从MainActivity跳转到SecondActivity,获取到launch()传来的"hello",Log 输出;然后回到MainActivity,触发回调,Log 输出setResult()传来的"world"(其实传回的是Intent,不过我们的Contract“从中作梗”,已经将其加工成 String 给我们了):

D/SecondActivity: hello
D/Callback: world

注册器 ActivityResultRegistry

我们发现,在之前的使用过程中,我们并没有接触到注册器ActivityResultRegistry,那么它到底是怎么工作的呢?
我们第一次提到Registry,是说它作为registerForActivityResult()的一个参数,那么我们就从这个方法入手:

//in ComponentActivity.java
//两个参数的重载
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
        @NonNull ActivityResultContract<I, O> contract,
        @NonNull ActivityResultCallback<O> callback) {
    return registerForActivityResult(contract, mActivityResultRegistry, callback);
}

//真正的调用
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
        @NonNull final ActivityResultContract<I, O> contract,
        @NonNull final ActivityResultRegistry registry,
        @NonNull final ActivityResultCallback<O> callback) {
    return registry.register(
            "activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
}

我们看到,我们最常使用的两个参数的重载,并非是没用到Registy,而是因为Activity中自带了一个mActivityResultRegistry。实际上,最终还是通过Registryregister()来获取的Launcher
mActivityResultRegistryActivityResultRegistry的一个实例对象,仅实现了其onLaunch()的抽象方法。而在ActivityResultRegistry中,比较重要的方法有以下几个onLaunch()register()dispatchResult()

  • onLaunch()是一个抽象方法,而mActivityResultRegistry就实现了它,内部进行了一些权限获取,最终通过ActivityCompat.startActivityForResult()来启动 Intent。(代码有点长就不贴了)
//in ActivityResultRegistry.java
public abstract <I, O> void onLaunch(
	int requestCode,
	ActivityResultContract<I, O> contract,
	I input,
	ActivityOptionsCompat options);
  • register()是一个 final 方法,它主要进行了 Lifecycle 的一系列操作,利用 LifecycleContainer 存储一些数据变量啥的,最后返回一个ActivityResultLauncher。它本身也是一个抽象类,这里在 return 的时候顺便实现了它的一些抽象方法
//in ActivityResultRegistry.java
public final <I, O> ActivityResultLauncher<I> register(
	final String key,
	final LifecycleOwner lifecycleOwner,
	final ActivityResultContract<I, O> contract,
	final ActivityResultCallback<O> callback) {

	Lifecycle lifecycle = lifecycleOwner.getLifecycle();
	//...
	return new ActivityResultLauncher<I>() {
		@Override
		public void launch(I input, ActivityOptionsCompat options) {
			Integer innerCode = mKeyToRc.get(key);
			if (innerCode == null) {
			//throw Exception
			mLaunchedKeys.add(key);
			try {
				onLaunch(innerCode, contract, input, options);  //1
			} catch (Exception e) { //throw Exception }
		}
		@Override
		public void unregister() {
			ActivityResultRegistry.this.unregister(key);  //2
		}
		@Override
		public ActivityResultContract<I, ?> getContract() {
			return contract;
		}
	};
}

可以看到,这里为ActivityResultLauncher实现的launch()最终调用的是ActivityResultRegistry自己的抽象方法onlaunch()(注释 1),而这个抽象的onlaunch()的实现则是由ComponentActivity中的mActivityResultRegistry实现(默认情况下);而unregister()实际上也是ActivityResultRegistry自己的unregister()(注释 2)。
也就是说,对于一个ActivityResultLauncher来说,它的unregister()是在ActivityResultRegistry中实现的,而他的launch()则是由Activity实现的。
感觉回调了好多层,我已经开始有些头晕了=x=

至于dispatchResult(),他们的代码有点长,我删删减减了一些:

//in ActivityResultRegistry.java
public final boolean dispatchResult(int requestCode, int resultCode, Intent data) {
    String key = mRcToKey.get(requestCode);
    if (key == null) { return false; }
    doDispatch(key, resultCode, data, mKeyToCallback.get(key));
    return true;
}

public final <O> boolean dispatchResult(int requestCode, O result) {
    String key = mRcToKey.get(requestCode);
    if (key == null) { return false; }
	//...
	ActivityResultCallback<O> callback =
			(ActivityResultCallback<O>) callbackAndContract.mCallback;
	if (mLaunchedKeys.remove(key)) {
		callback.onActivityResult(result);
	}
    return true;
}

private <O> void doDispatch(String key, int resultCode, Intent data, CallbackAndContract<O> callbackAndContract) {
	//...
	ActivityResultCallback<O> callback = callbackAndContract.mCallback;
	ActivityResultContract<?, O> contract = callbackAndContract.mContract;
	callback.onActivityResult(contract.parseResult(resultCode, data));
	mLaunchedKeys.remove(key);
    //...
}

总的来说,最后都是通过callback.onActivityResult()来将结果通过回调接口传给外部,这里的回调接口(CallbackAndContract.mCallback)就是我们的ActivityResultCallback。因此外部(包括非 Activity/Fragment)就可以通过dispatchResult()来获取回传的结果。

实际上,在弃用的ComponentActivity.onActivityResult()中就有dispatchResult(),用于拦截返回结果,将结果分发给ActivityResultRegistry进行处理。如果拦截失败则交给onActivityResult()继续传递。

//in ComponentActivity
@Deprecated
@CallSuper
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (!this.mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
    super.onActivityResult(requestCode, resultCode, data);
  }
}

不过在大多数情况下我们是不用自己实现Registry的,自带的mActivityResultRegistry能满足基本需求哒。

用生命周期组件 Lifecycle 进行包装

大部分情况下我们都只在ActivityFragment中会用到Result API,我们可以通过registerForActivityResult()直接生成一个Launcher,因为ConponentActivityFragment都实现了ActivityResultCaller接口,重写了这个方法。
而在一些特殊情况中,我们需要在非 Activity/Fragment的位置接收 Activity 回传的数据,这时候就要用到注册器ActivityResultRegistry

我们可以新建一个生命周期组件,实现DefaultLifecycleObserver,它分别持有注册器ActivityResultRegistry和启动器ActivityResultLauncher,利用注册器来生成启动器。
其实也可以直接声明一个ActivityResultRegistry实例,但是我们更习惯将其用LifecycleObserver进行一个包装。原因是当我们成功注册一个 Launcher 后,为了保证资源释放,需要在最后调用launcher.unregister()来将其释放。
不过由于 Activity 和 Fragment 都有自己的生命周期,其LifecycleOwner会在onDestroy()中自动释放 Launcher,不用我们操心。但是我们使用注册器的前提是“可能在非 Activity/Fragment的位置调用 Launcher”,这些位置不一定有自己的 Lifecycle,因此,为了避免每次手动调用unregister(),我们用生命周期组件将其包装,以实现 Launcher 的自动释放。

When using the ActivityResultRegistry APIs, it’s strongly recommended to use the APIs that take a LifecycleOwner, as the LifecycleOwner automatically removes your registered launcher when the Lifecycle is destroyed. However, in cases where a LifecycleOwner is not available, each ActivityResultLauncher class allows you to manually call unregister() as an alternative.

具体的实现如下:

class MyLifecycleObserver implements DefaultLifecycleObserver {
  private final ActivityResultRegistry mRegistry;
  private ActivityResultLauncher<String> mLauncher;

  MyLifecycleObserver(ActivityResultRegistry registry) {
    mRegistry = registry;
  }

  public void onCreate(LifecycleOwner owner) {
    mLauncher = mRegistry.register("key", owner, new CustomContract(), result -> {
      Log.d("callback", "Lifecycle onCreate: " + result);
    });
  }

  public void startLauncher(String inputString) {
    mLauncher.launch(inputString);
  }
}

其中用到的 Contract 是我们自己自定义的CustomContract,接收一个 String 作为launch()参数,返回一个 String 作为回调接收的内容。
接下来我们就可以在MainActivity中实例化这个MyLifecycleObserver,进而使用其中的 Launcher 了,以此来代替之前通过registerForActivityResult()获取 Launcher 的方式。

//In Other Class
private MyLifecycleObserver mObserver;
//onCreate中实例化Observer并绑定
@Override
protected void onCreate(Bundle savedInstanceState) {
	//...
	mObserver = new MyLifecycleObserver(getActivityResultRegistry());
	getLifecycle().addObserver(mObserver);
}
//通过mObserver来启动Launcher
mObserver.startLauncher("hello");

当我们用生命周期组件 Lifecycle 进行包装后,即使在其他的一些类中,我们也能轻松启动 Launcher,而不用关心自己应该在什么时候将其释放。

小结 - 两者对比

有一个很直观的一点就是,使用 Result API 之后,我们不再需要Request Code了。
在 Old API 中,我们需要 RequestCode 来告诉onActivityResult()我们的请求是来自哪里的;而在 Result API 中,我们采用了Launcher-Contract-Callback来进行请求和结果处理。每个 Launcher 都有自己的 Callback,相当于每个请求都有属于自己的onActivityResult(),不再需要统一处理。
我们看到 Registry 的代码中有很多 key 的出现,这个 key 就是用来生成每个 Launcher 的唯一识别码的(因此我们能用同一个 Registry 生成多个 Launcher)。相当于Request Code被偷偷藏在内部,对外部透明了,我们就不需要考虑它啦。

这也是 Result API 最为直观的优点,取消了Request Code,则就没有了越来越多的常量 Flag;没有了onActivityResult(),则就没有了越来越多的耦合和嵌套。 (不过我们会有越来越多的 Launcher 和 Callback,所以代码总量还是基本不变的)

此外,相比于 Old API,Result API 有着更加广泛的功能——至少我们能用它来进行运行时的权限请求了,而这也得益于 Contract 的引入。
不仅如此,支持自定义的 Contract 也让我们有了更好的扩展性,可以针对自己的需求来自创 Contract,让请求的发送和结果的处理更加流畅。

话虽如此,Result API 也有着一定的学习成本,毕竟它有着LauncherContractRegistry等组件,是以往不曾接触的。但是简单的使用还是比较简单的,而且掌握后其带来的多功能、高扩展也挺让人受益的

参考

  1. developers:Getting a result from an activity
  2. ActivityResultContract
  3. 谈谈 Fragment 中的 onActivityResult
  4. 再见!onActivityResult!你好,Activity Results API!
  5. 搞懂 Activity Result API (一)
  6. Activity Result API 详解,是时候放弃 startActivityForResult 了
  7. startActivityForResult() 被弃用,来试试 Activity Result API