Activity Result API 详解:现代 Android 开发的优雅数据交互方案

300 阅读3分钟

前言:昨天梳理Activity知识时有提到这个Activity Result API,今天深入梳理下这个启动API。Activity Result API是 AndroidX 中用于替代传统 startActivityForResult() 的现代化解决方案,解决了旧方案的诸多痛点,提供了更清晰、更安全的数据交互方式。

一、为什么需要 Activity Result API?

传统方式的痛点:
  1. 强耦合:结果处理逻辑分散在 onActivityResult() 中
  2. 类型不安全:需手动解析 Intent 数据
  3. 请求码管理:易出现请求码冲突(REQUEST_CODE)
  4. 生命周期问题: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 返回结果转换为类型安全的输出对象

启动阶段

20250604_b7922a.png

返回阶段

result20250604_489bc4.png

示例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:
  1. 在 onCreate 中初始化启动器

  2. 利用泛型避免类型转换错误

  3. 每个功能使用独立启动器

  4. 与 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));
    }
}