การเริ่มกิจกรรมอื่น ไม่ว่าจะเป็นกิจกรรมภายในแอปหรือจากกิจกรรมอื่น แอป ไม่จำเป็นต้องเป็นการดำเนินการทางเดียว คุณเริ่มกิจกรรมได้ด้วย และรับผลลัพธ์กลับมา เช่น แอปของคุณสามารถเริ่มแอปกล้อง ได้รับรูปภาพที่ถ่าย หรือคุณอาจเริ่มแอปรายชื่อติดต่อ เพื่อให้ผู้ใช้เลือกรายชื่อติดต่อ แล้วจึงรับรายชื่อติดต่อ รายละเอียด
ขณะที่กลยุทธ์
startActivityForResult()
และ
onActivityResult()
API สามารถใช้ได้ในคลาส Activity
ใน API ทุกระดับ Google
แนะนำให้ใช้ API ของผลการค้นหากิจกรรมซึ่งเปิดตัวใน AndroidX
Activity
และ Fragment
ชั้นเรียน
API ของผลการค้นหากิจกรรมมีคอมโพเนนต์สำหรับลงทะเบียนผลลัพธ์ เปิดใช้งานกิจกรรมที่สร้างผลลัพธ์ และจัดการผลลัพธ์ทันที ส่งโดยระบบ
ลงทะเบียน Callback สำหรับผลของกิจกรรม
เมื่อเริ่มต้นกิจกรรมสำหรับผลลัพธ์ เป็นไปได้ และในกรณี การทำงานที่ใช้หน่วยความจำมาก เช่น การใช้กล้อง นั้นแทบจะแน่นอนว่า และกิจกรรมของคุณจะถูกทำลายเนื่องจากหน่วยความจำเหลือน้อย
ด้วยเหตุนี้ API ของผลการค้นหากิจกรรมจะแยกผลลัพธ์ Callback จากตำแหน่งในโค้ดที่คุณเรียกใช้กิจกรรมอื่น เพราะ Callback ผลลัพธ์ก็จำเป็นจะต้องพร้อมใช้งานเมื่อกระบวนการและกิจกรรมของคุณ ขึ้น การเรียกกลับต้องได้รับการลงทะเบียนโดยไม่มีเงื่อนไขทุกครั้งที่ สร้างกิจกรรมแม้ว่าจะมีตรรกะในการเรียกใช้กิจกรรมอื่นเพียงอย่างเดียว ขึ้นอยู่กับข้อมูลจากผู้ใช้หรือตรรกะทางธุรกิจอื่นๆ
เมื่ออยู่ใน
ComponentActivity
หรือ
Fragment
, ผลลัพธ์ของกิจกรรม
API มี
registerForActivityResult()
API สำหรับการลงทะเบียน Callback ผลลัพธ์ registerForActivityResult()
ทำประตู
ActivityResultContract
และ
ActivityResultCallback
และแสดงค่า
ActivityResultLauncher
ที่คุณใช้ในการเริ่มกิจกรรมอื่น
ActivityResultContract
กำหนดประเภทอินพุตที่จำเป็นต่อการสร้างผลลัพธ์
พร้อมกับประเภทเอาต์พุตของผลการค้นหา API มี
สัญญาเริ่มต้น
สำหรับการดำเนินการผ่าน Intent พื้นฐาน เช่น การถ่ายภาพ การขอสิทธิ์
เปิดอยู่ นอกจากนี้คุณยัง
สร้างสัญญาที่กำหนดเอง
ActivityResultCallback
เป็นอินเทอร์เฟซเมธอดเดียวที่มี
onActivityResult()
ซึ่งจะนำออบเจ็กต์ของประเภทเอาต์พุตที่ระบุใน
ActivityResultContract
:
Kotlin
val getContent = registerForActivityResult(GetContent()) { uri: Uri? -> // Handle the returned Uri }
Java
// GetContent creates an ActivityResultLauncher<String> to let you pass // in the mime type you want to let the user select ActivityResultLauncher<String> mGetContent = registerForActivityResult(new GetContent(), new ActivityResultCallback<Uri>() { @Override public void onActivityResult(Uri uri) { // Handle the returned Uri } });
หากคุณมีการเรียกผลของกิจกรรมหลายรายการและคุณเลือกใช้
สัญญา
หรือต้องการการติดต่อกลับแยกกัน คุณสามารถโทรหา registerForActivityResult()
หลายรายการ
จำนวนครั้งในการลงทะเบียนอินสแตนซ์ ActivityResultLauncher
หลายรายการ คุณต้อง
เรียก registerForActivityResult()
ในลำดับเดียวกันสำหรับการสร้างแต่ละรายการ
ส่วนย่อยหรือกิจกรรมเพื่อให้มีการจัดส่งผลลัพธ์ที่กำลังดำเนินการไปยัง
Callback ที่ถูกต้อง
เรียก registerForActivityResult()
ได้อย่างปลอดภัยก่อนส่วนย่อยหรือกิจกรรม
สร้างขึ้น โดยให้นำมาใช้ได้โดยตรงเมื่อประกาศตัวแปรสมาชิก
สำหรับอินสแตนซ์ ActivityResultLauncher
รายการที่แสดงผล
เปิดกิจกรรมสำหรับผลลัพธ์
ขณะที่ registerForActivityResult()
จะลงทะเบียนการติดต่อกลับของคุณ ระบบจะไม่
เปิดกิจกรรมอื่นและเริ่มคำขอผลลัพธ์ แต่วิธีนี้
เป็นความรับผิดชอบของอินสแตนซ์ ActivityResultLauncher
ที่แสดงผล
ถ้ามีอินพุตอยู่แล้ว ตัวเรียกใช้งานจะใช้อินพุตที่ตรงกับประเภทของ
ActivityResultContract
การโทร
launch()
เริ่มกระบวนการสร้างผลลัพธ์ เมื่อผู้ใช้ดำเนินการต่างๆ เสร็จแล้ว
กิจกรรมและการคืนสินค้าที่ตามมา onActivityResult()
จาก
จากนั้น ระบบจะดำเนินการ ActivityResultCallback
ดังที่แสดงในตัวอย่างต่อไปนี้
Kotlin
val getContent = registerForActivityResult(GetContent()) { uri: Uri? -> // Handle the returned Uri } override fun onCreate(savedInstanceState: Bundle?) { // ... val selectButton = findViewById<Button>(R.id.select_button) selectButton.setOnClickListener { // Pass in the mime type you want to let the user select // as the input getContent.launch("image/*") } }
Java
ActivityResultLauncher<String> mGetContent = registerForActivityResult(new GetContent(), new ActivityResultCallback<Uri>() { @Override public void onActivityResult(Uri uri) { // Handle the returned Uri } }); @Override public void onCreate(@Nullable Bundle savedInstanceState) { // ... Button selectButton = findViewById(R.id.select_button); selectButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { // Pass in the mime type you want to let the user select // as the input mGetContent.launch("image/*"); } }); }
เวอร์ชันโอเวอร์โหลด
launch()
ซึ่งช่วยให้คุณส่งผ่าน
ActivityOptionsCompat
นอกเหนือจากอินพุต
รับผลของกิจกรรมในชั้นเรียนแยกต่างหาก
แม้ว่าคลาส ComponentActivity
และ Fragment
จะใช้เมธอด
ActivityResultCaller
ให้คุณใช้ registerForActivityResult()
API ได้
ได้รับผลลัพธ์กิจกรรมในชั้นเรียนแยกต่างหากที่ไม่ได้ใช้
ActivityResultCaller
โดยใช้
ActivityResultRegistry
โดยตรง
ตัวอย่างเช่น คุณอาจต้องการใช้
LifecycleObserver
ที่จัดการการลงทะเบียนสัญญาพร้อมการเปิดตัว Launcher ดังนี้
Kotlin
class MyLifecycleObserver(private val registry : ActivityResultRegistry) : DefaultLifecycleObserver { lateinit var getContent : ActivityResultLauncher<String> 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<Button>(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<String> mGetContent; MyLifecycleObserver(@NonNull ActivityResultRegistry registry) { mRegistry = registry; } public void onCreate(@NonNull LifecycleOwner owner) { // ... mGetContent = mRegistry.register(“key”, owner, new GetContent(), new ActivityResultCallback<Uri>() { @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(); } }); } }
Google ขอแนะนำอย่างยิ่งให้ใช้ ActivityResultRegistry
API
API ที่ใช้ LifecycleOwner
ซึ่งเป็น LifecycleOwner
โดยอัตโนมัติ
นำ Launcher ที่ลงทะเบียนไว้ออกเมื่อทำลาย Lifecycle
อย่างไรก็ตาม
ในกรณีที่ LifecycleOwner
ไม่พร้อมใช้งาน
ชั้นเรียน ActivityResultLauncher
ให้คุณโทรหาด้วยตนเองได้
unregister()
ไว้ใช้แทน
ทดสอบ
โดยค่าเริ่มต้น registerForActivityResult()
จะใช้ส่วน
วันที่ ActivityResultRegistry
ที่ได้รับจากกิจกรรม และยังให้โอเวอร์โหลดที่ช่วยให้คุณข้าม
ในอินสแตนซ์ของ ActivityResultRegistry
ของคุณเอง ซึ่งคุณสามารถใช้เพื่อทดสอบ
การเรียกผลของกิจกรรมโดยไม่ได้เปิดกิจกรรมอื่นขึ้นมาจริงๆ
เมื่อทดสอบส่วนย่อยของแอป คุณ
ระบุ ActivityResultRegistry
ทดสอบโดยใช้
FragmentFactory
จึงจะผ่าน
ใน ActivityResultRegistry
ไปยังตัวสร้างของส่วนย่อย
เช่น ส่วนย่อยที่ใช้สัญญา TakePicturePreview
เพื่อสร้าง
ภาพขนาดย่อ
ของรูปภาพ อาจเขียนได้ดังนี้
Kotlin
class MyFragment( private val registry: ActivityResultRegistry ) : Fragment() { val thumbnailLiveData = MutableLiveData<Bitmap?> val takePicture = registerForActivityResult(TakePicturePreview(), registry) { bitmap: Bitmap? -> thumbnailLiveData.setValue(bitmap) } // ... }
Java
public class MyFragment extends Fragment { private final ActivityResultRegistry mRegistry; private final MutableLiveData<Bitmap> mThumbnailLiveData = new MutableLiveData(); private final ActivityResultLauncher<Void> mTakePicture = registerForActivityResult(new TakePicturePreview(), mRegistry, new ActivityResultCallback<Bitmap>() { @Override public void onActivityResult(Bitmap thumbnail) { mThumbnailLiveData.setValue(thumbnail); } }); public MyFragment(@NonNull ActivityResultRegistry registry) { super(); mRegistry = registry; } @VisibleForTesting @NonNull ActivityResultLauncher<Void> getTakePicture() { return mTakePicture; } @VisibleForTesting @NonNull LiveData<Bitmap> getThumbnailLiveData() { return mThumbnailLiveData; } // ... }
เมื่อสร้าง ActivityResultRegistry
เฉพาะการทดสอบ คุณต้องใช้
เวลา
onLaunch()
การทดสอบของคุณแทนการโทรหา startActivityForResult()
สามารถเรียกใช้
dispatchResult()
โดยตรง ซึ่งจะให้ผลลัพธ์ที่คุณต้องการใช้ในการทดสอบ
val testRegistry = object : ActivityResultRegistry() {
override fun <I, O> onLaunch(
requestCode: Int,
contract: ActivityResultContract<I, O>,
input: I,
options: ActivityOptionsCompat?
) {
dispatchResult(requestCode, expectedResult)
}
}
การทดสอบที่สมบูรณ์สร้างผลลัพธ์ที่คาดหวัง และสร้างการทดสอบ
ActivityResultRegistry
ส่งไปยังส่วนย่อย เรียก Launcher
โดยตรงหรือใช้ API ทดสอบอื่นๆ เช่น Espresso แล้วยืนยัน
ผลลัพธ์:
@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 <I, O> onLaunch(
requestCode: Int,
contract: ActivityResultContract<I, O>,
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
และอินพุต แล้วสร้าง Intent
ที่
ถูกใช้แล้ว
กับ startActivityForResult()
สัญญาแต่ละฉบับต้องดำเนินการ
parseResult()
ซึ่งจะสร้างเอาต์พุตจาก resultCode
ที่ระบุ เช่น
Activity.RESULT_OK
หรือ Activity.RESULT_CANCELED
และ Intent
สัญญาสามารถนำมาใช้งานได้
getSynchronousResult()
หากเป็นไปได้ที่จะกำหนดผลลัพธ์สำหรับข้อมูลอินพุต
จำเป็นต้องโทรหา createIntent()
เริ่มกิจกรรมอื่น และใช้
parseResult()
เพื่อสร้างผลลัพธ์
ตัวอย่างต่อไปนี้แสดงวิธีการสร้าง ActivityResultContract
Kotlin
class PickRingtone : ActivityResultContract<Int, Uri?>() { 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<Integer, Uri> { @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
เป็นส่วนหนึ่งของ Callback
ดังที่ปรากฏในตัวอย่างต่อไปนี้
Kotlin
val startForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult -> if (result.resultCode == Activity.RESULT_OK) { val intent = result.data // 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<Intent> mStartForResult = registerForActivityResult(new StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { Intent intent = result.getData(); // 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)); } }); }