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 از نسخه API Level 1 (2008) بخشی از Android SDK بود و بیش از 12 سال روش اصلی دریافت نتیجه از Activity باقی ماند. با این حال، این روش دارای نقص‌های اساسی بود که Google در Activity Result API برطرف کرد. بیایید مشکلات اصلی و نحوه حل آنها توسط API جدید را بررسی کنیم.

مشکل 1: اتصال محکم به Activity

متد startActivityForResult از طریق requestCode به Activity و Fragment متصل است — یک عدد صحیح دلخواه که به onActivityResult منتقل می‌شود. توسعه‌دهنده به صورت دستی کد را با عملیات راه‌اندازی شده مطابقت می‌داد که منجر به خطا در استفاده مجدد از کد و وراثت می‌شد. ActivityResultLauncher requestCode را کاملاً حذف می‌کند: callback در مرحله ثبت به launcher خاصی متصل می‌شود و فقط برای آن فراخوانی می‌شود.

مشکل 2: از دست رفتن نتیجه هنگام چرخش صفحه

در تغییرات پیکربندی (چرخش صفحه، تغییر زبان) Activity بازسازی می‌شد و onActivityResult ممکن بود فعال نشود — callback از بین می‌رفت. Activity Result API به طور خودکار وضعیت launcher را از طریق SavedStateRegistry ذخیره و بازیابی می‌کند که دریافت نتیجه را حتی پس از بازسازی Activity تضمین می‌کند.

مشکل 3: عدم امنیت نوع

API قدیمی نتیجه را از طریق Intent با Bundle منتقل می‌کرد که در آن کلیدها و نوع داده‌ها توسط کامپایلر بررسی نمی‌شدند. Activity Result API از Contract استفاده می‌کند — یک رابط generic که نوع داده ورودی (I) و نوع نتیجه (O) را تعیین می‌کند. خطاهای عدم تطابق نوع در مرحله کامپایل شناسایی می‌شوند، نه در زمان اجرا.

ویژگیstartActivityForResultActivityResultLauncher
RequestCodeنیاز به مدیریت دستی داردخودکار، نیاز نیست
امنیت نوعنداردGeneric Contract
ذخیره در چرخشاز بین می‌رودSavedStateRegistry
حداقل APIAPI 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 بسیاری از مشکلات رویکرد قدیمی را برطرف کرد، اما استفاده نادرست از آن می‌تواند منجر به انواع جدیدی از خطاها شود. بیایید رایج‌ترین مشکلات و روش‌های اجتناب از آنها را بررسی کنیم.

خطا: ثبت در داخل لامبدا یا کوروتین

ثبت launcher باید هنگام مقداردهی اولیه مؤلفه انجام شود — در onCreate Activity یا مقداردهنده Fragment. اگر launcher را در داخل لامبدا، callback یا کوروتین ثبت کنید، هنگام بازسازی 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 از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید