android获取当前activity view,获取 activity 的结果

启动另一个 activity(无论是您应用中的 activity 还是其他应用中的 activity)不一定是单向操作。您也可以启动另一个 activity 并接收返回的结果。例如,您的应用可启动相机应用并接收拍摄的照片作为结果。或者,您可以启动“通讯录”应用以便用户选择联系人,并且您将接收联系人详细信息作为结果。

虽然所有 API 级别的 Activity 类均提供底层 startActivityForResult() 和 onActivityResult() API,但我们强烈建议您使用 AndroidX Activity 和 Fragment 中引入的 Activity Result API。

Activity Result API 提供了用于注册结果、启动结果以及在系统分派结果后对其进行处理的组件。

针对 activity 结果注册回调

在启动 activity 以获取结果时,可能会出现您的进程和 activity 因内存不足而被销毁的情况;如果是使用相机等内存密集型操作,几乎可以确定会出现这种情况。

因此,Activity Result API 会将结果回调从您之前启动另一个 activity 的代码位置分离开来。由于在重新创建进程和 activity 时需要使用结果回调,因此每次创建 activity 时都必须无条件注册回调,即使启动另一个 activity 的逻辑仅基于用户输入内容或其他业务逻辑也是如此。

ActivityResultContract 定义生成结果所需的输入类型以及结果的输出类型。这些 API 可为拍照和请求权限等基本 intent 操作提供默认协定。您还可以创建自己的自定义协定。

ActivityResultCallback 是单一方法接口,带有 onActivityResult() 方法,可接受 ActivityResultContract 中定义的输出类型的对象:

Kotlin

val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->

// Handle the returned Uri

}Java

// GetContent creates an ActivityResultLauncher to allow you to pass

// in the mime type you'd like to allow the user to select

ActivityResultLauncher mGetContent = registerForActivityResult(new GetContent(),

new ActivityResultCallback() {

@Override

public void onActivityResult(Uri uri) {

// Handle the returned Uri

}

});

如果您有多个使用不同协定或需要单独回调的 activity 结果调用,则可以多次调用 registerForActivityResult(),以注册多个 ActivityResultLauncher 实例。每次创建 fragment 或 activity 时,都必须按照相同的顺序调用 registerForActivityResult(),才能确保将生成的结果传递给正确的回调。

在 fragment 或 activity 创建完毕之前可安全地调用 registerForActivityResult(),因此,在为返回的 ActivityResultLauncher 实例声明成员变量时可以直接使用它。

注意:虽然在 fragment 或 activity 创建完毕之前可安全地调用 registerForActivityResult(),但在 fragment 或 activity 的 Lifecycle 变为 CREATED 状态之前,您无法启动 ActivityResultLauncher。

启动 activity 以获取其结果

虽然 registerForActivityResult() 会注册您的回调,但它不会启动另一个 activity 并发出结果请求。这些操作由返回的 ActivityResultLauncher 实例负责。

如果存在输入内容,启动器会接受与 ActivityResultContract 的类型匹配的输入内容。调用 launch() 会启动生成结果的过程。当用户完成后续 activity 并返回时,系统将执行 ActivityResultCallback 中的 onActivityResult(),如以下示例所示:

Kotlin

val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->

// Handle the returned Uri

}

override fun onCreate(savedInstanceState: Bundle?) {

// ...

val selectButton = findViewById(R.id.select_button)

selectButton.setOnClickListener {

// Pass in the mime type you'd like to allow the user to select

// as the input

getContent.launch("image/*")

}

}Java

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 分开保存和恢复。

在单独的类中接收 activity 结果

虽然 ComponentActivity 和 Fragment 类通过实现 ActivityResultCaller 接口来允许您使用 registerForActivityResult() API,但您也可以直接使用 ActivityResultRegistry 在未实现 ActivityResultCaller 的单独类中接收结果。

例如,您可能需要实现一个 LifecycleObserver,用于处理协定的注册和启动器的启动:

Kotlin

class MyLifecycleObserver(private val registry : ActivityResultRegistry)

