Back Press Handling হল Android-এ সিস্টেম ব্যাক বাটন আটকানো এবং প্রক্রিয়াকরণের একটি প্রক্রিয়া, যা নির্ধারণ করে এটি চাপলে কী পদক্ষেপ নেওয়া হয়। Android Developers (2024) অনুসারে, Android 11 থেকে ঐতিহ্যগত onBackPressed() পদ্ধতি OnBackPressedDispatcher দ্বারা প্রতিস্থাপিত হয়েছে। নতুন API পদানুক্রমের যেকোনো স্তরের উপাদানগুলিকে চাপ আটকানোর অনুমতি দেয়, শুধু Activity নয়। মূল পার্থক্য — অগ্রাধিকার সহ একটি শৃঙ্খলে একাধিক callback-এর সমর্থন।
মূল বিষয়
Back Press Handling হল Android-এর একটি সিস্টেম প্রক্রিয়া যা নির্ধারণ করে ব্যবহারকারী যখন হার্ডওয়্যার বা সফ্টওয়্যার ব্যাক বাটন চাপলে কী হয়। প্রসঙ্গের উপর নির্ভর করে, চাপটি: বর্তমান স্ক্রিন বন্ধ করে আগেরটিতে ফিরতে পারে, কীবোর্ড লুকাতে পারে, Drawer বা Bottom Sheet বন্ধ করতে পারে, ব্যবহারকারী যদি রুট স্ক্রিনে থাকে তবে অ্যাপ থেকে প্রস্থান করতে পারে।
ব্যাক বাটনের আচরণ Android-এর প্রতিটি সংস্করণের সাথে বিবর্তিত হয়েছে। Android 10 অঙ্গভঙ্গি নেভিগেশন চালু করেছে, Android 11 OnBackPressedDispatcher-কে মানক API হিসেবে চালু করেছে, এবং Android 13 উন্নত predictive back gesture সমর্থন এনেছে, যেখানে সিস্টেম কার্য actually সম্পাদনের আগে একটি ট্রানজিশন অ্যানিমেশন দেখায়। Google সমস্ত ডিভাইস জুড়ে অনুমানযোগ্য এবং সামঞ্জস্যপূর্ণ Back Press আচরণের দিকে ধারাবাহিকভাবে এগোচ্ছে।
ব্যাক বাটনের সঠিক পরিচালনা একটি Android অ্যাপে UX-এর একটি গুরুত্বপূর্ণ উপাদান। ব্যবহারকারী আশা করে যে ব্যাক চাপলে সে নেভিগেশন স্ট্যাকের আগের স্ক্রিনে ফিরে যাবে, অপ্রত্যাশিতভাবে অ্যাপ বন্ধ হবে না। এই প্রত্যাশা লঙ্ঘন করা Google Play-তে নেতিবাচক পর্যালোচনা এবং কম রেটিংয়ের অন্যতম প্রধান কারণ।
Android-এ Back Press API-এর ইতিহাস প্ল্যাটফর্মের সামগ্রিক বিবর্তন প্রতিফলিত করে: Activity-তে একটি সহজ পদ্ধতি থেকে lifecycle সমর্থন এবং Compose সহ একটি নমনীয় callback সিস্টেম পর্যন্ত। আসুন উন্নয়নের তিনটি ধাপ দেখি।
প্রথম Android API থেকেই, ব্যাক বাটন Activity ক্লাসের onBackPressed পদ্ধতিতে পরিচালিত হত। ডেভেলপার এই পদ্ধতি ওভাররাইড করে নিজের লজিক লিখত। সমস্যা ছিল যে Fragment এবং View চাপ আটকাতে পারত না — সমস্ত নিয়ন্ত্রণ Activity-র মাধ্যমে যেত। এর ফলে স্ফীত Activity এবং জটিল if-else শৃঙ্খল তৈরি হত কে চাপটি পরিচালনা করবে তা নির্ধারণ করতে।
Activity 1.0.0 (AndroidX) দিয়ে, Google OnBackPressedDispatcher চালু করেছে। এটি একটি কেন্দ্রীয় ডিসপ্যাচার যা যেকোনো উপাদান — Activity, Fragment, Dialog, কাস্টম View — থেকে callback গ্রহণ করে। Callback একটি ক্রমে (priority-র মাধ্যমে) নিবন্ধিত হয় এবং গতিশীলভাবে যোগ বা সরানো যায়। OnBackPressedDispatcher পুরানো onBackPressed-এর আগে আহ্বান করা হয়, যা Activity দ্বারা পরিচালনার আগে চাপ আটকানোর অনুমতি দেয়।
Android 13 predictive back gesture চালু করেছে — একটি সিস্টেম অ্যানিমেশন যা দেখায় ব্যবহারকারী অঙ্গভঙ্গি সম্পূর্ণ করার আগে ব্যাক চাপ কোথায় নিয়ে যাবে। এই অ্যানিমেশন সমর্থন করতে, ডেভেলপারদের OnBackPressedDispatcher ব্যবহার করতে হবে এবং isEnabled প্রপার্টির মাধ্যমে নির্দেশ করতে হবে যে callback সিস্টেম অ্যানিমেশন সমর্থন করে কিনা। যদি callback predictive animation সমর্থন না করে, সিস্টেম একটি ডিফল্ট অ্যানিমেশন দেখায় যা অ্যাপ প্রসঙ্গের সাথে মেলে না।
| API | সর্বনিম্ন SDK | Fragment সমর্থন | Predictive Back |
|---|---|---|---|
| onBackPressed | API Level 1 | Activity-র মাধ্যমে | না |
| OnBackPressedDispatcher | Activity 1.0.0 | সরাসরি | আংশিক |
| OnBackPressedDispatcher + lifecycle | Activity 1.3.0 | Lifecycle-aware | সম্পূর্ণ |
OnBackPressedDispatcher হল নতুন Back Press API-এর মূল। এটি callback-এর একটি শৃঙ্খল পরিচালনা করে, প্রথমটি ইভেন্ট পরিচালনা না করা পর্যন্ত সেগুলিকে ক্রমান্বয়ে আহ্বান করে। যদি কোনো callback চাপ না পরিচালনা করে, ডিসপ্যাচার ডিফল্ট পদক্ষেপ সম্পাদন করে — Activity-র জন্য finish() বা Navigation Component-এর জন্য popBackStack() কল করা।
একটি callback addCallback-এর মাধ্যমে LifecycleOwner এবং OnBackPressedCallback অবজেক্টের সাথে নিবন্ধিত হয়। Callback-এর একটি isEnabled প্রপার্টি থাকে — যদি এটি false-এ সেট করা হয়, callback এড়িয়ে যাওয়া হয়। অগ্রাধিকারের জন্য, আপনি 0 (সর্বনিম্ন) থেকে Integer.MAX_VALUE পর্যন্ত মান পাঠাতে পারেন। Fragment Activity Result API জীবনচক্র-আবদ্ধ স্বয়ংক্রিয় callback নিবন্ধনের জন্য এই প্রক্রিয়া ব্যবহার করে।
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(enabled = true) {
override fun handleOnBackPressed() {
if (isDrawerOpen) { closeDrawer() }
else { isEnabled = false; onBackPressed() }
}
}
onBackPressedDispatcher.addCallback(this, callback)
}
Callback স্বয়ংক্রিয়ভাবে সরানো হয় যখন LifecycleOwner DESTROYED অবস্থায় যায়। এটি স্ক্রিন রোটেশনের সময় callback লিকের পুরানো সমস্যা সমাধান করে। যদি একটি Fragment-এ callback যোগ করা হয়, Fragment ধ্বংস হলে এটি সরানো হবে বলে নিশ্চিত। অস্থায়ীভাবে callback নিষ্ক্রিয় করতে, isEnabled প্রপার্টি ব্যবহার করুন — আপনি এটি সরানো এবং পুনরায় নিবন্ধন ছাড়াই টগল করতে পারেন।
কল ক্রম যোগ করার ক্রমের বিপরীত: শেষ যোগ করা callback প্রথম নিয়ন্ত্রণ পায়। এটি বোধগম্য কারণ সবচেয়ে গভীরভাবে নেস্টেড UI উপাদান (উদাহরণস্বরূপ, Fragment-এর ভিতরে Bottom Sheet) তার প্যারেন্ট Fragment-এর আগে চাপ পরিচালনা করা উচিত। যদি গভীরতম callback চাপ না পরিচালনা করে (isEnabled = false), নিয়ন্ত্রণ শৃঙ্খলের পরবর্তীটিতে যায়।
Fragment API requireActivity().onBackPressedDispatcher পদ্ধতির মাধ্যমে OnBackPressedDispatcher-এর সাথে নিজস্ব একীকরণ প্রদান করে। Fragment 1.2.0 থেকে শুরু করে, প্রতিটি Fragment নিজস্ব callback নিবন্ধন করতে পারে, যা স্বয়ংক্রিয়ভাবে Fragment জীবনচক্রের সাথে আবদ্ধ হয় এবং ধ্বংস হলে সরানো হয়।
Fragment-এ callback নিবন্ধন onCreate, onViewCreated বা এমনকি View-তেই করা হয় — গুরুত্বপূর্ণ হল LifecycleOwner (Fragment) সক্রিয়। যখন Fragment STARTED অবস্থায় যায়, callback সক্রিয় হয়; STOPPED হলে, এটি নিষ্ক্রিয় হয়। এটি নিশ্চিত করে যে লুকানো Fragment (ViewPager-এ) ব্যাক চাপ পরিচালনা করবে না।
class EditorFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
showDiscardDialog()
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}
}
ডায়ালগ এবং BottomSheets বন্ধ করার জন্য স্বয়ংক্রিয়ভাবে ব্যাক চাপ আটকায়। বন্ধ করার আগে অতিরিক্ত পদক্ষেপ নেওয়ার প্রয়োজন হলে — উচ্চ অগ্রাধিকার সহ একটি callback নিবন্ধন করুন। গুরুত্বপূর্ণ: আপনি যদি Dialog-এ setCancelable(false) সেট করেন, callback সক্রিয় হবে না — এটি সিস্টেম আচরণ।
Jetpack Compose BackHandler composable ফাংশনের মাধ্যমে ব্যাক বাটন পরিচালনার জন্য একটি ঘোষণামূলক API প্রদান করে। BackHandler enabled (বুলিয়ান অবস্থা) এবং onBack — চাপলে আহ্বান করা callback গ্রহণ করে। যদি enabled = false হয়, চাপটি শৃঙ্খলে আরও এগিয়ে যায়।
BackHandler স্বয়ংক্রিয়ভাবে প্যারেন্ট Activity-র OnBackPressedDispatcher-এ OnBackPressedCallback নিবন্ধন করে। এটি composable জীবনচক্রকে সম্মান করে: রচনা ছেড়ে গেলে, callback সরানো হয়। enabled অবস্থার সাথে আবদ্ধ হতে পারে — উদাহরণস্বরূপ, কেবল তখনই নিশ্চিতকরণ ডায়ালগ দেখান যখন ফর্মে অসংরক্ষিত পরিবর্তন থাকে।
@Composable
fun EditScreen(hasUnsavedChanges: Boolean) {
BackHandler(enabled = hasUnsavedChanges) {
// Show confirmation dialog
}
Column {
TextField(value = ..., onValueChange = ...)
}
}
Compose-এ Predictive back gesture Compose 1.5.0 থেকে সমর্থিত। BackHandler স্বয়ংক্রিয়ভাবে সিস্টেম ট্রানজিশন অ্যানিমেশন পরিচালনা করে যদি এটি ডিভাইসে সক্ষম থাকে। কাস্টম predictive অ্যানিমেশনের জন্য, predictiveBackHandler মডিফায়ার ব্যবহার করুন, যা 0 থেকে 1 পর্যন্ত অঙ্গভঙ্গির অগ্রগতি ফেরত দেয়।
Back Press Handling সহজ মনে হয়, কিন্তু বাস্তবে ডেভেলপাররা বেশ কিছু পদ্ধতিগত ভুল করে। আসুন Google-এর সুপারিশ এবং সম্প্রদায়ের অভিজ্ঞতার ভিত্তিতে সবচেয়ে সাধারণ সমস্যা এবং তাদের সমাধান দেখি।
handleOnBackPressed-এ সরাসরি finish() কল করা অপ্রত্যাশিত অ্যাপ প্রস্থানের কারণ হতে পারে যদি নেভিগেশন স্ট্যাকে পটভূমির স্ক্রিন থাকে। Activity বন্ধ করার আগে সর্বদা Navigation Component বা Coordinator-এর মাধ্যমে NavController.backStack check করুন।
যদি আপনি LifecycleOwner ছাড়া callback নিবন্ধন করেন (প্যারামিটার ছাড়া পুরানো addCallback ব্যবহার করে), callback চিরকাল বেঁচে থাকবে এবং Activity ইতিমধ্যে ধ্বংস হয়ে গেলে NullPointerException সৃষ্টি করতে পারে। সর্বদা LifecycleOwner-এর সাথে addCallback(this, callback) ব্যবহার করুন।
মোডাল উইন্ডোর (Bottom Sheet, Dialog) জন্য, isEnabled = true কেবল তখনই সেট করুন যখন উইন্ডো দৃশ্যমান। একটি ল্যাম্বডা সহ addCallback ব্যবহার করুন যা উইন্ডোর অবস্থা check করে। Navigation Component NavHost-এর জন্য স্বয়ংক্রিয়ভাবে এটি পরিচালনা করে।
দ্রুত দুবার ব্যাক চাপলে finish()-এর ডাবল কল হতে পারে। 500 ms-এর মধ্যে পুনরাবৃত্ত কল থেকে রক্ষা পেতে ফ্ল্যাগ বা throttleLast ব্যবহার করুন। Navigation Component এই পরিস্থিতি নেটিভভাবে পরিচালনা করে, কিন্তু কাস্টম পরিস্থিতিতে আপনাকে ম্যানুয়ালি সুরক্ষা প্রয়োগ করতে হবে।
private var lastBackPressTime = 0L
override fun handleOnBackPressed() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastBackPressTime > 500) {
lastBackPressTime = currentTime
navigateBack()
}
}
প্রায়শই জিজ্ঞাসিত প্রশ্ন
onBackPressed Android 11-এ deprecated করা হয়েছে কারণ এটি কেবল Activity স্তরে কাজ করে। OnBackPressedDispatcher যেকোনো উপাদানকে (Fragment, Dialog, View) জীবনচক্র সমর্থন সহ একটি একক প্রক্রিয়ার মাধ্যমে চাপ আটকানোর অনুমতি দেয়।
Android সিস্টেম অ্যাপ্লিকেশন এটি পাওয়ার আগে প্রান্ত থেকে সোয়াইপ অঙ্গভঙ্গিকে সিস্টেম ব্যাক চাপে রূপান্তর করে। OnBackPressedDispatcher স্তরে, আপনি এই দুটি ঘটনাকে আলাদা করতে পারেন না — উভয়ই handleOnBackPressed হিসাবে আসে।
হ্যাঁ, OnBackPressedDispatcher পরিচালনা তিন-বাটন ডিভাইস এবং অঙ্গভঙ্গি নেভিগেশনের জন্য একই। আটকানোর কোড নেভিগেশন ধরণের উপর নির্ভর করে না — সিস্টেম নিজেই অঙ্গভঙ্গিকে ডিসপ্যাচার কল-এ রূপান্তর করে।
Android 13+ এমুলেটরের Developer Options-এ predictive back সক্রিয় করুন। ADB ব্যবহার করুন: `adb shell settings put global enable_back_animation 1`। সক্রিয় করার পর, সিস্টেম অ্যানিমেশন ব্যাক চাপলে ট্রানজিশনের পূর্বরূপ দেখাবে।
দুটি শর্ত check করুন: LifecycleOwner-কে STARTED বা RESUMED অবস্থায় থাকতে হবে, এবং callback-এর isEnabled true হতে হবে। যদি উভয় শর্ত পূরণ হয়, নিশ্চিত করুন যে callback সঠিক OnBackPressedDispatcher-এ যোগ করা হয়েছে — Fragment-এ requireActivity().onBackPressedDispatcher ব্যবহার করুন।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন