ActivityResultLauncher: มันคืออะไรและวิธีใช้งาน

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-10 เวลาอ่าน: 9 นาที

ActivityResultLauncher เป็นส่วนประกอบของ Android Activity Result API ที่ถูกนำเสนอในเวอร์ชัน Activity 1.2.0 ของไลบรารี androidx.activity มันแทนที่เมธอด startActivityForResult และ onActivityResult ที่เลิกใช้แล้ว ซึ่งเป็นส่วนหนึ่งของ Android SDK ตั้งแต่เริ่มก่อตั้ง ตาม Android Developers (2024) API ใหม่ช่วยขจัดปัญหาการผูกพันอย่างแน่นแฟ้นกับ Activity และการขาดความปลอดภัยของชนิดข้อมูล ActivityResultLauncher จะถูกลงทะเบียนล่วงหน้าและใช้ Contract สำหรับการกำหนดชนิดข้อมูลอินพุตและเอาต์พุตอย่างเข้มงวด

ประเด็นสำคัญ

  • ActivityResultLauncher — API ใหม่สำหรับรับผลลัพธ์จาก Activity แทนที่ startActivityForResult ที่เลิกใช้แล้ว
  • Contract — ออบเจ็กต์ที่กำหนดชนิดข้อมูลอินพุตและเอาต์พุตสำหรับสถานการณ์เฉพาะ
  • การลงทะเบียน ทำผ่าน registerForActivityResult ก่อนเรียก launch
  • Callback จะถูกเรียกหลังจาก Activity เป้าหมายเสร็จสิ้นพร้อมผลลัพธ์
  • API พร้อมใช้งาน สำหรับ Activity, Fragment และ Compose ตั้งแต่ Activity 1.2.0

ActivityResultLauncher คืออะไร

ActivityResultLauncher เป็นคลาสจากแพ็คเกจ androidx.activity.result ที่ให้กลไกที่ปลอดภัยด้านชนิดข้อมูลสำหรับการเริ่ม Activity และรับผลลัพธ์ Launcher ถูกสร้างขึ้นผ่านเมธอด registerForActivityResult ซึ่งรับพารามิเตอร์สองตัว: Contract (อธิบายชนิดอินพุตและเอาต์พุต) และ ActivityResultCallback (ตัวจัดการผลลัพธ์) หลังจากการลงทะเบียน launcher พร้อมที่จะถูกเรียกผ่านเมธอด launch

ความแตกต่างหลัก จาก API เก่า คือการแยกการลงทะเบียนและการเริ่มทำงาน การลงทะเบียนจะดำเนินการในขั้นตอนการเริ่มต้น (Activity.onCreate หรือ Fragment.onCreate) callback จะถูกผูกกับ launcher เพียงครั้งเดียวและรับประกันว่าจะทำงานเมื่อผลลัพธ์กลับมา ซึ่งช่วยขจัดปัญหาที่ onActivityResult ทำงานในลำดับที่ไม่คาดคิดหรือบน Activity ที่ถูกทำลาย

ActivityResultLauncher รองรับ ทุกสถานการณ์ ที่เคยถูกจัดการผ่าน onActivityResult: การเริ่มกล้อง แกลเลอรี การขอรายชื่อติดต่อ สิทธิ์ และ Activity ที่กำหนดเอง นอกจากนี้ API ยังสามารถขยายได้: นักพัฒนาสามารถสร้าง Contract ที่กำหนดเองสำหรับสถานการณ์การแลกเปลี่ยนข้อมูลเฉพาะระหว่าง Activity

ทำไม Activity Result API จึงแทนที่ startActivityForResult

startActivityForResult เป็นส่วนหนึ่งของ Android SDK ตั้งแต่ API Level 1 (2008) และยังคงเป็นวิธีหลักในการรับผลลัพธ์จาก Activity มานานกว่า 12 ปี อย่างไรก็ตาม เมธอดนี้มีข้อบกพร่องพื้นฐานที่ Google แก้ไขใน Activity Result API มาดูปัญหาหลักและวิธีที่ API ใหม่แก้ปัญหาเหล่านี้กัน