: DefaultLifecycleObserver {

lateinit var getContent : ActivityResultLauncher

override fun onCreate(owner: LifecycleOwner) {

getContent = registry.register("key", owner, GetContent()) { uri ->

// Handle the returned Uri

}

}

fun selectImage() {

getContent.launch("image/*")

}

}

class MyFragment : Fragment() {

lateinit var observer : MyLifecycleObserver

override fun onCreate(savedInstanceState: Bundle?) {

// ...

observer = MyLifecycleObserver(requireActivity().activityResultRegistry)

lifecycle.addObserver(observer)

}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

val selectButton = view.findViewById(R.id.select_button)

selectButton.setOnClickListener {

// Open the activity to select an image

observer.selectImage()

}

}

}Java

class MyLifecycleObserver implements DefaultLifecycleObserver {

private final ActivityResultRegistry mRegistry;

private ActivityResultLauncher mGetContent;

MyLifecycleObserver(@NonNull ActivityResultRegistry registry) {

mRegistry = registry;

}

public void onCreate(@NonNull LifecycleOwner owner) {

// ...

mGetContent = mRegistry.register(“key”, owner, new GetContent(),

new ActivityResultCallback() {

@Override

public void onActivityResult(Uri uri) {

// Handle the returned Uri

}

});

}

public void selectImage() {

// Open the activity to select an image

mGetContent.launch("image/*");

}

}

class MyFragment extends Fragment {

private MyLifecycleObserver mObserver;

@Override

void onCreate(Bundle savedInstanceState) {

// ...

mObserver = new MyLifecycleObserver(requireActivity().getActivityResultRegistry());

getLifecycle().addObserver(mObserver);

}

@Override

void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

Button selectButton = findViewById(R.id.select_button);

selectButton.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View view) {

mObserver.selectImage();

}

});

}

}

使用 ActivityResultRegistry API 时,强烈建议您使用可接受 LifecycleOwner 作为参数的 API,因为 LifecycleOwner 会在 Lifecycle 被销毁时自动移除已注册的启动器。不过,如果 LifecycleOwner 不存在,每个 ActivityResultLauncher 类都允许您手动调用 unregister() 作为替代。

测试

默认情况下,registerForActivityResult() 会自动使用 activity 提供的 ActivityResultRegistry。此外,它还提供了一个重载,让您可以传入自己的 ActivityResultRegistry 实例,该实例可用于测试您的 activity 结果调用,无需实际启动另一个 activity。

测试应用的 fragment 时,使用 FragmentFactory 将 ActivityResultRegistry 传入该 fragment 的构造函数即可提供测试 ActivityResultRegistry。

注意:任何允许您在测试中注入单独 ActivityResultRegistry 的机制都足以支持您测试 activity 结果调用。

例如,使用 TakePicturePreview 协定获取图片缩略图的 fragment 可能按类似如下所示的方式编写:

Kotlin

class MyFragment(

private val registry: ActivityResultRegistry

) : Fragment() {

val thumbnailLiveData = MutableLiveData

val takePicture = registerForActivityResult(TakePicturePreview(), registry) {

bitmap: Bitmap? -> thumbnailLiveData.setValue(bitmap)

}

// ...

}Java

public class MyFragment extends Fragment {

private final ActivityResultRegistry mRegistry;

private final MutableLiveData mThumbnailLiveData = new MutableLiveData();

private final ActivityResultLauncher mTakePicture =

registerForActivityResult(new TakePicturePreview(), mRegistry, new ActivityResultCallback() {

@Override

public void onActivityResult(Bitmap thumbnail) {

mThumbnailLiveData.setValue(thumbnail);

}

});

public MyFragment(@NonNull ActivityResultRegistry registry) {

super();

mRegistry = registry;

}

@VisibleForTesting

@NonNull

ActivityResultLauncher getTakePicture() {

return mTakePicture;

}

@VisibleForTesting

@NonNull

LiveData getThumbnailLiveData() {

return mThumbnailLiveData;

}

// ...

}

创建专用于测试的 ActivityResultRegistry 时,必须实现 onLaunch() 方法。您的测试实现可以直接调用 dispatchResult(),而不是调用 startActivityForResult(),从而提供要在测试中使用的确切结果:

