“小云朵像棉花糖,长颈鹿嫌自己脖子不够长。”
Activity Result API 前世今生
前言
起因是看到以前的代码中,ComponentActivity中的startActivityForResult()
和onActivityResult()
被弃用了(但是startActivity()
没有被弃用),然后点进去一看,发现他们在androidx
的activity:1.2.0-alpha02
和fragment: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 Code和Result 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()
,需要满足以下条件:
- Fragment 应直接调用
startActivityForResult()
,而不是调用getActivity().startActivityForResult()
- 如果 Activity 有自己的
onActivityResult()
,那么其中要加上super.onActivityResult()
(可以在方法最开始,也可以在最后)。
缺点
和大部分被弃用的方法不同,Old API 的方法并没有功能上的问题,它并不是因为有线程安全、内存泄漏等隐患而被弃用的,更多的是因为——随着应用的扩展,onActivityResult()
会陷入各种嵌套,耦合严重且难以维护。
也就是说,Old API 可以用,但是用起来很难受(除非程序的体量比较小,功能比较简洁)。
一个很直观的表现就是我们需要维护越来越多的常量标识,除了Intent
本身putExtra
和getExtra
所用到的标识外,不同的 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,这个方法接收三个参数:
ActivityResultContract<I, O> contract
:Contract,称为协议或约束,用于规定输入类型(I)和输出类型(O),其内部通过构造 Intent 实现页面之间的跳转。ActivityResultRegistry registry
:Registry,协议注册器,通常在 Activity / Fragment 以外的地方接收回传的Result
时使用。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/*"
,则会打开视频的目录等。当然还有可以传入“*/*”
,表示打开所有的目录。因此,上传头像等功能就能用它来帮助实现啦。
GetContent
、OpenMultipleDocuments
、OpenDocumentTree
等也是相似的操作流程。
由于运行时请求权限的场景比较多,所以来看看相关的 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
。实际上,最终还是通过Registry的register()
来获取的Launcher。
mActivityResultRegistry
是ActivityResultRegistry的一个实例对象,仅实现了其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 进行包装
大部分情况下我们都只在Activity和Fragment中会用到Result API
,我们可以通过registerForActivityResult()
直接生成一个Launcher,因为ConponentActivity
和Fragment
都实现了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 aLifecycleOwner
, as theLifecycleOwner
automatically removes your registered launcher when theLifecycle
is destroyed. However, in cases where aLifecycleOwner
is not available, eachActivityResultLauncher
class allows you to manually callunregister()
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 也有着一定的学习成本,毕竟它有着Launcher、Contract、Registry等组件,是以往不曾接触的。但是简单的使用还是比较简单的,而且掌握后其带来的多功能、高扩展也挺让人受益的