「Android」从其他Activity获取结果:registerForActivityResult()
「Android」从其他Activity获取结果:registerForActivityResult()
简介
- Activity Result APIs 可以取代 startActivityForResult 方法,去启动Activity以获取结果
- Activity Result APIs 可以取代 requestPermissions 方法,去请求运行时权限
背景
启动一个 activity(无论是本应用中的 activity 还是其他应用中的 activity)不一定是单向操作,也可以启动另一个 activity 并接收返回的结果。
常见的场景是调用系统相机、调用相册获取照片、调用通讯录、获取部分特殊权限等,传统方式通常是通过 Intent 携带数据,然后使用 startActivityForResult 方法来启动下一个 Activity,然后通过 onActivityResult 来接收返回的数据。
传统方式的问题在于:
- 在启动 activity 以获取结果时,可能会出现进程和 activity 因内存不足而被销毁的情况
- onActivityResult 回调方法嵌套耦合严重,逻辑混乱导致难以维护
为启动Activity获取的结果注册结果回调
基于上述存在问题,Activity Result APIs 将启动 Activity 的结果回调,与启动 Activity 的逻辑进行了分离。
位于 ComponentActivity 或 Fragment 中时,Activity Result API 提供了 registerForActivityResult() API,用于注册结果回调。
registerForActivityResult() 接受 ActivityResultContract 和 ActivityResultCallback 作为参数,并返回 ActivityResultLauncher,用来启动另一个 activity,其中:
- ActivityResultContract 定义生成结果所需的输入类型以及结果的输出类型。这些 API 可为拍照和请求权限等基本 intent 操作提供默认协定,同时还可以创建自定义协定。
- ActivityResultCallback 是单一方法接口,带有 onActivityResult() 方法,可接受 ActivityResultContract 中定义的输出类型的对象:
ActivityResultLauncher mGetContent = registerForActivityResult(new GetContent(), new ActivityResultCallback() { @Override public void onActivityResult(Uri uri) { // Handle the returned Uri }});
启动Activity以获取结果
registerForActivityResult() 仅为启动 Activity 获取的结果注册结果回调,但它本身不会并启动另一个 activity 并发出结果请求。启动 Activity 的操作由 registerForActivityResult() 返回的 ActivityResultLauncher 的实例对象负责。
如果存在输入参数,ActivityResultLauncher 的实例对象会根据输入参数去匹配 ActivityResultContract 的类型。调用ActivityResultLauncher 的实例对象的 launch() 方法,会启动 Activity 并获取结果。当用户完成后续 activity 并返回时,系统将执行 ActivityResultCallback 中的 onActivityResult() 方法。
ActivityResultLauncher mGetContent = registerForActivityResult(new GetContent(), new ActivityResultCallback() { @Override public void onActivityResult(Uri uri) { // Handle the returned Uri }});@Overridepublic void onCreate(@Nullable savedInstanceState: Bundle) { // ... Button selectButton = findViewById(R.id.select_button); selectButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { // Pass in the mime type you'd like to allow the user to select // as the input mGetContent.launch("image/*"); } });}
注意:由于在调用 launch() 与触发 onActivityResult() 回调的两个时间点之间,进程和 activity 可能会被销毁,因此,处理结果所需的任何其他状态,都必须与这些 API 分开保存和恢复。
预定义Contract
在 Activity Result APIs 中提供了一系列预定义 Contract 供开发者去分别启动Activity以获取结果和请求运行时权限:
- StartActivityForResult:通用的Contract,不做任何转换,Intent作为输入,ActivityResult作为输出,这也是最常用的一个协定。
- RequestMultiplePermissions:用于请求一组权限
- RequestPermission:用于请求单个权限
- TakePicturePreview:调用MediaStore.ACTION_IMAGE_CAPTURE拍照,返回值为Bitmap图片
- TakePicture:调用MediaStore.ACTION_IMAGE_CAPTURE拍照,并将图片保存到给定的Uri地址,返回true表示保存成功。
- TakeVideo:调用MediaStore.ACTION_VIDEO_CAPTURE 拍摄视频,保存到给定的Uri地址,返回一张缩略图。
- PickContact:从通讯录APP获取联系人
- GetContent:提示用选择一条内容,返回一个通过ContentResolver#openInputStream(Uri)访问原生数据的Uri地址(content://形式) 。默认情况下,它增加了 Intent#CATEGORY_OPENABLE, 返回可以表示流的内容。
- CreateDocument:提示用户选择一个文档,返回一个(file:/http:/content:)开头的Uri。
- OpenMultipleDocuments:提示用户选择文档(可以选择多个),分别返回它们的Uri,以List的形式。
- OpenDocumentTree:提示用户选择一个目录,并返回用户选择的作为一个Uri返回,应用程序可以完全管理返回目录中的文档。
使用以上预定义 Contract 进行开发的经典例子:
StartActivityForResult
ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback() { @Override public void onActivityResult(ActivityResult result) { } }); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); activityResultLauncher.launch(intent); }
RequestMultiplePermissions
ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback
新旧对比:onActivityResult & Activity Result APIs
旧:
public void openSomeActivityForResult() { Intent intent = new Intent(this, SomeActivity.class); startActivityForResult(intent, 123);}@Overrideprotected void onActivityResult (int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK && requestCode == 123) { doSomeOperations(); }}
新:
// You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayedActivityResultLauncher someActivityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { // There are no request codes Intent data = result.getData(); doSomeOperations(); } } });public void openSomeActivityForResult() { Intent intent = new Intent(this, SomeActivity.class); someActivityResultLauncher.launch(intent);}
参考
https://segmentfault.com/a/11...
https://developer.android.com...
更多相关文章
- Fuel:Kotlin / Android最简单的HTTP网络库
- Flutter Android启动源码分析(一)
- Android(安卓)MTP之服务端UsbService启动
- Activity的四种启动方式
- android 获取sim卡运营商信息
- Android(安卓)Studio ADB响应失败解决方法
- (备忘)Android(安卓)app中调用启动其他应用(系统应用和第三方应用)
- 《Android》Lesson09-Acitivity的四种启动模式
- android recovery 模式启动进入流程