ActivityResultLauncher হল Android Activity Result API-এর একটি উপাদান, যা androidx.activity লাইব্রেরির Activity 1.2.0 সংস্করণে প্রবর্তিত হয়েছে। এটি startActivityForResult এবং onActivityResult পুরানো পদ্ধতিগুলিকে প্রতিস্থাপন করে, যা Android SDK-এর সৃষ্টির পর থেকে এর অংশ ছিল। Android Developers (2024) অনুসারে, নতুন API Activity-এর সাথে শক্ত সংযোগ এবং টাইপ নিরাপত্তার অভাবের সমস্যাগুলি দূর করে। ActivityResultLauncher আগে থেকেই নিবন্ধিত হয় এবং ইনপুট ও আউটপুট ডেটার কঠোর টাইপিংয়ের জন্য Contract ব্যবহার করে।
মূল বিষয়
ActivityResultLauncher হল androidx.activity.result প্যাকেজের একটি ক্লাস যা একটি Activity শুরু করার এবং ফলাফল পাওয়ার জন্য টাইপ-নিরাপদ প্রক্রিয়া প্রদান করে। Launcher registerForActivityResult পদ্ধতির মাধ্যমে তৈরি করা হয়, যা দুটি প্যারামিটার নেয়: Contract (ইনপুট এবং আউটপুট প্রকারগুলি বর্ণনা করে) এবং ActivityResultCallback (ফলাফল হ্যান্ডলার)। নিবন্ধনের পরে, launcher launch পদ্ধতির মাধ্যমে কল করার জন্য প্রস্তুত হয়।
পুরানো API থেকে মূল পার্থক্য হল নিবন্ধন এবং লঞ্চের পৃথকীকরণ। নিবন্ধন প্রারম্ভিক পর্যায়ে (Activity.onCreate বা Fragment.onCreate) সম্পাদিত হয়, callback একবার launcher-এর সাথে আবদ্ধ হয় এবং ফলাফল ফিরে এলে এটি কার্যকর হওয়ার নিশ্চয়তা দেয়। এটি সেই সমস্যা দূর করে যেখানে onActivityResult অপ্রত্যাশিত ক্রমে বা ধ্বংস করা Activity-তে কার্যকর হত।
ActivityResultLauncher সমস্ত পরিস্থিতি সমর্থন করে যা আগে onActivityResult-এর মাধ্যমে পরিচালিত হত: ক্যামেরা, গ্যালারি চালু করা, পরিচিতি, অনুমতি এবং কাস্টম Activities অনুরোধ করা। তাছাড়া, API প্রসারণযোগ্য: ডেভেলপাররা Activities-এর মধ্যে ডেটা বিনিময়ের নির্দিষ্ট পরিস্থিতির জন্য কাস্টম Contract তৈরি করতে পারেন।
startActivityForResult API Level 1 (2008) থেকে Android SDK-এর অংশ ছিল এবং ১২ বছরেরও বেশি সময় ধরে Activity থেকে ফলাফল পাওয়ার প্রধান উপায় ছিল। তবে, এই পদ্ধতিতে মৌলিক ত্রুটি ছিল যা Google Activity Result API-তে সমাধান করেছে। আসুন মূল সমস্যা এবং নতুন API কীভাবে সেগুলি সমাধান করে তা দেখি।
startActivityForResult পদ্ধতি requestCode-এর মাধ্যমে Activity এবং Fragment-এর সাথে আবদ্ধ — একটি নির্বিচার পূর্ণসংখ্যা যা onActivityResult-এ পাঠানো হয়। ডেভেলপার ম্যানুয়ালি কোডটিকে চালু করা অপারেশনের সাথে মেলাত, যা কোড পুনঃব্যবহার এবং উত্তরাধিকারে ত্রুটির দিকে পরিচালিত করত। ActivityResultLauncher সম্পূর্ণরূপে requestCode বাদ দেয়: callback নিবন্ধনের সময় একটি নির্দিষ্ট launcher-এর সাথে আবদ্ধ হয় এবং শুধুমাত্র তার জন্য আহ্বান করা হয়।
কনফিগারেশন পরিবর্তনের সময় (স্ক্রিন ঘোরানো, ভাষা পরিবর্তন), Activity পুনরায় তৈরি হত এবং onActivityResult কার্যকর নাও হতে পারত — callback হারিয়ে যেত। Activity Result API SavedStateRegistry-এর মাধ্যমে স্বয়ংক্রিয়ভাবে launcher অবস্থা সংরক্ষণ এবং পুনরুদ্ধার করে, যা Activity পুনর্নির্মাণের পরেও ফলাফল পাওয়ার নিশ্চয়তা দেয়।
পুরানো API ফলাফল Bundle-সহ Intent-এর মাধ্যমে পাঠাত, যেখানে কী এবং ডেটার প্রকারগুলি কম্পাইলার দ্বারা পরীক্ষা করা হত না। Activity Result API Contract ব্যবহার করে — একটি জেনেরিক ইন্টারফেস যা ইনপুট ডেটা প্রকার (I) এবং ফলাফল প্রকার (O) নির্ধারণ করে। প্রকার অমিল ত্রুটিগুলি রানটাইমের পরিবর্তে কম্পাইল টাইমে ধরা পড়ে।
| বৈশিষ্ট্য | startActivityForResult | ActivityResultLauncher |
|---|---|---|
| RequestCode | ম্যানুয়াল ব্যবস্থাপনা প্রয়োজন | স্বয়ংক্রিয়, প্রয়োজন নেই |
| টাইপ নিরাপত্তা | নেই | জেনেরিক Contract |
| ঘোরানোর সময় সংরক্ষণ | হারিয়ে যায় | SavedStateRegistry |
| সর্বনিম্ন API | API Level 1 | Activity 1.2.0 |
| Compose-এ ব্যবহার | সমর্থিত নয় | rememberLauncherForActivityResult |
Contract হল ActivityResultContract<I, O> ইন্টারফেস, যা নির্ধারণ করে কীভাবে একটি Activity চালু করতে হবে এবং ফলাফলটি কীভাবে ব্যাখ্যা করতে হবে। Google সাধারণ পরিস্থিতির জন্য অন্তর্নির্মিত কন্ট্র্যাক্টের একটি সেট সরবরাহ করে যা বেশিরভাগ ডেভেলপারের প্রয়োজনীয়তা কভার করে।
StartIntentSenderForResult — IntentSender চালু করার জন্য একটি মৌলিক কন্ট্র্যাক্ট। সিস্টেম পরিস্থিতিতে ব্যবহৃত হয়, উদাহরণস্বরূপ Google Sign-In-এর মাধ্যমে অনুমোদন বা Google Pay-এর মাধ্যমে পেমেন্ট করার সময়। ইনপুট প্যারামিটার হল PendingIntent, আউটপুট হল কোড এবং Intent সহ ActivityResult।
RequestMultiplePermissions — Android 6.0+-এ একসাথে একাধিক অনুমতি অনুরোধ করার জন্য কন্ট্র্যাক্ট। ইনপুট প্যারামিটার হল অনুমতি নামের String অ্যারে, আউটপুট হল প্রতিটি অনুরোধের ফলাফল সহ Map<String, Boolean>। আগে এর জন্য onRequestPermissionsResult-এ অনুরোধ কোড মিলানোর সাথে ম্যানুয়াল পার্সিং প্রয়োজন হত।
TakePicture — সিস্টেম ক্যামেরার মাধ্যমে ছবি তোলার জন্য কন্ট্র্যাক্ট। ইনপুট হল Uri যেখানে ছবি সংরক্ষণ করতে হবে, আউটপুট হল Boolean (সফলতা)। TakeVideo ভিডিওর সাথে একইভাবে কাজ করে। এই কন্ট্র্যাক্টগুলি বিভিন্ন ডিভাইসে অস্থির আচরণ সহ পুরানো MediaStore.ACTION_IMAGE_CAPTURE প্রতিস্থাপন করে।
GetContent — সিস্টেম পিকার-এর মাধ্যমে সামগ্রী নির্বাচনের জন্য কন্ট্র্যাক্ট। ইনপুট হল MIME প্রকার (যেমন image/*), আউটপুট হল নির্বাচিত ফাইলের Uri। OpenDocument একাধিক নির্বাচন এবং ডকুমেন্ট প্রকার অনুসারে ফিল্টারিং সমর্থন করে ভিন্ন। উভয় কন্ট্র্যাক্ট SAF (Storage Access Framework)-এর মাধ্যমে কাজ করে।
CreateDocument — সিস্টেম ডায়ালগের মাধ্যমে নতুন ডকুমেন্ট তৈরি করার জন্য কন্ট্র্যাক্ট। ব্যবহারকারী নাম এবং ফোল্ডার নির্বাচন করে, সিস্টেম লেখার জন্য Uri ফেরত দেয়। OpenDocumentTree সম্পূর্ণ ডিরেক্টরিতে অ্যাক্সেস সরবরাহ করে — ব্যবহারকারী একটি ফোল্ডার নির্বাচন করে, এবং অ্যাপ ভিতরের সমস্ত ফাইল পড়তে এবং লিখতে tree-uri পায়।
মৌলিক প্যাটার্ন ক্লাসিক Android-এ ActivityResultLauncher ব্যবহারের জন্য দুটি ধাপ নিয়ে গঠিত: প্রারম্ভিককরণে registerForActivityResult-এর মাধ্যমে নিবন্ধন এবং ব্যবহারকারীর ক্রিয়ার প্রতিক্রিয়ায় launch কল করা। আসুন গ্যালারি থেকে একটি ছবি নির্বাচনের একটি সাধারণ উদাহরণ দেখি।
Launcher Activity-র onCreate-এ নিবন্ধন করুন — এটি নিশ্চিত করে যে কোনও সম্ভাব্য কলের আগে callback প্রস্তুত থাকে। লঞ্চ করার ঠিক আগে কখনও launcher নিবন্ধন করবেন না — এটি API কন্ট্র্যাক্ট লঙ্ঘন করে এবং Activity পুনর্নির্মাণে ফলাফল হারাতে পারে।
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-এ, নিবন্ধন onCreate, onAttach বা onCreateView-এ প্রারম্ভিককরণে করা হয়। FragmentActivity প্যারেন্ট Activity-র মাধ্যমে launcher পাঠায়, তাই ফলাফল Activity-র পরিবর্তে Fragment-এর ভিতরে প্রক্রিয়া করা হয়। এটি onActivityResult-এর তুলনায় এনক্যাপসুলেশন উন্নত করে, যেখানে সমস্ত Fragment-এর সমস্ত ফলাফল একটি Activity পদ্ধতিতে সংগ্রহ করা হত।
class ProfileFragment : Fragment() {
private val cameraLauncher =
registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) { updateProfilePhoto() }
}
fun takePhoto(photoUri: Uri) {
cameraLauncher.launch(photoUri)
}
}
Jetpack Compose Activity Result API-এর জন্য একটি বিশেষ composable ফাংশন সরবরাহ করে — rememberLauncherForActivityResult। ক্লাসিক পদ্ধতির বিপরীতে, Compose-এ launcher remember-এর মাধ্যমে composable জীবনচক্রের সাথে আবদ্ধ একটি অবজেক্ট হিসাবে তৈরি করা হয়। এটি Activity বা Fragment-এ সরাসরি অ্যাক্সেস ছাড়াই সম্পূর্ণরূপে ঘোষণামূলক শৈলীতে Activity Result API ব্যবহার করার অনুমতি দেয়।
rememberLauncherForActivityResult Contract এবং callback নেয়, এবং ActivityResultLauncher ফেরত দেয়। পুনঃসংযোজনের সময় launcher সংরক্ষিত থাকে এবং কম্পোজিশন ছেড়ে গেলে স্বয়ংক্রিয়ভাবে পরিষ্কার হয়ে যায়। launch কল একটি ইভেন্টের প্রতিক্রিয়ায় ঘটে — উদাহরণস্বরূপ, বোতাম ক্লিক বা অবস্থা পরিবর্তন।
@Composable
fun PhotoPicker() {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()
) { uri -> handleImage(uri) }
Button(onClick = { launcher.launch("image/*") }) {
Text("ছবি নির্বাচন করুন")
}
}
Compose-এ অনুমতি অনুরোধও RequestPermission বা RequestMultiplePermissions কন্ট্র্যাক্টের সাথে rememberLauncherForActivityResult-এর মাধ্যমে করা হয়। Google accompanist-permissions ব্যবহার করার পরামর্শ দেয়, তবে এটি অভ্যন্তরীণভাবেও Activity Result API ব্যবহার করে। অনুমতির অবস্থা ট্র্যাক করতে, remember বা ViewModel-এ অবস্থা সংরক্ষণ করা সুবিধাজনক।
Activity Result API পুরানো পদ্ধতির অনেক সমস্যা দূর করেছে, তবে অনুপযুক্ত ব্যবহার নতুন ধরণের ত্রুটির কারণ হতে পারে। আসুন সবচেয়ে সাধারণ সমস্যা এবং সেগুলি এড়ানোর উপায়গুলি দেখি।
Launcher-এর নিবন্ধন উপাদান প্রারম্ভিককরণের সময় করা আবশ্যক — Activity onCreate বা Fragment আরম্ভকর্তায়। যদি আপনি lambda, callback বা coroutine-এর ভিতরে launcher নিবন্ধন করেন, Activity পুনর্নির্মাণে নিবন্ধন আবার করা হতে পারে এবং পুরানো launcher ফলাফলের সাথে সংযোগ হারাবে।
প্রতিটি launcher অবস্থা সংরক্ষণের জন্য একটি অনন্য কী পায়। যদি আপনি একটি উপাদানে একই Contract-এর সাথে দুটি launcher নিবন্ধন করেন, SavedStateRegistry একটির অবস্থা অন্যটির দ্বারা ওভাররাইট করতে পারে। Android Studio lint নিয়ম UnnecessaryRegisterForActivityResult-এর মাধ্যমে এই সম্পর্কে সতর্ক করে, তবে ম্যানুয়ালি স্বতন্ত্রতা নিয়ন্ত্রণ করা ভাল।
ব্যবহারকারী ক্রিয়াটি বাতিল করতে পারে — সিস্টেম ব্যাক বোতাম টিপুন, অ্যাপ ছোট করুন, বা অন্য অ্যাপে স্যুইচ করুন। এই ক্ষেত্রে, callback null বা RESULT_CANCELED-সহ ActivityResult পাবে। NullPointerException এড়াতে ব্যবহারের আগে সর্বদা null-এর জন্য ফলাফল পরীক্ষা করুন।
যদি আপনার অ্যাপ ঘন ঘন অনুরূপ পরিস্থিতি শুরু করে — উদাহরণস্বরূপ, একটি পরিচিতি নির্বাচন করা এবং নাম ও ফোন ফেরত দেওয়া — তাহলে একটি কাস্টম Contract তৈরি করুন। এটি কোড পঠনযোগ্যতা উন্নত করে এবং লঞ্চ ও ফলাফল পরিচালনার যুক্তিতে কেন্দ্রীয় পরিবর্তনের অনুমতি দেয়।
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-এর নিবন্ধনের জন্য Activity বা Fragment প্রসঙ্গ প্রয়োজন। শুধুমাত্র অবস্থা সংরক্ষণের জন্য ViewModel ব্যবহার করুন, এবং Activity বা Fragment-এ launcher তৈরি করুন এবং ফলাফল ViewModel-এ পাঠান।
Activity Result API লাইব্রেরি activity-ktx 1.2.0 থেকে উপলব্ধ। ন্যূনতম SDK হল API Level 14 (Android 4.0), তবে বেশিরভাগ কন্ট্র্যাক্ট শুধুমাত্র API Level 19+ এ কাজ করে।
প্রথম অপারেশন শেষ হওয়ার আগে launch-এর পুনরাবৃত্ত কল উপেক্ষা করা হবে। Activity Result API সমান্তরাল লঞ্চ সমর্থন করে না — নতুন কল করার আগে প্রথম অপারেশন থেকে callback-এর জন্য অপেক্ষা করুন।
মাইগ্রেশন উপযুক্ত Contract-এর সাথে startActivityForResult কলটি registerForActivityResult দিয়ে প্রতিস্থাপন করে করা হয়। onActivityResult সরান এবং launcher callback-এ ফলাফল পরিচালনা করুন। Google Android Developers ডকুমেন্টেশনে একটি মাইগ্রেশন গাইড সরবরাহ করে।
হ্যাঁ, অনেক লাইব্রেরি ActivityResultContracts-এর মাধ্যমে ইন্টিগ্রেশন সমর্থন করে। উদাহরণস্বরূপ, ML Kit Barcode Scanner স্ক্যানার চালু করতে StartIntentSenderForResult ব্যবহার করে। নির্দিষ্ট লাইব্রেরির ডকুমেন্টেশন দেখুন।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন