前言
长久以来,我们开启一个Activity 并获取其返回值一直使用的是startActivityForResult
和onActivityResult
这两个方法。然而,从Activity 1.2.0-alpha02
和Fragment 1.3.0-alpha02
开始, 这两个从学Android 开始就存在的函数被Google 标记为了废弃,取而代之的是新的 Activity Result API
。为什么Google 要重新设计这个历史悠久的API 呢?本文尝试通过比较这两种写法从而得出结论。
传统onActivityResult 写法
这个只要是一个Android 开发者肯定再熟悉不过了,固定套路走一波。
class MainActivity : AppCompatActivity(R.layout.activity_main) {
companion object {
const val REQUEST_CODE = 909
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fab.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra(EXTRA_DATA, "data")
startActivityForResult(intent, REQUEST_CODE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE) {
val text = data?.getStringExtra(EXTRA_DATA_BACK) ?: ""
Log.e("MainActivity", "data back is ==>$text")
}
}
}
总结一下这种写法,主要是:
- 定义
requestCode
常量 - 调用
startActivityForResult
开启另外一个Activity
- 在
onActivityResult
中根据requestCode
和resultCode
来获取返回的数据并处理
看起来除了会写一些样板代码之外,似乎并没有什么不妥,并且长期以来我们也习惯了这种写法。
Activity Result API 写法
首先我们需要在在 gradle 文件中引入依赖:
// activity
implementation("androidx.activity:activity-ktx:1.2.0")
// fragment
implementation("androidx.fragment:fragment-ktx:1.3.0")
然后就可以编写对应的代码:
private val getContent =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val text = it?.data?.getStringExtra(EXTRA_DATA_BACK) ?: ""
Log.e("MainActivity", "query data by activity result API, and data is ==>$text")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fab.setOnLongClickListener {
getContent.launch(Intent(this, SecondActivity::class.java))
true
}
}
看起来似乎是简单了不少,但是并不清楚上面具体是什么意思。我们逐个来分析。
首先是registerForActivityResult
这个函数,它接收两个参数,一个是ActivityResultContract
,一个是ActivityResultCallback
,同时返回一个ActivityResultLauncher
对象。
@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback) {
return registerForActivityResult(contract, mActivityResultRegistry, callback);
}
ActivityResultContract
它是一个抽象类,我们可以将其理解为一种约定好的协议,抽象出createIntent
和parseResult
这两种能力,分别负责提供启动Activity
需要的Intent
和解析返回回来的数据。
我们传入的StartActivityForResult
就是它的子类。
public static final class StartActivityForResult
extends ActivityResultContract<Intent, ActivityResult> {
@NonNull
@Override
public Intent createIntent(@NonNull Context context, @NonNull Intent input) {
return input;
}
@NonNull
@Override
public ActivityResult parseResult(
int resultCode, @Nullable Intent intent) {
// ActivityResult 封装了返回的resultCode 和数据
return new ActivityResult(resultCode, intent);
}
}
StartActivityForResult
是ActivityResultContracts
的内部类,我们从后者的命名看出(使用复数形式),它包含了不止一种协议(ActivityResultContract
)实现,还提供了诸如权限请求、拍照、拍视频、打开文件等默认实现。
ActivityResultCallback
从其命名就可以看出是最后拿到的回调处理,这里的范型参数O 对应了具体的协议输出类型,也就是上面parseResult
返回的参数,如StartActivityForResult
返回的就是ActivityResult
。
public interface ActivityResultCallback<O> {
/**
* Called when result is available
*/
void onActivityResult(@SuppressLint("UnknownNullness") O result);
}
registerForActivityResult
有了上面两个函数的基础,我们再回到registerForActivityResult
,它最终会调用ActivityResultRegistry
类中的register
函数。在这个函数中,生成了对应的requestCode
并将其缓存起来,同时根据当前控制器的lifecycle
添加或移除对应的key
,保证了生命周期的安全。
@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();
// ...省略代码
final int requestCode = registerKey(key);
mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract));
final ActivityResult pendingResult = mPendingResults.getParcelable(key);
LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
if (lifecycleContainer == null) {
lifecycleContainer = new LifecycleContainer(lifecycle);
}
if (pendingResult != null) {
mPendingResults.remove(key);
LifecycleEventObserver observer = new LifecycleEventObserver() {
@Override
public void onStateChanged(
@NonNull LifecycleOwner lifecycleOwner,
@NonNull Lifecycle.Event event) {
if (Lifecycle.Event.ON_START.equals(event)) {
callback.onActivityResult(contract.parseResult(
pendingResult.getResultCode(),
pendingResult.getData()));
}
}
};
lifecycleContainer.addObserver(observer);
mKeyToLifecycleContainers.put(key, lifecycleContainer);
}
LifecycleEventObserver observer = new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner,
@NonNull Lifecycle.Event event) {
if (Lifecycle.Event.ON_DESTROY.equals(event)) {
unregister(key);
}
}
};
lifecycleContainer.addObserver(observer);
return new ActivityResultLauncher<I>() {
@Override
public void launch(I input, @Nullable ActivityOptionsCompat options) {
onLaunch(requestCode, contract, input, options);
}
@Override
public void unregister() {
ActivityResultRegistry.this.unregister(key);
}
@NonNull
@Override
public ActivityResultContract<I, ?> getContract() {
return contract;
}
};
}
ActivityResultLauncher
它是一个抽象类,最核心的抽象方法是launch
函数,负责去执行既定的各种contract
,我们在外部使用的时候,就是直接调用它的launch
函数。
前面协议和返回值处理的声明一系列操作实际上是在ActivityResultRegistry
这个类中进行了注册,这货也是一个抽象类,有一个重要的抽象方法onLaunch
,可以看到包含了之前声明的所有信息。
/**
* Start the process of executing an {@link ActivityResultContract} in a type-safe way,
* using the provided {@link ActivityResultContract contract}.
*
* @param requestCode request code to use
* @param contract contract to use for type conversions
* @param input input required to execute an ActivityResultContract.
* @param options Additional options for how the Activity should be started.
*/
@MainThread
public abstract <I, O> void onLaunch(
int requestCode,
@NonNull ActivityResultContract<I, O> contract,
@SuppressLint("UnknownNullness") I input,
@Nullable ActivityOptionsCompat options);
当注册完毕之后,ActivityResultRegistry
每次都会返回一个ActivityResultLauncher
匿名实现,在其launch
函数中调用onLaunch
函数。同时,Activity
的父类ComponentActivity
持有了ActivityResultRegistry
的实现,所以,最终执行的动作又回到了Activity
中来。
private ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry() {
@Override
public <I, O> void onLaunch(
final int requestCode,
@NonNull ActivityResultContract<I, O> contract,
I input,
@Nullable ActivityOptionsCompat options) {
ComponentActivity activity = ComponentActivity.this;
// ...省略代码
// Start activity path
Intent intent = contract.createIntent(activity, input);
Bundle optionsBundle = null;
if (intent.hasExtra(EXTRA_ACTIVITY_OPTIONS_BUNDLE)) {
optionsBundle = intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS_BUNDLE);
intent.removeExtra(EXTRA_ACTIVITY_OPTIONS_BUNDLE);
} else if (options != null) {
optionsBundle = options.toBundle();
}
if (ACTION_REQUEST_PERMISSIONS.equals(intent.getAction())) {
// ...省略代码
} else if (ACTION_INTENT_SENDER_REQUEST.equals(intent.getAction())) {
// ...省略代码
} else {
// startActivityForResult path
ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
}
}
};
可以看到,之前的createIntent
被调用,同时根据不同的协议使用不同的执行方式。值得注意的是,startActivityForResult
虽然在上层被标记为了deprecated,但是实际上底层调用的还是它。
可扩展性
上文说到,ActivityResultContracts
类中包含了所有官方提供的ActivityResultContract
默认实现,开发者也可以根据实际需求自己实现协议,仅需传入对应的范型参数和实现方法即可。比如这个官方提供的示例:
class PickRingtone : ActivityResultContract<Int, Uri?>() {
override fun createIntent(context: Context, ringtoneType: Int) =
Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringtoneType)
}
override fun parseResult(resultCode: Int, result: Intent?) : Uri? {
if (resultCode != Activity.RESULT_OK) {
return null
}
return result?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
}
}
总结
通过上文的分析,梳理了Activity Result API
的调用流程,可以发现它的至少三个优点:
-
减少了样板代码。开发者不必在每一个使用
startActivityForResult
的地方定义requestCode,重写onActivityResult
函数。 -
与视图控制器解耦。之前使用
onActivityResult
的时候,必定和Activity
或者fragment
绑定,使用Activity Result API
后,就可以将返回值处理的逻辑单独剥离处理出来。具体怎么做呢?我们从上文可以看出,
Activity Result API
的核心是ActivityResultRegistry
,在ComponentActivity
中持有了它,并且将其暴露出来供外部使用,这就给了开发者很多想象空间。比如外部可以直接拿到ActivityResultRegistry
对象进行register 操作,代码上与Activity
或者Fragment
上分开,从而减少与视图控制器的耦合。class MyLifecycleObserver(private val registry : ActivityResultRegistry) : DefaultLifecycleObserver { lateinit var getContent : ActivityResultLauncher<String> fun onCreate(owner: LifecycleOwner) { getContent = registry.register("key", owner, GetContent()) { uri -> // Handle the returned Uri } } fun selectImage() { getContent("image/*") } }
-
生命周期安全。由于在
ActivityResultRegistry
注册的时候使用了Lifecycle
进行了绑定和解绑操作,所以不会出现收到返回结果时Activity
被销毁了的情况。
总的来说,使用新的Activity Result API
能够使得代码更灵活,一定程度上减少开发者的重复工作,同时使用起来起来也很简单,值得上手。
同时我们通过可以学习Google 大佬的设计思路,如何合理的使用设计模式,将问题拆分解耦,抽象出更高层的模块等。之前的API 通过使用Intent
添加不同的flag 来跳转页面、拍照等,虽然简单,但是太散,第一眼根本不容易看出它具体是干啥的,而使用新的API 之后,通过使用对应的协议(虽然底层实现还是一样的),但是见名知意,仅须维护ActivityResultContracts
的协议即可,符合了高内聚,低耦合的设计原则,值得我们学习。
最后,Enjoy!
注:以上源码分析基于activity 1.2.0-beta01
。