Android registerForActivityResult 详解
2026/8/3 3:50:04 网站建设 项目流程

文章目录

    • 一、背景
    • 二、基本用法
      • 示例:启动 Activity 并获取回传数据
      • 示例:申请单个权限
    • 三、核心概念
      • 执行流程
    • 四、注册时机
    • 五、常用 Contract
      • 5.1 StartActivityForResult
      • 5.2 RequestPermission / RequestMultiplePermissions
      • 5.3 GetContent / GetMultipleContents
      • 5.4 TakePicture / CaptureVideo
      • 5.4 CreateDocument / OpenDocument
      • 5.5 内置 Contract 汇总
    • 六、自定义 Contract
      • 示例:封装 Activity 跳转
      • 示例:多选图片
    • 七、新旧对比

一、背景

在 Android 开发中,从当前 Activity 发起一个操作并在完成后获取返回结果是常见的需求,典型场景包括:

  • 跳转到另一个 Activity,等它关闭后获取回传数据
  • 向用户申请运行时权限
  • 从相册或文件管理器中选择文件
  • 调用系统相机拍照

在 AndroidX Activity 1.2.0 之前,这些场景依赖多套不同的 API:Activity 跳转用startActivityForResult/onActivityResult,权限申请用requestPermissions/onRequestPermissionsResult。这些 API 存在一些共性问题:

  1. 代码分散:发起请求和接收结果的逻辑分布在不同的方法中,不利于阅读和维护。
  2. requestCode 管理繁琐:需要手动维护整型常量,多模块协作时容易产生冲突。
  3. 回调膨胀:所有返回结果汇聚到同一个回调方法中,业务扩展后分支逻辑不断膨胀。
  4. API 风格不统一:Activity 跳转和权限申请使用完全不同的回调机制。

AndroidX 引入了registerForActivityResult来解决这些问题,将所有"发起—返回"模式统一为一套声明式 API。


二、基本用法

registerForActivityResult的调用形式为:

ActivityResultLauncher<I>launcher=registerForActivityResult(ActivityResultContract<I,O>contract,ActivityResultCallback<O>callback);// 触发操作launcher.launch(input);

与旧 API 最直观的区别是:不再需要requestCode,也不再需要重写任何回调方法。发起和接收结果在同一个代码块中完成声明。

示例:启动 Activity 并获取回传数据

ActivityResultLauncher<Intent>launcher=registerForActivityResult(newActivityResultContracts.StartActivityForResult(),result->{if(result.getResultCode()==RESULT_OK&&result.getData()!=null){Stringvalue=result.getData().getStringExtra("key");}});launcher.launch(newIntent(this,SecondActivity.class));

示例:申请单个权限

