本文已同步发表于我的微信公众号,搜索
代码说
即可关注,欢迎与我沟通交流。
文章目录
- startActivityForResult()被标记为过时
- registerForActivityResult替代方案
- 使用示例
- ActivityResultContract 场景
- 自定义ActivityResultContract
- 源码浅析
- registerForActivityResult()
- ActivityResultLauncher#launch()
- 总结
- 资料
startActivityForResult()被标记为过时
一般我们声明的Activity
都会继承自 AppCompatActivity
(需要引入androidx.appcompat:appcompat库
),而AppCompatActivity -> FragmentActivity -> ComponentActivity (->表示继承)
。
当我们需要跳转到另一个Activity
并需要拿到返回结果时,可以使用startActivityForResult()
来实现,然而随着相应库的提高,突然有一天,你会发现startActivityForResult()
被标记成过时方法了,如下:
点进去看是 ComponentActivity#startActivityForResult()
及onActivityResult()
被标记为过时:
/**
* {@inheritDoc}
*
* @deprecated use
* {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
* passing in a {@link StartActivityForResult} object for the {@link ActivityResultContract}.
*/
@Override
@Deprecated //被标记为过时
public void startActivityForResult(@SuppressLint("UnknownNullness") Intent intent,
int requestCode) {
super.startActivityForResult(intent, requestCode);
}
/**
* {@inheritDoc}
*
* @deprecated use
* {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
* with the appropriate {@link ActivityResultContract} and handling the result in the
* {@link ActivityResultCallback#onActivityResult(Object) callback}.
*/
@CallSuper
@Override
@Deprecated //被标记为过时
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
ComponentActivity
是通过androidx.activity:activity
引入的,通过查看该库的更新记录发现是在 androidx.activity:activity:1.2.0-alpha04 中将其废弃的,所以在这个版本之后,如果我们想使用startActivityForResult()
,都会看到过时提醒并推荐使用registerForActivityResult()
替代。
registerForActivityResult替代方案
使用示例
class ResultApiActivity : AppCompatActivity() {
//1、注册回调函数
private var resultLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
//处理返回的结果
val code = result.resultCode //返回码 如:Activity.RESULT_OK、Activity.RESULT_CANCELED
val data = result.data
log("resultCode:$code,data:${intent?.getStringExtra(ResultApi2Activity.KEY_TRANSFER)}")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_result_api)
mTvResultApi.setOnClickListener {
//2、启动Intent跳转
resultLauncher.launch(Intent(this, ResultApi2Activity::class.java))
}
}
}
//执行结果: resultCode:-1,data:i'm value from ResultApi2Activity
首先1处在 ComponentActivity
或 Fragment
中调用registerForActivityResult()
注册一个回调函数来处理其他页面返回的结果,registerForActivityResult()
接受 ActivityResultContract
和 ActivityResultCallback
作为参数:
- ActivityResultContract<I,O>:定义生成结果所需的
输入类型(I)
、输出类型(O)
,可为Activity传值
、拍照
、请求权限
等基本intent
操作提供默认协定,还可以创建自定义协定。 - ActivityResultCallback< O>:带有
onActivityResult(O result)
方法的interface
类,参数类型是ActivityResultContract<I,O>
中定义的输出类型O
对象。
registerForActivityResult()
方法执行后,返回ActivityResultLauncher
类型,用来启动另一个activity
,但是此时还没有启动,需要调用ActivityResultLauncher#launch()
进行启动。
ActivityResultContract 场景
上一节示例中,registerForActivityResult()
的第一个参数我们传入的是ActivityResultContracts.StartActivityForResult()
,其是ActivityResultContract
接口的具体实现类,我们点进ActivityResultContracts
看看其他实现类:
ActivityResultContracts.xxx | launch()入参 | 结果回调 | 说明 |
---|---|---|---|
StartActivityForResult | Intent | ActivityResult(resultCode, intent) | 启动另一个Activity并接收其返回结果 |
RequestPermission | String,如:Manifest.permission.CAMERA | Boolean | 请求一个权限并接收其授权结果 |
RequestMultiplePermissions | String[],如arrayOf(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE) | Map<String, Boolean> ,如:for ((permission, isGranted) in permissions) {if (isGranted) {// 权限已授权} else {// 权限未授权}} | 请求多个权限并接收其授权结果 |
CaptureVideo | Uri | ||
@RequiresApi(19) CreateDocument | String ,如:launcher.launch("document.txt") | Uri | 创建一个新的文档并返回其URI |
GetContent | String ,如:launcher.launch("image/*") | Uri | 打开文件选择器并获取所选文件的URI |
@RequiresApi(18) GetMultipleContents | String | List< Uri> | 打开文件选择器并获取所选多个文件的URI |
@RequiresApi(19) OpenDocument | String[] | Uri | 打开现有文档并返回其URI |
@RequiresApi(21) OpenDocumentTree | Uri | Uri | 打开文档树并返回所选目录的URI |
@RequiresApi(19) OpenMultipleDocuments | String[] | List< Uri> | 打开多个现有文档并返回它们的URI |
PickContact | Void | Uri | 打开联系人应用程序并返回所选联系人的URI |
StartIntentSenderForResult | IntentSenderRequest | ActivityResult | 启动一个IntentSender并接收其返回结果 |
TakePicture | Uri | Boolean | 启动相机应用程序并拍摄照片 |
TakePicturePreview | Void | Bitmap | 启动相机应用程序并拍摄预览照片 |
自定义ActivityResultContract
通常来说上一节介绍的ActivityResultContract
已经够我们平时开发使用了,不过系统还是提供了自定义ActivityResultContract
的能力。
自定义ActivityResultContract
可以让我们根据自己的需求创建一个新的Activity
或Intent
操作,并使用registerForActivityResult()
方法将其与ActivityResultLauncher
对象绑定。以下是一个自定义ActivityResultContract
的示例:
/**
* 1、自定义ActivityResultContract
*/
class CustomContract : ActivityResultContract<Void, String>() {
companion object {
const val DEFAULT_VALUE = "default_value"
}
/**
* 创建Intent
* @param context Context
* @param input 当前类的第一个泛型参数
* @return
*/
override fun createIntent(context: Context, input: Void?): Intent {
return Intent(context, ResultApi2Activity::class.java)
}
/**
* 解析结果,类似于Activity#onActivityResult
* @param resultCode 返回码 [Activity.setResult] 的 resultCode
* @param intent [Activity.setResult] 的 intent
* @return
*/
override fun parseResult(resultCode: Int, intent: Intent?): String {
if (resultCode != Activity.RESULT_OK || intent == null) return DEFAULT_VALUE
return intent.getStringExtra(ResultApi2Activity.KEY_TRANSFER) ?: DEFAULT_VALUE
}
}
//2、Activity中使用自定义Contract
private val mLauncher =
registerForActivityResult(CustomContract()) { result -> log("return result:$result") }
//点击事件中触发launch
mTvCustomContract.setOnClickListener { mLauncher.launch(null)}
//3、目标Activity
class ResultApi2Activity : AppCompatActivity() {
companion object{
const val KEY_TRANSFER = "key_transfer"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setResult(RESULT_OK, intent.putExtra(KEY_TRANSFER, "i'm value from ResultApi2Activity"))
}
}
首先自定义ActivityResultContract
,重写了其中的createIntent()
和parseResult()
方法。createIntent()
方法用于创建一个新的Intent
对象,因为输入参数为Void
,所以不需要传数据给目标Activity
。parseResult()
方法用于解析目标Activity
返回的结果,并将其转换为输出类型String
。
接着,使用registerForActivityResult()
方法将CustomContract
与ActivityResultLauncher
对象绑定,并在需要启动该Activity
时使用该对象的launch()
启动它。
源码浅析
上述示例中主要涉及两个方法,分别为registerForActivityResult()
及ActivityResultLauncher#launch()
方法,下面重点看下这两个方法。
registerForActivityResult()
//ComponentActivity.java
@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback) {
return registerForActivityResult(contract, mActivityResultRegistry, callback);
}
@NonNull
@Override
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);
}
可以看到最终调用了ActivityResultRegistry#register()
方法:
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();
//1、判断Lifecycle的状态必须小于Lifecycle.State.STARTED,否则直接抛异常
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.");
}
//2、生成唯一的 requestCode
final int requestCode = registerKey(key);
//3、获取或初始化生命周期容器LifecycleContainer
LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
if (lifecycleContainer == null) {
lifecycleContainer = new LifecycleContainer(lifecycle);
}
//4、注册观察者并将其添加到LifecycleContainer中
LifecycleEventObserver observer = new LifecycleEventObserver() {
@Override
public void onStateChanged(
@NonNull LifecycleOwner lifecycleOwner,
@NonNull Lifecycle.Event event) {
if (Lifecycle.Event.ON_START.equals(event)) {
mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract));
if (mParsedPendingResults.containsKey(key)) {
final O parsedPendingResult = (O) mParsedPendingResults.get(key);
mParsedPendingResults.remove(key);
callback.onActivityResult(parsedPendingResult);
}
final ActivityResult pendingResult = mPendingResults.getParcelable(key);
if (pendingResult != null) {
mPendingResults.remove(key);
callback.onActivityResult(contract.parseResult(
pendingResult.getResultCode(),
pendingResult.getData()));
}
} else if (Lifecycle.Event.ON_STOP.equals(event)) {
mKeyToCallback.remove(key);
} else if (Lifecycle.Event.ON_DESTROY.equals(event)) {
unregister(key);
}
}
};
lifecycleContainer.addObserver(observer);
mKeyToLifecycleContainers.put(key, lifecycleContainer);
//5、初始化ActivityResultLauncher对象并返回
return new ActivityResultLauncher<I>() {
@Override
public void launch(I input, @Nullable ActivityOptionsCompat options) {
mLaunchedKeys.add(key);
onLaunch(requestCode, contract, input, options);
}
@Override
public void unregister() {
ActivityResultRegistry.this.unregister(key);
}
@NonNull
@Override
public ActivityResultContract<I, ?> getContract() {
return contract;
}
};
}
- 1处判断当前
Lifecycle
的状态必须小于Lifecycle.State.STARTED
,否则直接抛异常。也就是说registerForActivityResult()
必须在onStart()
之前调用。 - 2处注册键值,该方法会调用
registerKey()
方法生成一个唯一的requestCode
,并将key
和requestCode
绑定起来。 - 3处从
mKeyToLifecycleContainers
集合中获取与key
对应的LifecycleContainer
对象。如果不存在,则会创建一个新的LifecycleContainer
对象,并将其与lifecycle
绑定起来。 - 4处创建一个
LifecycleEventObserver
对象,并将其添加到3处的LifecycleContainer
对象中。该观察者会在LifecycleOwner
对象的生命周期发生变化时执行相应的操作,例如在STARTED
状态时将CallbackAndContract
对象添加到mKeyToCallback
集合中,在STOPPED
状态时将其从集合中移除,在DESTROYED
状态时调用unregister()
方法注销ActivityResultLauncher
对象等。 - 5处返回一个
ActivityResultLauncher
对象,该对象包含了launch()
、unregister()
和getContract()
三个方法。其中,launch()
方法用于启动Activity
或Intent
操作,unregister()
方法用于注销ActivityResultLauncher
对象,getContract()
方法用于获取与之绑定的ActivityResultContract
对象。
ActivityResultLauncher#launch()
上一节中registerForActivityResult()返回了ActivityResultLauncher对象,当启动跳转时,只需要调用launch()方法即可:
public void launch(I input) {
launch(input, null);
}
public abstract void launch( I input, ActivityOptionsCompat options);
可以看到最终调用的 launch( I input, ActivityOptionsCompat options)
方法是一个abstract
抽象方法,其具体实现自然是上一节5处初始化ActivityResultLauncher
的地方,可以看到其内部又调用了ActivityResultRegistry#onLaunch(requestCode, contract, input, options)
方法,继续找ActivityResultRegistry
初始化的地方,可以找到在ComponentActivity
中初始化了ActivityResultRegistry
:
this.mActivityResultRegistry = new ActivityResultRegistry() {
public <I, O> void onLaunch(final int requestCode, @NonNull ActivityResultContract<I, O> contract, I input, @Nullable ActivityOptionsCompat options) {
ComponentActivity activity = ComponentActivity.this;
final ActivityResultContract.SynchronousResult<O> synchronousResult = contract.getSynchronousResult(activity, input);
//1、同步检查结果,如果不为空,直接返回contract.getSynchronousResult中的数据
if (synchronousResult != null) {
(new Handler(Looper.getMainLooper())).post(new Runnable() {
public void run() {
dispatchResult(requestCode, synchronousResult.getValue());
}
});
} else {
//2、创建Intent对象
Intent intent = contract.createIntent(activity, input);
Bundle optionsBundle = null;
if (intent.getExtras() != null && intent.getExtras().getClassLoader() == null) {
intent.setExtrasClassLoader(activity.getClassLoader());
}
if (intent.hasExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE")) {
optionsBundle = intent.getBundleExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE");
intent.removeExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE");
} else if (options != null) {
optionsBundle = options.toBundle();
}
if ("androidx.activity.result.contract.action.REQUEST_PERMISSIONS".equals(intent.getAction())) {
String[] permissions = intent.getStringArrayExtra("androidx.activity.result.contract.extra.PERMISSIONS");
if (permissions == null) {
permissions = new String[0];
}
ActivityCompat.requestPermissions(activity, permissions, requestCode);
} else if ("androidx.activity.result.contract.action.INTENT_SENDER_REQUEST".equals(intent.getAction())) {
IntentSenderRequest request = (IntentSenderRequest)intent.getParcelableExtra("androidx.activity.result.contract.extra.INTENT_SENDER_REQUEST");
try {
ActivityCompat.startIntentSenderForResult(activity, request.getIntentSender(), requestCode, request.getFillInIntent(), request.getFlagsMask(), request.getFlagsValues(), 0, optionsBundle);
} catch (final IntentSender.SendIntentException var11) {
(new Handler(Looper.getMainLooper())).post(new Runnable() {
public void run() {
dispatchResult(requestCode, 0, (new Intent()).setAction("androidx.activity.result.contract.action.INTENT_SENDER_REQUEST").putExtra("androidx.activity.result.contract.extra.SEND_INTENT_EXCEPTION", var11));
}
});
}
} else {
ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
}
}
}
};
- 1处用
ActivityResultContract
的getSynchronousResult()
方法获取同步结果。如果存在同步结果,则会将其返回值通过dispatchResult()
方法分发出去。 - 如果1处不存在同步结果,那么就会走2处逻辑,调用
ActivityResultContract
的createIntent()
方法创建一个新的Intent
对象,并根据需要设置Activity
或Intent
操作的选项。 - 检查
Intent
对象的Action
属性是否为"androidx.activity.result.contract.action.REQUEST_PERMISSIONS"
或"androidx.activity.result.contract.action.INTENT_SENDER_REQUEST"
。如果是,则会分别调用ActivityCompat.requestPermissions()
方法请求权限或ActivityCompat.startIntentSenderForResult()
方法启动相应的操作;如果都不是,则默认调用ActivityCompat.startActivityForResult()
方法启动Activity
或Intent
操作。
//ComponentActivity.java
//1、startActivityForResult()返回结果
@Deprecated
@CallSuper
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (!this.mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
//2、权限申请的结果
@Deprecated
@CallSuper
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (!this.mActivityResultRegistry.dispatchResult(requestCode, -1, (new Intent()).putExtra("androidx.activity.result.contract.extra.PERMISSIONS", permissions).putExtra("androidx.activity.result.contract.extra.PERMISSION_GRANT_RESULTS", grantResults)) && VERSION.SDK_INT >= 23) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
//分发ActivityResultLauncher对象的返回结果
@MainThread
public final boolean dispatchResult(int requestCode, int resultCode, @Nullable Intent data) {
String key = mRcToKey.get(requestCode);
if (key == null) {
return false;
}
mLaunchedKeys.remove(key);
doDispatch(key, resultCode, data, mKeyToCallback.get(key));
return true;
}
//3、处理返回结果
private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
@Nullable CallbackAndContract<O> callbackAndContract) {
if (callbackAndContract != null && callbackAndContract.mCallback != null) {
ActivityResultCallback<O> callback = callbackAndContract.mCallback;
ActivityResultContract<?, O> contract = callbackAndContract.mContract;
callback.onActivityResult(contract.parseResult(resultCode, data));
} else {
// Remove any parsed pending result
mParsedPendingResults.remove(key);
// And add these pending results in their place
mPendingResults.putParcelable(key, new ActivityResult(resultCode, data));
}
}
主要来看3处的doDispatch()
方法:
- 检查
callbackAndContract
对象是否为空,如果不为空,则会调用ActivityResultCallback
对象的onActivityResult()
方法处理返回结果。到这里执行结果就会在registerForActivityResult()
中的第2个参数中收到结果。 - 如果上述不成立,会将返回结果存储到
mPendingResults
集合中,以便稍后使用。
总结
- 个人认为
startActivityForResult()/onActivityResult()、onRequestPermissionsResult()
被标记成过时并不是因为有什么性能问题或者bug
,而是通过registerForActivityResult()
统一进行收口,为什么这么说呢,因为registerForActivityResult()
内部最终还是调用了ComponentActivity
中的startActivityForResult()、onActivityResult()、onRequestPermissionsResult()
方法,只不过是对其进行了统一的封装而已,简化了使用。 - 使用
registerForActivityResult()
,可以通过一个lambda
表达式来处理返回结果,避免了繁琐的requestCode
和onActivityResult
的处理逻辑。此外,registerForActivityResult
还支持多个请求码和多个返回结果,使得代码更加清晰和易于维护。
资料
【1】android官网:Activity Result API