प्रश्न और दायरा
आप Activities, Fragments, और Compose स्क्रीन्स वाले एक बड़े Android ऐप का रखरखाव करते हैं। Android 15 से, यूज़र्स सिस्टम बैक प्रीव्यू देख सकते हैं, लेकिन लेगेसी स्क्रीन्स अभी भी onBackPressed या KeyEvent.KEYCODE_BACK के साथ बैक को इंटरसेप्ट करती हैं। इसका परिणाम गायब एनिमेशन, गलत समय पर पुष्टिकरण (confirmation) लॉजिक, या रद्द किए गए जेस्चर के बाद बची हुई स्टेट के रूप में सामने आता है। इंटरव्यूअर एक क्रमिक माइग्रेशन और टेस्ट प्लान चाहता है।
मान लें कि Android 13 और बाद के वर्जनों का समर्थन जारी रहना चाहिए और नेविगेशन स्टैक को एक बार में फिर से नहीं लिखा जा सकता है। Android बैकवर्ड-कंपैटिबल OnBackPressedCallback, या सीधे सिस्टम इंटीग्रेशन की आवश्यकता होने पर प्लेटफ़ॉर्म OnBackInvokedCallback की अनुशंसा करता है। Compose तब PredictiveBackHandler का उपयोग करता है जब जेस्चर प्रोग्रेस की आवश्यकता होती है।
इंटरव्यूअर क्या मूल्यांकन करता है
- क्या आप predictive back को वन-शॉट क्लिक के बजाय प्रीव्यू के बाद कमिट या कैंसलेशन के रूप में मॉडल करते हैं।
- क्या लेगेसी इंटरसेप्शन उन AndroidX कॉलबैक्स में स्थानांतरित होता है जिनकी इनेबल्ड स्टेट UI स्टेट का अनुसरण करती है।
- क्या आप UI एनिमेशन कॉलबैक्स को लॉगिंग या बिज़नेस ऑब्जर्वर्स से अलग करते हैं ताकि सिस्टम एनिमेशन बाधित (consume) न हो।
- क्या आप Compose कॉलबैक क्रम, कैंसलेशन रिस्टोरेशन, और View/Fragment कंपैटिबिलिटी की व्याख्या करते हैं।
- क्या आप Android 15 सिस्टम एनिमेशन, एक्टिविटी-लेवल ऑप्ट-आउट, और दोहराए जाने योग्य डिवाइस टेस्ट को कवर करते हैं।
एक कमज़ोर उत्तर कहता है "AndroidX अपग्रेड करें और एक फ़्लैग चालू करें।" एक मज़बूत उत्तर कॉलबैक ज़िम्मेदारी, आइडम्पोटेंट कैंसलेशन रिकवरी, और एक्टिविटी-दर-एक्टिविटी रोलआउट को परिभाषित करता है।
उत्तर देने से पहले स्पष्टीकरण
- क्या स्क्रीन Fragments/Navigation Component, Compose, या कस्टम Activity बैक स्टैक का उपयोग करती है? यह माइग्रेशन API तय करता है।
- क्या बैक को किसी अनसेव्ड फ़ॉर्म की पुष्टि करनी चाहिए? कॉलबैक को जेस्चर से पहले UI स्टेट का अनुसरण करना चाहिए, न कि इसके पूरा होने के बाद निर्णय लेना चाहिए।
- क्या किसी कस्टम एनिमेशन के लिए जेस्चर प्रोग्रेस की आवश्यकता है? अंतिम इंटरसेप्शन के लिए
BackHandlerका उपयोग करें औरPredictiveBackHandlerका केवल तब उपयोग करें जब प्रोग्रेस की आवश्यकता हो। - क्या टारगेट में Android 15 शामिल है? सिस्टम एनिमेशन और टेस्टिंग अलग हैं, जबकि कंपैटिबिलिटी लेयर को पुराने रिलीज़ को कवर करना चाहिए।
- क्या ऐप एक्टिविटी या स्क्रीन के आधार पर रोल आउट हो सकता है? एक बड़ा ऐप एक्टिविटी-लेवल
enableOnBackInvokedCallbackफ़्लैग का उपयोग कर सकता है।
ये उत्तर डिज़ाइन को बदलते हैं: जब प्रोग्रेस अनावश्यक हो तो एक सरल कॉलबैक रखें, फ़ॉर्म स्टेट के लिए रिस्टोरेशन लागू करें, और प्रत्येक लेगेसी इंटरसेप्शन को हटाने से पहले AndroidX स्क्रीन्स को माइग्रेट करें।
30-सेकंड का उत्तर फ़्रेमवर्क
"मैं प्रत्येक लेगेसी बैक इंटरसेप्शन की सूची बनाता हूँ और कॉलबैक्स को ऑब्जर्वेबल UI स्टेट से बांधता हूँ। मैं onBackPressed और KEYCODE_BACK इंटरसेप्शन को OnBackPressedCallback से बदलता हूँ; मैं सीधे प्लेटफ़ॉर्म इंटीग्रेशन के लिए केवल OnBackInvokedCallback का उपयोग करता हूँ, और Compose में PredictiveBackHandler का उपयोग तब करता हूँ जब मुझे प्रोग्रेस की आवश्यकता होती है। प्रीव्यू केवल रिवर्सिबल विज़ुअल्स को बदलता है। एक कमिट किया गया जेस्चर नेविगेट या पुष्टि करता है; एक रद्द किया गया जेस्चर शुरुआती स्नैपशॉट को रिस्टोर करता है। डिफ़ॉल्ट या ओवरले प्राथमिकता वाले कॉलबैक सिस्टम एनिमेशन को दबा सकते हैं, इसलिए UI एनिमेशन और बिज़नेस लॉगिंग अलग-अलग पाथ का उपयोग करते हैं। मैं इसे प्रति Activity रोल आउट करता हूँ और Android 13, 14, और 15 का परीक्षण करता हूँ।"
चरण-दर-चरण समाधान
1. बैक को एक स्टेट मशीन के रूप में मॉडल करें
जेस्चर को idle → preview(progress) → committed या idle → preview(progress) → cancelled के रूप में मानें। प्रीव्यू केवल रिवर्सिबल विज़ुअल्स को अपडेट करता है। कमिट नेविगेशन, डिसमिसल, या कन्फर्मेशन करता है। कैंसलेशन एक स्नैपशॉट को रिस्टोर करता है। उस अवधि में कोई भी इरिवर्सिबल बिज़नेस राइट नहीं होना चाहिए जब यूज़र अभी भी जेस्चर को रिलीज़ (रद्द) कर सकता है।
2. लेगेसी इंटरसेप्शन APIs को माइग्रेट करें
Android का कंपैटिबिलिटी पाथ AndroidX Activity को अपग्रेड करना और OnBackPressedDispatcher के साथ एक OnBackPressedCallback रजिस्टर करना है। Activity.onBackPressed या KeyEvent.KEYCODE_BACK में इंटरसेप्ट करना बंद करें। प्लेटफ़ॉर्म-लेवल बैक इंटीग्रेशन की आवश्यकता होने पर OnBackInvokedCallback का उपयोग करें। KEYCODE_BACK के अभी भी समर्थित उपयोग हैं, लेकिन इसे अब बैक इंटरसेप्शन एंट्री पॉइंट नहीं होना चाहिए।
val confirmCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
showDiscardDialog()
}
}
onBackPressedDispatcher.addCallback(viewLifecycleOwner, confirmCallback)
formState.collect { state ->
confirmCallback.isEnabled = state.hasUnsavedChanges
}इनेबल्ड स्टेट ऑब्जर्वेबल फ़ॉर्म स्टेट से आती है, न कि कॉलबैक सक्रिय होने के बाद की गई देर से जाँच से। जब फ़ॉर्म साफ़ (clean) हो, तो कॉलबैक को डिसेबल कर दें ताकि सिस्टम या नेविगेशन कंपोनेंट बैक को हैंडल करे।
3. Compose प्रोग्रेस और कैंसलेशन को हैंडल करें
जब किसी Compose स्क्रीन को प्रोग्रेस-संचालित एनिमेशन की आवश्यकता हो, तो BackEventCompat को कलेक्ट करने के लिए PredictiveBackHandler का उपयोग करें। जब कलेक्शन रद्द हो जाए या जेस्चर कमिट न हो, तो एनिमेशन और स्टेड स्टेट को रिस्टोर करें। प्रोग्रेस के बिना अंतिम इंटरसेप्शन के लिए BackHandler का उपयोग करें ताकि साधारण स्क्रीन्स अनावश्यक जेस्चर लॉजिक न ले जाएं।
PredictiveBackHandler(enabled = canNavigateBack) { progress ->
try {
progress.collect { event -> renderPreview(event.progress) }
navigateBack()
} catch (cancelled: CancellationException) {
restorePreviewState()
}
}कॉलबैक्स को एक स्टैक के रूप में प्रोसेस किया जाता है: अंतिम जोड़ा गया इनेबल्ड कॉलबैक अगले जेस्चर को हैंडल करता है। नेस्टेड Compose में, सबसे आंतरिक PredictiveBackHandler या BackHandler जीतता है, इसलिए अस्पष्ट ग्लोबल कॉलबैक्स से बचें।
4. सिस्टम एनिमेशन को न दबाएं
Android गाइड कहता है कि PRIORITY_DEFAULT या PRIORITY_OVERLAY पर एक OnBackPressedCallback या OnBackInvokedCallback प्रेडिक्टिव सिस्टम एनिमेशन को रोकता है और ऐप से एनिमेशन को हैंडल करवाता है। UI कॉलबैक्स को डायलॉग्स और ट्रांज़िशन्स को नियंत्रित करना चाहिए। लॉगिंग या बिज़नेस ऑब्जर्वेशन को सिस्टम को ब्लॉक करने वाले उच्च-प्राथमिकता वाले कॉलबैक के बजाय एक गैर-उपभोग्य (non-consuming) ऑब्जर्वेशन पाथ का उपयोग करना चाहिए।
5. प्रति Activity रोल आउट करें
एप्लिकेशन स्तर पर android:enableOnBackInvokedCallback सेट करें या इसे किसी एक Activity के लिए ओवरराइड करें। एक बड़ा ऐप माइग्रेट की गई Activities के लिए इसे इनेबल कर सकता है जबकि लेगेसी स्क्रीन्स अस्थायी रूप से ऑप्ट आउट कर सकती हैं, फिर एक समय में एक स्क्रीन से पुराने इंटरसेप्शन को हटा सकती हैं। फ़्लैग को बंद करने से OnBackInvokedCallback अनदेखा हो जाता है, लेकिन AndroidX OnBackPressedCallback अभी भी चल सकता है, इसलिए दोनों पाथ का अलग-अलग परीक्षण करें।
6. कमिट, कैंसलेशन, और सीमाओं का परीक्षण करें
प्रत्येक स्क्रीन के लिए परीक्षण करें: कमिट सीमा से पहले छोड़ना; सीमा के बाद छोड़ना; फ़ॉर्म डर्टी और क्लीन; नेस्टेड Fragment/Compose कॉलबैक्स; रूट Activity से सिस्टम होम पर लौटना; और Android 13, 14, और 15। केवल स्क्रीनशॉट ही नहीं, बल्कि नेविगेशन स्टैक, फ़ॉर्म डेटा, अंतिम एनिमेशन स्टेट, और कॉलबैक इनेबल्ड स्टेट का दावा (assert) करें।
उच्च-गुणवत्ता वाला नमूना उत्तर
"मैं बैक को एक रद्द करने योग्य स्टेट मशीन के रूप में मॉडल करूँगा और Activities, Fragments, और Compose में लेगेसी इंटरसेप्शन की सूची बनाऊँगा। कंपैटिबिलिटी माइग्रेशन OnBackPressedCallback का उपयोग करता है; OnBackInvokedCallback सीधे प्लेटफ़ॉर्म इंटीग्रेशन के लिए है; PredictiveBackHandler केवल Compose प्रोग्रेस एनिमेशन के लिए है। कॉलबैक इनेबल्ड स्टेट इसका अनुसरण करती है कि फ़ॉर्म डर्टी है या नहीं। प्रीव्यू रिवर्सिबल विज़ुअल्स को बदलता है, कैंसलेशन उन्हें रिस्टोर करता है, और कमिट नेविगेट या पुष्टि करता है।
"मैं डिफ़ॉल्ट या ओवरले प्राथमिकता वाले कॉलबैक्स से बचूँगा जो सिस्टम एनिमेशन को दबा देते हैं और UI एनिमेशन को लॉगिंग से अलग रखूँगा। एक बड़ा ऐप प्रति Activity enableOnBackInvokedCallback रोल आउट करता है, कमिट और कैंसलेशन के लिए Android 13, 14, और 15 का परीक्षण करता है, और onBackPressed तथा KEYCODE_BACK इंटरसेप्शन को धीरे-धीरे हटाता है। स्वीकृति केवल एक एनिमेशन के बजाय नेविगेशन, स्टेट रिस्टोरेशन, कॉलबैक क्रम, और सिस्टम बैक-टू-होम व्यवहार की जाँच करती है।"
सामान्य गलतियाँ
- गलती: केवल
handleOnBackPressedके अंदर डर्टी फ़ॉर्म स्टेट की जाँच करना → यह क्यों विफल होता है: प्रीव्यू पहले ही हो चुका है और कैंसलेशन ठीक से रिस्टोर नहीं कर सकता → समाधान: ऑब्जर्वेबल स्टेट से कॉलबैक को इनेबल या डिसेबल करें। - गलती: हर जगह एक ग्लोबल
PRIORITY_OVERLAYकॉलबैक रजिस्टर करना → यह क्यों विफल होता है: सिस्टम प्रेडिक्टिव एनिमेशन दब जाता है → समाधान: केवल आवश्यक UI इवेंट्स का उपभोग करें और लॉग्स के लिए गैर-उपभोग्य ऑब्जर्वेशन का उपयोग करें। - गलती: Compose में केवल
navigateBack()को कॉल करना → यह क्यों विफल होता है: कैंसलेशन के बाद प्रीव्यू स्टेट बनी रहती है → समाधान: कैंसलेशन को कैच करें और स्नैपशॉट को रिस्टोर करें। - गलती: केवल Android 15 डिवाइस का परीक्षण करना → यह क्यों विफल होता है: कंपैटिबिलिटी APIs और पुराना व्यवहार असत्यापित रहता है → समाधान: Android 13, 14, 15 और एक्टिविटी-लेवल फ़्लैग्स को कवर करें।
फ़ॉलो-अप और उत्तर
जब यूज़र बीच में ही रिलीज़ कर देता है तो फ़ॉर्म डेटा का क्या होना चाहिए?
प्रीव्यू केवल अस्थायी विज़ुअल्स को बदलता है। फ़ॉर्म मॉडल और परसिस्टेड डेटा को अपरिवर्तित रखें, फिर जेस्चर रद्द होने पर विज़ुअल स्नैपशॉट को रिस्टोर करें। ऐसे जेस्चर के लिए सेव/डिसकार्ड डायलॉग न दिखाएं जो कभी कमिट ही नहीं हुआ।
प्रत्येक स्क्रीन के लिए एक ग्लोबल कॉलबैक का उपयोग क्यों न करें?
कॉलबैक प्राथमिकता स्टैक ऑर्डर और इनेबल्ड स्टेट पर निर्भर करती है। एक ग्लोबल कॉलबैक Fragment, Navigation, या Compose लॉजिक को छिपा सकता है और वर्तमान स्क्रीन की अनसेव्ड स्टेट को नहीं जान सकता है। स्क्रीन-लेवल सिंगल-रिस्पॉन्सिबिलिटी कॉलबैक्स को सत्यापित करना आसान होता है।
आप सिस्टम एनिमेशन का त्याग किए बिना बैक को कैसे लॉग करते हैं?
लॉगिंग के लिए उपभोग करने वाले डिफ़ॉल्ट या ओवरले कॉलबैक का उपयोग न करें। रूट Activity छोड़ने के लिए, सिस्टम नेविगेशन ऑब्जर्वर या लाइफ़साइकिल सिग्नल का उपयोग करें; UI व्यवहार के लिए UI कॉलबैक्स को ज़िम्मेदार रखें।
एक बड़ा मल्टी-एक्टिविटी ऐप इस फ़ीचर को कैसे रोल आउट करता है?
एक रूढ़िवादी (conservative) एप्लिकेशन सेटिंग से शुरुआत करें, फिर माइग्रेट की गई Activities पर enableOnBackInvokedCallback=true को ओवरराइड करें। प्रत्येक Activity द्वारा कमिट, कैंसलेशन, नेस्टेड-कॉलबैक, और पुराने-वर्जन टेस्ट पास करने के बाद ही विस्तार करें।
जब केवल अंतिम बैक की आवश्यकता हो तो कौन सा Compose API सही है?
BackHandler का उपयोग करें। यह सरल कैंसलेशन सेमेंटिक्स के साथ अंतिम इंटरसेप्शन को व्यक्त करता है। PredictiveBackHandler का उपयोग केवल तब करें जब BackEventCompat.progress एक इंटरैक्टिव एनिमेशन को संचालित करता है।
संदर्भ
- Android Developers: प्रेडिक्टिव बैक जेस्चर के लिए समर्थन जोड़ें।
- Android Developers: प्रेडिक्टिव बैक डिज़ाइन।
- Android Developers: Jetpack Compose के लिए Predictive back के बारे में।