val testRegistry = object : ActivityResultRegistry() {

override fun onLaunch(

requestCode: Int,

contract: ActivityResultContract,

input: I,

options: ActivityOptionsCompat?

) {

dispatchResult(requestCode, expectedResult)

}

}

完整的测试会产生预期的结果,构造一个测试 ActivityResultRegistry,将其传递给 fragment,直接触发启动器或通过 Espresso 等其他测试 API 触发启动器,然后验证结果:

@Test

fun activityResultTest {

// Create an expected result Bitmap

val expectedResult = Bitmap.createBitmap(1, 1, Bitmap.Config.RGBA_F16)

// Create the test ActivityResultRegistry

val testRegistry = object : ActivityResultRegistry() {

override fun onLaunch(

requestCode: Int,

contract: ActivityResultContract,

input: I,

options: ActivityOptionsCompat?

) {

dispatchResult(requestCode, expectedResult)

}

}

// Use the launchFragmentInContainer method that takes a

// lambda to construct the Fragment with the testRegistry

with(launchFragmentInContainer { MyFragment(testRegistry) }) {

onFragment { fragment ->

// Trigger the ActivityResultLauncher

fragment.takePicture()

// Verify the result is set

assertThat(fragment.thumbnailLiveData.value)

.isSameInstanceAs(expectedResult)

}

}

}

创建自定义协定

虽然 ActivityResultContracts 包含一些预先构建的可用 ActivityResultContract 类,但您可以使用自己的协定,提供您所需要的精确类型安全 API。

每个 ActivityResultContract 都需要定义输入和输出类,如果您不需要任何输入,可使用 Void(在 Kotlin 中,使用 Void? 或 Unit)作为输入类型。

每个协定都必须实现 createIntent() 方法,该方法接受 Context 和输入内容作为参数,并构造将与 startActivityForResult() 配合使用的 Intent。

每个协定还必须实现 parseResult(),这会根据指定的 resultCode(如 Activity.RESULT_OK 或 Activity.RESULT_CANCELED)和 Intent 生成输出内容。

如果无需调用 createIntent()、启动另一个 activity 并借助 parseResult() 来构建结果即可确定指定输入内容的结果,协定可以选择性地实现 getSynchronousResult()。

Kotlin

class PickRingtone : ActivityResultContract() {

override fun createIntent(context: Context, ringtoneType: Int) =

Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {

putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringtoneType)

}

override fun parseResult(resultCode: Int, result: Intent?) : Uri? {

if (resultCode != Activity.RESULT_OK) {

return null

}

return result?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)

}

}Java

public class PickRingtone extends ActivityResultContract {

@NonNull

@Override

public Intent createIntent(@NonNull Context context, @NonNull Integer ringtoneType) {

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringtoneType.intValue());

return intent;

}

@Override

public Uri parseResult(int resultCode, @Nullable Intent result) {

if (resultCode != Activity.RESULT_OK || result == null) {

return null;

}

return result.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);

}

}

如果您不需要自定义协定,则可以使用 StartActivityForResult 协定。这是一个通用协定,它可接受任何 Intent 作为输入内容并返回 ActivityResult,让您能够在回调中提取 resultCode 和 Intent,如以下示例所示:

Kotlin

val startForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->

if (result.resultCode == Activity.RESULT_OK) {

val intent = result.intent

// Handle the Intent

}

}

override fun onCreate(savedInstanceState: Bundle) {

// ...

val startButton = findViewById(R.id.start_button)

startButton.setOnClickListener {

// Use the Kotlin extension in activity-ktx

// passing it the Intent you want to start

startForResult.launch(Intent(this, ResultProducingActivity::class.java))

}

}Java

ActivityResultLauncher mStartForResult = registerForActivityResult(new StartActivityForResult(),

new ActivityResultCallback() {

@Override

public void onActivityResult(ActivityResult result) {

if (result.getResultCode() == Activity.RESULT_OK) {

Intent intent = result.getIntent();

// Handle the Intent

}

}

});

@Override

public void onCreate(@Nullable savedInstanceState: Bundle) {

// ...

Button startButton = findViewById(R.id.start_button);

startButton.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View view) {

// The launcher with the Intent you want to start

mStartForResult.launch(new Intent(this, ResultProducingActivity.class));

}

});

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值