ปัญหา 1: การผูกพันอย่างแน่นแฟ้นกับ Activity

เมธอด startActivityForResult ผูกติดกับ Activity และ Fragment ผ่าน requestCode — จำนวนเต็มตามอำเภอใจที่ส่งไปยัง onActivityResult นักพัฒนาต้องจับคู่รหัสกับการทำงานที่เริ่มต้นด้วยตนเอง ซึ่งนำไปสู่ข้อผิดพลาดในการนำโค้ดกลับมาใช้ใหม่และการสืบทอด ActivityResultLauncher กำจัด requestCode อย่างสมบูรณ์: callback จะถูกผูกกับ launcher เฉพาะในเวลาที่ลงทะเบียนและจะถูกเรียกสำหรับ launcher นั้นเท่านั้น

ปัญหา 2: การสูญเสียผลลัพธ์เมื่อหมุนหน้าจอ

ระหว่าง การเปลี่ยนแปลงการกำหนดค่า (การหมุนหน้าจอ การเปลี่ยนภาษา) Activity ถูกสร้างใหม่และ onActivityResult อาจไม่ทำงาน — callback สูญหาย Activity Result API จะบันทึกและกู้คืนสถานะ launcher โดยอัตโนมัติผ่าน SavedStateRegistry เพื่อรับประกันว่าจะได้รับผลลัพธ์แม้หลังจากการสร้าง Activity ใหม่

ปัญหา 3: การขาดความปลอดภัยของชนิดข้อมูล

API เก่าส่ง ผลลัพธ์ ผ่าน Intent พร้อม Bundle ซึ่งคีย์และชนิดข้อมูลไม่ถูกตรวจสอบโดยคอมไพเลอร์ Activity Result API ใช้ Contract — อินเทอร์เฟซทั่วไปที่กำหนดชนิดข้อมูลอินพุต (I) และชนิดผลลัพธ์ (O) ข้อผิดพลาดประเภทไม่ตรงกันจะถูกตรวจจับในเวลาคอมไพล์ ไม่ใช่เวลารันไทม์

คุณลักษณะstartActivityForResultActivityResultLauncher
RequestCodeต้องการการจัดการด้วยตนเองอัตโนมัติ ไม่ต้องการ
ความปลอดภัยของชนิดไม่มีContract ทั่วไป
การบันทึกเมื่อหมุนสูญหายSavedStateRegistry
API ขั้นต่ำAPI Level 1Activity 1.2.0
การใช้งานใน Composeไม่รองรับrememberLauncherForActivityResult

คอนแทรกต์หลักของ Activity Result API

Contract คืออินเทอร์เฟซ ActivityResultContract<I, O> ซึ่งกำหนดวิธีเริ่ม Activity และวิธีตีความผลลัพธ์ Google มีชุดคอนแทรกต์ในตัวสำหรับสถานการณ์ทั่วไปที่ครอบคลุมความต้องการของนักพัฒนาส่วนใหญ่

StartIntentSenderForResult

StartIntentSenderForResult — คอนแทรกต์พื้นฐานสำหรับการเริ่ม IntentSender ใช้ในสถานการณ์ของระบบ เช่น เมื่ออนุญาตผ่าน Google Sign-In หรือชำระเงินผ่าน Google Pay พารามิเตอร์อินพุตคือ PendingIntent เอาต์พุตคือ ActivityResult พร้อมรหัสและ Intent

RequestMultiplePermissions

RequestMultiplePermissions — คอนแทรกต์สำหรับขอสิทธิ์หลายรายการพร้อมกันบน Android 6.0+ พารามิเตอร์อินพุตคืออาร์เรย์ String ที่มีชื่อสิทธิ์ เอาต์พุตคือ Map<String, Boolean> พร้อมผลลัพธ์ของแต่ละคำขอ ก่อนหน้านี้ต้องมีการแยกวิเคราะห์ด้วยตนเองใน onRequestPermissionsResult พร้อมการจับคู่รหัสคำขอ

TakePicture และ TakeVideo

TakePicture — คอนแทรกต์สำหรับถ่ายภาพผ่านกล้องของระบบ อินพุตคือ Uri ที่จะบันทึกภาพ เอาต์พุตคือ Boolean (สำเร็จ) TakeVideo ทำงานคล้ายกันกับวิดีโอ คอนแทรกต์เหล่านี้แทนที่ MediaStore.ACTION_IMAGE_CAPTURE ที่เลิกใช้แล้วซึ่งมีพฤติกรรมไม่เสถียรบนอุปกรณ์ต่างกัน

GetContent และ OpenDocument

GetContent — คอนแทรกต์สำหรับเลือกเนื้อหาผ่านตัวเลือกระบบ อินพุตคือชนิด MIME (เช่น image/*) เอาต์พุตคือ Uri ของไฟล์ที่เลือก OpenDocument แตกต่างตรงที่รองรับการเลือกหลายรายการและการกรองตามชนิดเอกสาร คอนแทรกต์ทั้งสองทำงานผ่าน SAF (Storage Access Framework)

CreateDocument และ OpenDocumentTree

CreateDocument — คอนแทรกต์สำหรับสร้างเอกสารใหม่ผ่านไดอะล็อกระบบ ผู้ใช้เลือกชื่อและโฟลเดอร์ ระบบส่งคืน Uri สำหรับการเขียน OpenDocumentTree ให้การเข้าถึงไดเรกทอรีทั้งหมด — ผู้ใช้เลือกโฟลเดอร์ และแอปจะได้รับ tree-uri สำหรับอ่านและเขียนไฟล์ทั้งหมดภายใน

การใช้งานใน Activity และ Fragment

รูปแบบพื้นฐาน สำหรับการใช้ ActivityResultLauncher ใน Android ดั้งเดิมประกอบด้วยสองขั้นตอน: การลงทะเบียนผ่าน registerForActivityResult ในขั้นตอนการเริ่มต้นและการเรียก launch เพื่อตอบสนองต่อการกระทำของผู้ใช้ มาดูตัวอย่างทั่วไปของการเลือกรูปภาพจากแกลเลอรี

การลงทะเบียนและเริ่มทำงานใน Activity

ลงทะเบียน launcher ใน onCreate ของ Activity — ซึ่งรับประกันว่า callback พร้อมก่อนการเรียกใดๆ ที่อาจเกิดขึ้น อย่าลงทะเบียน launcher ทันทีก่อนเริ่มทำงาน — ซึ่งละเมิดสัญญา API และอาจทำให้สูญเสียผลลัพธ์เมื่อ Activity ถูกสร้างใหม่

kotlin
class MainActivity : AppCompatActivity() {
    private val pickImageLauncher =
        registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
            uri?.let { binding.imageView.setImageURI(it) }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        pickImageLauncher.launch("image/*")
    }
}

การใช้งานใน Fragment

ใน Fragment การลงทะเบียนจะดำเนินการใน onCreate, onAttach หรือการเริ่มต้นใน onCreateView FragmentActivity ส่ง launcher ผ่าน Activity หลัก ดังนั้นผลลัพธ์จะถูกประมวลผลภายใน Fragment ไม่ใช่ใน Activity ซึ่งช่วยปรับปรุงการห่อหุ้มเมื่อเทียบกับ onActivityResult ที่ผลลัพธ์ทั้งหมดจาก Fragment ทั้งหมดถูกรวบรวมในเมธอดเดียวของ Activity

kotlin
class ProfileFragment : Fragment() {
    private val cameraLauncher =
        registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
            if (success) { updateProfilePhoto() }
        }

    fun takePhoto(photoUri: Uri) {
        cameraLauncher.launch(photoUri)
    }
}

ActivityResultLauncher ใน Jetpack Compose

Jetpack Compose มีฟังก์ชัน composable พิเศษสำหรับ Activity Result API — rememberLauncherForActivityResult แตกต่างจากวิธีการดั้งเดิม ใน Compose launcher ถูกสร้างเป็นออบเจ็กต์ที่ผูกกับวงจรชีวิตของ composable ผ่าน remember ซึ่งช่วยให้ใช้ Activity Result API ในรูปแบบประกาศได้อย่างสมบูรณ์โดยไม่ต้องเข้าถึง Activity หรือ Fragment โดยตรง

rememberLauncherForActivityResult

rememberLauncherForActivityResult รับ Contract และ callback และส่งคืน ActivityResultLauncher Launcher จะถูกเก็บรักษาไว้ระหว่างการประกอบใหม่และถูกล้างโดยอัตโนมัติเมื่อออกจากองค์ประกอบ การเรียก launch เกิดขึ้นเพื่อตอบสนองต่อเหตุการณ์ — ตัวอย่างเช่น การคลิกปุ่มหรือการเปลี่ยนแปลงสถานะ

kotlin
@Composable
fun PhotoPicker() {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(
        ActivityResultContracts.GetContent()
    ) { uri -> handleImage(uri) }

    Button(onClick = { launcher.launch("image/*") }) {
        Text("เลือกรูปภาพ")
    }
}

การจัดการสิทธิ์ใน Compose

การขอสิทธิ์ ใน Compose ก็ทำผ่าน rememberLauncherForActivityResult ด้วยคอนแทรกต์ RequestPermission หรือ RequestMultiplePermissions Google แนะนำให้ใช้ accompanist-permissions แต่ภายในก็ใช้ Activity Result API เช่นกัน ในการติดตามสถานะสิทธิ์ ควรเก็บสถานะใน remember หรือ ViewModel

ข้อผิดพลาดทั่วไปและแนวปฏิบัติที่ดีที่สุด

Activity Result API ขจัดปัญหามากมายของวิธีการเก่า แต่การใช้งานที่ไม่ถูกต้องอาจนำไปสู่ข้อผิดพลาดรูปแบบใหม่ มาดูปัญหาที่พบบ่อยที่สุดและวิธีหลีกเลี่ยง

ข้อผิดพลาด: การลงทะเบียนภายใน lambda หรือ coroutine

การลงทะเบียน launcher ต้องดำเนินการระหว่างการเริ่มต้นคอมโพเนนต์ — ใน onCreate ของ Activity หรือตัวเริ่มต้น Fragment หากคุณลงทะเบียน launcher ภายใน lambda, callback หรือ coroutine เมื่อ Activity ถูกสร้างใหม่ การลงทะเบียนอาจถูกดำเนินการอีกครั้งและ launcher เก่าจะสูญเสียการเชื่อมต่อกับผลลัพธ์

ข้อผิดพลาด: การลงทะเบียน launcher หลายตัวด้วยคีย์เดียวกัน

แต่ละ launcher จะได้รับคีย์เฉพาะสำหรับการบันทึกสถานะ หากคุณลงทะเบียน launcher สองตัวด้วย Contract เดียวกันในคอมโพเนนต์เดียว SavedStateRegistry อาจเขียนทับสถานะของตัวหนึ่งด้วยอีกตัวหนึ่ง Android Studio เตือนเกี่ยวกับเรื่องนี้ผ่านกฎ lint UnnecessaryRegisterForActivityResult แต่ควรควบคุมความเป็นเอกลักษณ์ด้วยตนเองจะดีกว่า

แนวปฏิบัติที่ดีที่สุด: จัดการผลลัพธ์ null เสมอ

ผู้ใช้อาจ ยกเลิก การกระทำ — กดปุ่มกลับของระบบ ย่อแอป หรือสลับไปยังแอปอื่น ในกรณีนี้ callback จะได้รับ null หรือ ActivityResult พร้อม RESULT_CANCELED ตรวจสอบผลลัพธ์ว่าเป็น null ก่อนใช้งานเสมอเพื่อหลีกเลี่ยง NullPointerException

แนวปฏิบัติที่ดีที่สุด: Contract ที่กำหนดเองสำหรับตรรกะที่นำกลับมาใช้ใหม่ได้

หากแอปของคุณเริ่ม สถานการณ์ที่คล้ายกัน บ่อยครั้ง — เช่น การเลือกรายชื่อติดต่อและส่งคืนชื่อและเบอร์โทรศัพท์ — ให้สร้าง Contract ที่กำหนดเอง ซึ่งช่วยเพิ่มความสามารถในการอ่านโค้ดและอนุญาตให้เปลี่ยนแปลงตรรกะการเริ่มและการจัดการผลลัพธ์จากส่วนกลาง

kotlin
class PickContactContract : ActivityResultContract<Void, ContactData?>() {
    override fun createIntent(context: Context, input: Void?) =
        Intent(Intent.ACTION_PICK).setType(ContactsContract.Contacts.CONTENT_TYPE)

    override fun parseResult(resultCode: Int, intent: Intent?) =
        intent?.data?.let { queryContact(it) }
}

คำถามที่พบบ่อย

สามารถใช้ ActivityResultLauncher ใน ViewModel ได้หรือไม่?

ไม่ — ActivityResultLauncher ต้องการบริบทของ Activity หรือ Fragment สำหรับการลงทะเบียน ใช้ ViewModel สำหรับเก็บสถานะเท่านั้น และสร้าง launcher ใน Activity หรือ Fragment แล้วส่งผลลัพธ์ไปยัง ViewModel

ต้องใช้ SDK ขั้นต่ำเท่าใดสำหรับ Activity Result API?

Activity Result API พร้อมใช้งานตั้งแต่ไลบรารี activity-ktx 1.2.0 SDK ขั้นต่ำคือ API Level 14 (Android 4.0) แต่คอนแทรกต์ส่วนใหญ่ทำงานบน API Level 19+ เท่านั้น

จะเกิดอะไรขึ้นหากเรียก launch สองครั้งก่อนได้รับผลลัพธ์?

การเรียกซ้ำ launch ก่อนที่การดำเนินการแรกจะเสร็จสมบูรณ์จะถูกละเว้น Activity Result API ไม่รองรับการเริ่มพร้อมกัน — รอ callback จากการดำเนินการแรกก่อนทำการเรียกใหม่

จะแทนที่ onActivityResult ในโค้ดเก่าได้อย่างไร?

การย้าย ทำได้โดยแทนที่การเรียก startActivityForResult ด้วย registerForActivityResult โดยใช้ Contract ที่เหมาะสม ลบ onActivityResult และจัดการผลลัพธ์ใน callback ของ launcher Google มีคู่มือการย้ายในเอกสาร Android Developers

ActivityResultLauncher ทำงานกับไลบรารีอย่าง ML Kit หรือ Barcode Scanner ได้หรือไม่?

ได้ หลายไลบรารีรองรับการรวมผ่าน ActivityResultContracts ตัวอย่างเช่น ML Kit Barcode Scanner ใช้ StartIntentSenderForResult เพื่อเริ่มสแกนเนอร์ ตรวจสอบเอกสารของไลบรารีนั้นๆ

สรุป

  • ActivityResultLauncher — API ที่ปลอดภัยด้านชนิดข้อมูลสมัยใหม่แทนที่ startActivityForResult ที่เลิกใช้แล้ว
  • Contract กำหนดชนิดข้อมูลอินพุตและเอาต์พุต ขจัดการจับคู่ requestCode ด้วยตนเอง
  • การลงทะเบียน ดำเนินการในขั้นตอนการเริ่มต้น ผลลัพธ์รับประกันว่าจะถูกส่งผ่าน callback
  • คอนแทรกต์ในตัว ครอบคลุมกล้อง แกลเลอรี สิทธิ์ เอกสาร และรายชื่อติดต่อ
  • Jetpack Compose ใช้ rememberLauncherForActivityResult เพื่อทำงานกับ API
  • Contract ที่กำหนดเอง ช่วยให้สามารถนำตรรกะการเริ่มกลับมาใช้ใหม่ระหว่างคอมโพเนนต์
  • API รักษาสถานะ ระหว่างการเปลี่ยนแปลงการกำหนดค่าผ่าน SavedStateRegistry

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม