「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() 接受 ActivityResultContractActivityResultCallback 作为参数,并返回 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
        }
});

@Override
public 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>() {
        @Override
        public void onActivityResult(Map result) {
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE};
        activityResultLauncher.launch(permissions);
    }

新旧对比:onActivityResult & Activity Result APIs

旧:

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    startActivityForResult(intent, 123);
}

@Override
protected 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 displayed
ActivityResultLauncher 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...

你可能感兴趣的:(android)