ActivityResultLauncher<String>launcher=registerForActivityResult(newActivityResultContracts.RequestPermission(),granted->{if(granted){// 授权成功}else{// 授权被拒绝}});launcher.launch(Manifest.permission.CAMERA);

可以看到,Activity 跳转和权限申请现在使用完全相同的 API 风格,代码结构一致。


三、核心概念

registerForActivityResult围绕三个核心概念构建:

概念类型职责
ContractActivityResultContract<I, O>定义操作的类型:输入 I 是什么,输出 O 是什么,以及如何生成启动 Intent、如何解析返回结果
CallbackActivityResultCallback<O>结果返回后的处理逻辑,参数 O 的类型由 Contract 决定
LauncherActivityResultLauncher<I>通过registerForActivityResult获得的启动器,调用launch(I)触发操作

执行流程

launch(输入 I) ↓ Contract.createIntent(Context, I) → 生成 Intent → 打开目标组件 ↓ 目标组件关闭 ↓ Contract.parseResult(int, Intent) → 将返回数据解析为类型 O ↓ Callback.onActivityResult(O) → 执行业务逻辑

四、注册时机

registerForActivityResult必须在 Activity 生命周期进入STARTED状态之前调用。官方推荐在onCreate中进行注册。

// 正确@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);launcher=registerForActivityResult(...);}// 错误privatevoidonButtonClick(){launcher=registerForActivityResult(...);// 此时生命周期已过 STARTED}

原因:当 Activity 因系统内存回收被销毁后重建时,只会重新执行onCreate。如果在onCreate中注册,重建时会自动重新注册,之前在途的请求结果也不会丢失。若在事件回调中注册,重建后注册缺失,结果将无法兑现。


五、常用 Contract

AndroidX 内置了多种 Contract,覆盖常见场景。

5.1 StartActivityForResult

启动一个 Activity 并接收返回结果。最常用的 Contract。

ActivityResultLauncher<Intent>launcher=registerForActivityResult(newActivityResultContracts.StartActivityForResult(),result->{if(result.getResultCode()==RESULT_OK&&result.getData()!=null){Stringdata=result.getData().getStringExtra("extra_key");}});
  • 输入:Intent
  • 输出:ActivityResult(封装了resultCodedata

5.2 RequestPermission / RequestMultiplePermissions

申请单个或多个运行时权限。

// 单个权限ActivityResultLauncher<String>singlePermissionLauncher=registerForActivityResult(newActivityResultContracts.RequestPermission(),granted->{/* granted 为 Boolean */});// 多个权限ActivityResultLauncher<String[]>multiPermissionLauncher=registerForActivityResult(newActivityResultContracts.RequestMultiplePermissions(),result->{/* result 为 Map<String, Boolean> */});
  • RequestPermission:输入String(权限名),输出Boolean
  • RequestMultiplePermissions:输入String[](权限名数组),输出Map<String, Boolean>

5.3 GetContent / GetMultipleContents

从系统文件选择器中选择文件。

// 单选ActivityResultLauncher<String>launcher=registerForActivityResult(newActivityResultContracts.GetContent(),uri->{/* uri 为 Uri? */});launcher.launch("image/*");// MIME 类型launcher.launch("video/*");launcher.launch("*/*");// 任意文件// 多选ActivityResultLauncher<String>multiLauncher=registerForActivityResult(newActivityResultContracts.GetMultipleContents(),uris->{/* uris 为 List<Uri> */});

5.4 TakePicture / CaptureVideo

调用系统相机拍照或录像。

// 拍照ActivityResultLauncher<Uri>takePictureLauncher=registerForActivityResult(newActivityResultContracts.TakePicture(),success->{/* success 为 Boolean */});// 录像ActivityResultLauncher<Uri>captureVideoLauncher=registerForActivityResult(newActivityResultContracts.CaptureVideo(),success->{/* success 为 Boolean */});// 需要先准备输出文件的 UriUrioutputUri=/* 由 FileProvider 生成 */;takePictureLauncher.launch(outputUri);

5.4 CreateDocument / OpenDocument

创建或打开文档。

// 创建新文件ActivityResultLauncher<String>createLauncher=registerForActivityResult(newActivityResultContracts.CreateDocument("text/plain"),uri->{/* uri 为 Uri? */});createLauncher.launch("文件名.txt");// 打开已有文件ActivityResultLauncher<String[]>openLauncher=registerForActivityResult(newActivityResultContracts.OpenDocument(),uri->{/* uri 为 Uri? */});openLauncher.launch(newString[]{"image/*","application/pdf"});

5.5 内置 Contract 汇总

Contract输入输出用途
RequestPermissionStringBoolean单个权限申请
RequestMultiplePermissionsString[]Map<String, Boolean>多个权限申请
StartActivityForResultIntentActivityResult通用 Activity 启动
StartIntentSenderForResultIntentSenderRequestActivityResult启动 IntentSender
GetContentString(MIME)Uri?选择单个文件
GetMultipleContentsString(MIME)List<Uri>选择多个文件
TakePictureUriBoolean拍照并保存
TakePicturePreviewBitmap?拍照获取缩略图
CaptureVideoUriBoolean录像并保存
CreateDocumentString(MIME)Uri?创建文件
OpenDocumentString[](MIME)Uri?打开文件
OpenDocumentTreeUri?Uri?选择目录
PickContactUri?选择联系人

六、自定义 Contract

当内置 Contract 无法满足需求时,可以通过继承ActivityResultContract<I, O>实现自定义 Contract。需要覆写两个方法:

方法调用时机作用
createIntent(Context, I)launch()调用时根据输入 I 生成启动目标组件的 Intent
parseResult(int, Intent)目标组件关闭时将 resultCode 和 Intent 解析为输出 O

示例:封装 Activity 跳转

将启动某个 Activity 并传递字符串的逻辑封装为 Contract,调用方无需直接构造 Intent:

classOpenTargetActivityContractextendsActivityResultContract<String,ActivityResult>{privatefinalClass<?extendsActivity>targetClass;publicOpenTargetActivityContract(Class<?extendsActivity>targetClass){this.targetClass=targetClass;}@NonNull@OverridepublicIntentcreateIntent(@NonNullContextcontext,Stringinput){Intentintent=newIntent(context,targetClass);intent.putExtra("payload",input);returnintent;}@NonNull@OverridepublicActivityResultparseResult(intresultCode,@NullableIntentdata){returnnewActivityResult(resultCode,data);}}// 使用ActivityResultLauncher<String>launcher=registerForActivityResult(newOpenTargetActivityContract(TargetActivity.class),result->{if(result.getResultCode()==RESULT_OK&&result.getData()!=null){Stringback=result.getData().getStringExtra("result");}});launcher.launch("要传递的内容");

示例:多选图片

系统默认的GetMultipleContents已支持多选,这里通过自定义 Contract 来演示实现思路:

classPickMultipleImagesContractextendsActivityResultContract<Void,List<Uri>>{@NonNull@OverridepublicIntentcreateIntent(@NonNullContextcontext,Voidinput){Intentintent=newIntent(Intent.ACTION_PICK);intent.setType("image/*");intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);returnintent;}@NonNull@OverridepublicList<Uri>parseResult(intresultCode,@NullableIntentdata){if(resultCode==RESULT_OK&&data!=null){List<Uri>uris=newArrayList<>();ClipDataclipData=data.getClipData();if(clipData!=null){for(inti=0;i<clipData.getItemCount();i++){uris.add(clipData.getItemAt(i).getUri());}}elseif(data.getData()!=null){uris.add(data.getData());}returnuris;}returnCollections.emptyList();}}

七、新旧对比

对比维度旧 APIregisterForActivityResult
Activity 回传startActivityForResult+onActivityResultStartActivityForResultContract
权限申请requestPermissions+onRequestPermissionsResultRequestPermissionContract
文件选择手动构造 Intent +onActivityResultGetContent/OpenDocumentContract
代码组织发起和回调分离在两个方法在同一位置声明
requestCode手动管理整型常量
类型安全回调参数无编译期约束泛型保证类型匹配
可扩展性需覆写 Activity/Fragment 回调方法实现自定义 Contract

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询