前言:昨天梳理Activity知识时有提到这个Activity Result API,今天深入梳理下这个启动API。Activity Result API是 AndroidX 中用于替代传统
startActivityForResult()的现代化解决方案,解决了旧方案的诸多痛点,提供了更清晰、更安全的数据交互方式。
一、为什么需要 Activity Result API?
传统方式的痛点:
- 强耦合:结果处理逻辑分散在
onActivityResult()中 - 类型不安全:需手动解析 Intent 数据
- 请求码管理:易出现请求码冲突(REQUEST_CODE)
- 生命周期问题:Activity 重建时结果可能丢失
新方案优势:
✅ 解耦结果处理逻辑
✅ 类型安全的输入/输出
✅ 自动管理请求码
✅ 生命周期感知(重建后自动恢复)
二、核心组件
1. ActivityResultLauncher
- 启动器对象,用于执行启动操作
- 通过泛型指定输入数据类型(如 Uri、String 等)
2. ActivityResultContract
-
定义输入/输出类型契约
-
系统内置常用 Contract:
StartActivityForResult():通用启动TakePicture():拍照返回图片RequestPermission():权限请求PickContact():选择联系人
3. ActivityResultCallback
- 结果回调接口
- 通过泛型定义结果类型
三、代码示例
1:注册启动器(推荐在 onCreate 中)
// 在 Activity/Fragment 中
private ActivityResultLauncher<Intent> detailLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 注册启动器
detailLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK) {
Intent data = result.getData();
String resultData = data.getStringExtra("RESULT_KEY");
// 处理返回数据
updateUI(resultData);
}
});
}
2:启动目标 Activity
// 启动带参数的目标 Activity
void openDetailPage(User user) {
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("USER_ID", user.id);
detailLauncher.launch(intent);
}
3:目标 Activity 返回结果
// 在 DetailActivity 中
void submitResult() {
Intent resultIntent = new Intent();
resultIntent.putExtra("RESULT_KEY", "处理成功");
setResult(RESULT_OK, resultIntent);
finish();
}
四、高级用法:自定义 Contract
ActivityResultContract是 Activity Result API 的核心抽象类,其设计体现了现代 Android 开发中的类型安全思想。
1.基本使用流程:
// 1. 自定义 Contract
class PickUserContract extends ActivityResultContract<Void, User>() {
@NonNull
@Override
public Intent createIntent(@NonNull Context context, Void input) {
return new Intent(context, UserPickerActivity.class);
}
@Override
public User parseResult(int resultCode, @Nullable Intent intent) {
if (resultCode != Activity.RESULT_OK || intent == null) {
return null;
}
return (User) intent.getSerializableExtra("SELECTED_USER");
}
}
// 2. 注册自定义 Contract
ActivityResultLauncher<Void> userPickerLauncher = registerForActivityResult(
new PickUserContract(),
user -> {
if (user != null) {
showSelectedUser(user);
}
});
// 3. 启动选择器
userPickerLauncher.launch(null);
2. 设计解析
// 设计思想体现
public abstract class ActivityResultContract<I, O> {
// 协议定义:什么输入产生什么输出
public abstract Intent createIntent(Context context, I input);
// 协议实现:如何解释系统返回结果
public abstract O parseResult(int resultCode, @Nullable Intent intent);
}
I(Input) :输入类型,表示启动操作所需的参数类型O(Output) :输出类型,表示操作返回的结果类型
// 系统内置的拍照 Contract
public class TakePicture extends ActivityResultContract<Uri, Boolean> {
// I = Uri (图片保存位置)
// O = Boolean (是否成功保存)
}
// 自定义文件选择 Contract
public class FilePicker extends ActivityResultContract<String, Uri> {
// I = String (MIME 类型过滤)
// O = Uri (选择的文件 URI)
}
3.核心方法解析
1. createIntent() 方法
@NonNull
public abstract Intent createIntent(@NonNull Context context, I input);
作用:将类型安全的输入参数转换为 Android 系统所需的 Intent
2. parseResult() 方法
public abstract O parseResult(int resultCode, @Nullable Intent intent);
作用:将原始的 Activity 返回结果转换为类型安全的输出对象
启动阶段
返回阶段
示例1:实现位置选择器
// 自定义
public class LocationPickerContract extends ActivityResultContract<Void, LatLng> {
// 输入:不需要参数(Void)
// 输出:经纬度对象(LatLng)
@NonNull
@Override
public Intent createIntent(@NonNull Context context, Void input) {
return new Intent(context, LocationPickerActivity.class);
}
@Override
public LatLng parseResult(int resultCode, @Nullable Intent intent) {
if (resultCode != Activity.RESULT_OK || intent == null) {
return null;
}
// 从Intent中解析经纬度
double lat = intent.getDoubleExtra("EXTRA_LATITUDE", 0);
double lng = intent.getDoubleExtra("EXTRA_LONGITUDE", 0);
return new LatLng(lat, lng);
}
}
//使用自定义的Contract
// 注册启动器
ActivityResultLauncher<Void> locationLauncher = registerForActivityResult(
new LocationPickerContract(),
latLng -> {
if (latLng != null) {
mapView.setCenter(latLng);
}
});
// 启动位置选择器
findViewById(R.id.btn_pick_location).setOnClickListener(v -> {
locationLauncher.launch(null); // 输入为Void类型
});
示例2:多参数输入
// 使用Pair组合多个输入参数
public class CropImageContract extends ActivityResultContract<Pair<Uri, Integer>, Uri> {
@NonNull
@Override
public Intent createIntent(@NonNull Context context, Pair<Uri, Integer> input) {
Uri sourceUri = input.first;
int aspectRatio = input.second;
return new Intent(context, CropActivity.class)
.putExtra("SOURCE_URI", sourceUri)
.putExtra("ASPECT_RATIO", aspectRatio);
}
// parseResult实现...
}
// 使用示例
ActivityResultLauncher<Pair<Uri, Integer>> cropLauncher = registerForActivityResult(...);
cropLauncher.launch(Pair.create(imageUri, 16));
使用时的Tips:
-
在
onCreate中初始化启动器 -
利用泛型避免类型转换错误
-
每个功能使用独立启动器
-
与 ViewModel 结合使用
//与ViewModel 整合
public class OrderViewModel extends ViewModel {
private final ActivityResultLauncher<Intent> paymentLauncher;
public void initLauncher(Fragment fragment) {
paymentLauncher = fragment.registerForActivityResult(...);
}
public void startPayment(Order order) {
paymentLauncher.launch(createPaymentIntent(order));
}
}