2026 में Android SplashScreen API: बिना सफ़ेद फ़्लैश के कोल्ड स्टार्ट
Android के SplashScreen API की एक व्यावहारिक गाइड — थीम सेटअप, एनिमेटेड आइकन की साइज़ सीमाएँ जिन्हें कोई नहीं पढ़ता, और अभी तैयार न हुए डेटा के लिए keepOnScreenCondition।
हर Android ऐप Android 12 से स्प्लैश स्क्रीन दिखाता है, चाहे आपने माँगा हो या नहीं। जिस पल उपयोगकर्ता लॉन्चर पर टैप करता है, सिस्टम आपके ऐप आइकन और थीम रंगों से तुरंत एक स्प्लैश खींच देता है — असली विकल्प जो आपके पास है वह है डिफ़ॉल्ट को स्वीकार करना, या androidx.core.splashscreen को उस स्क्रीन को खींचने के लिए कॉन्फ़िगर करना जिसे आप वाकई शिप करना चाहते थे। मैंने जो भी खराब स्प्लैश स्क्रीन देखी हैं, वे इसलिए खराब नहीं हैं क्योंकि किसी ने उन्हें बुरी तरह डिज़ाइन किया — वे इसलिए खराब हैं क्योंकि किसी ने कुछ भी कॉन्फ़िगर नहीं किया, और सिस्टम के सबसे अच्छे अनुमान ने वह जगह भर दी।
Granyn बनाते समय मैंने इस पोस्ट की हर गलती की, जिसका डैशबोर्ड पहली फ्रेम दिखाने लायक होने से पहले Room से लोड किए गए पहले बैलेंस आंकड़े की मांग करता है। यह वह सेटअप है जिसने सफ़ेद फ़्लैश को रोका और स्प्लैश स्क्रीन को ज़रूरत से एक पल ज़्यादा टिके रहने से रोका।
आपको स्प्लैश स्क्रीन क्यों मिलती है, चाहें या न चाहें
Android 12 (API 31) से शुरू होकर, प्लेटफ़ॉर्म हर कोल्ड स्टार्ट को इंटरसेप्ट करता है और आपकी Activity के अपना कंटेंट व्यू अटैच करने से पहले ही सिस्टम-द्वारा-खींची गई स्प्लैश विंडो दिखाता है। यह कोई लाइब्रेरी फ़ीचर नहीं है जिसे आप छोड़ सकें — यह डिफ़ॉल्ट फ्रेमवर्क व्यवहार है। androidx.core.splashscreen आपको जो देता है वह एक कम्पैटिबिलिटी शिम है जो आपको उस सिस्टम विंडो को API 23 तक लगातार कॉन्फ़िगर करने देता है, बजाय इसके कि 12+ पर असली चीज़ मिले और बाकी सब पर सादा सफ़ेद या काला आयत।
सेटअप छोड़ दें और आपको सिस्टम का डिफ़ॉल्ट मिलता है: आपकी थीम से खींचे गए एक ठोस रंग पर आपका लॉन्चर आइकन, कोई एनिमेशन नहीं, यह कितनी देर टिकता है इस पर कोई नियंत्रण नहीं। यह टूटा हुआ नहीं है, लेकिन यह शायद ही कभी वह होता है जो एक ब्रांडेड ऐप चाहता है, और कम्पैटिबिलिटी लाइब्रेरी के बिना 12-पूर्व डिवाइसों पर, यह आपकी थीम लागू होने से पहले एक कष्टप्रद सफ़ेद फ़्लैश हो सकता है।
थीम को जोड़ना
स्प्लैश लगभग पूरी तरह से XML में कॉन्फ़िगर होता है, एक थीम के रूप में जो setContentView चलने से पहले लागू होती है:
<!-- res/values/themes.xml -->
<style name="Theme.App.Starting" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/brand_background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
<item name="windowSplashScreenAnimationDuration">500</item>
<item name="postSplashScreenTheme">@style/Theme.App</item>
</style>
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
setContent { AppTheme { GranynApp() } }
}
}
installSplashScreen() को super.onCreate() से पहले चलना ज़रूरी है — इसे बाद में कॉल करें, तो कम्पैटिबिलिटी लाइब्रेरी समय पर विंडो को इंटरसेप्ट नहीं कर पाती, और आप चुपचाप सिस्टम के डिफ़ॉल्ट पर वापस चले जाते हैं। postSplashScreenTheme वह थीम है जिस पर स्प्लैश बंद होने के बाद विंडो स्विच होती है; इसे सेट करना भूल जाने से आपकी Activity कुछ पल के लिए असली कंटेंट पर लागू स्प्लैश-विंडो एट्रिब्यूट्स के साथ रेंडर करने में अटकी रह जाती है।
आइकन साइज़ की वह सीमा जिसे कोई तब तक नहीं पढ़ता जब तक यह काट न ले
windowSplashScreenAnimatedIcon की एक सख्त सीमा है: drawable को 240×240dp के एक सर्कल के भीतर रेंडर किया जाता है, और इससे बड़ी कोई भी चीज़ स्केल होने के बजाय चुपचाप काट दी जाती है। यह सबसे आम स्प्लैश स्क्रीन बग है जो मैं देखता हूँ — कोई एडेप्टिव लॉन्चर आइकन को दोबारा इस्तेमाल कर लेता है, जो 72×72dp दिखने वाले सर्कल के भीतर 108×108dp के फ़ोरग्राउंड लेयर के लिए डिज़ाइन किया गया है, और यह या तो स्प्लैश की सीमा से बाहर निकल जाता है या ठीक एक स्क्रीन डेंसिटी पर टूटा हुआ दिखने वाले तरीके से कट जाता है।
समाधान एक समर्पित स्प्लैश आइकन है, जो अपने आप में 240dp सीमा के लिए साइज़ और सेंटर किया गया हो, न कि दोबारा इस्तेमाल किया गया लॉन्चर एसेट:
res/drawable/splash_icon.xml (एक ही वेक्टर, कोई एडेप्टिव लेयर नहीं, 240x240dp में फ़िट)
अगर आइकन को एनिमेटेड होना है (एक AnimatedVectorDrawable), तो वही साइज़ सीमा लागू होती है, और windowSplashScreenAnimationDuration यह सीमित करता है कि प्लेटफ़ॉर्म इसके लिए कितनी देर इंतज़ार करता है — API 31+ पर अधिकतम 1000ms, हालाँकि पुराने API पर कम्पैटिबिलिटी लाइब्रेरी उस सीमा को उसी तरह लागू नहीं करती, जो अगर आप केवल एक OS वर्ज़न चेक करते हैं तो टेस्टिंग में असंगति का अपना ही स्रोत है।
जब डेटा तैयार न हो: keepOnScreenCondition
Granyn के लिए जो हिस्सा वाकई मायने रखता था वह आइकन नहीं था — वह टाइमिंग थी। स्प्लैश स्क्रीन जैसे ही पहली फ्रेम खींचने के लिए तैयार होती है, बंद हो जाती है, जो एक Compose स्क्रीन के लिए ViewModel के पास दिखाने के लिए कुछ होने से पहले हो सकता है। ऐसे ही छोड़ दिया जाए, तो यह स्प्लैश स्क्रीन के गायब होने और आधे सेकंड बाद बैलेंस लोड होने के बीच एक खाली डैशबोर्ड की फ़्लैश पैदा करता है — यह स्प्लैश स्क्रीन के सिर्फ़ उस अतिरिक्त पल के लिए टिके रहने से भी बदतर है।
SplashScreen.setKeepOnScreenCondition स्प्लैश विंडो को तब तक खुला रखता है जब तक कोई शर्त पूरी न हो जाए:
class MainActivity : ComponentActivity() {
private val viewModel: DashboardViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
splashScreen.setKeepOnScreenCondition {
!viewModel.isReady.value
}
setContent { AppTheme { GranynApp(viewModel) } }
}
}
यहाँ isReady एक StateFlow<Boolean> है जो तब true हो जाता है जब डैशबोर्ड के बैलेंस के लिए पहली Room क्वेरी वाकई कुछ लौटाती है। यह शर्त हर फ्रेम पर पोल की जाती है, इसलिए इसे एक सस्ते बूलियन रीड के रूप में ही रखें — लैम्ब्डा के अंदर सीधे कभी कोई suspend कॉल या डेटाबेस हिट नहीं।
दो चीज़ें इसे ऐप को हमेशा के लिए अटकाने का तरीका बनने से रोककर सुरक्षित बनाती हैं। पहला, एक सीमा तय करें — अगर isReady कभी true नहीं होता (एक करप्ट डेटाबेस, अटकी हुई क्वेरी), तो स्प्लैश स्क्रीन को टाइमआउट होना चाहिए और UI को हमेशा के लिए जमे रहने के बजाय अपनी खुद की लोडिंग या एरर स्थिति दिखाने देनी चाहिए:
private val isReady = MutableStateFlow(false)
init {
viewModelScope.launch {
withTimeoutOrNull(2000) {
dashboardFlow.first { it != null }
}
isReady.value = true
}
}
दूसरा, setKeepOnScreenCondition केवल एग्ज़िट को टालता है — यह न तो इनपुट को ब्लॉक करता है और न ही बाकी ऐप स्टार्टअप को, इसलिए इसे ट्री में नीचे किसी असली लोडिंग स्थिति के विकल्प के रूप में इस्तेमाल न करें। यह आपको इसके ऐप हैंग होने जैसा दिखने से पहले ज़्यादा से ज़्यादा कुछ सौ मिलीसेकंड का धैर्य ही खरीद कर देता है।
एग्ज़िट एनिमेशन, अगर फ़ेड मायने रखता है
डिफ़ॉल्ट रूप से स्प्लैश व्यू को बस हटा दिया जाता है। अगर एग्ज़िट पर एक कस्टम फ़ेड या स्केल ब्रांड फ़ील के लिए मायने रखता है, तो setOnExitAnimationListener आपको खुद इसे हटाने से पहले एनिमेट करने के लिए असली स्प्लैश व्यू देता है:
splashScreen.setOnExitAnimationListener { splashView ->
splashView.view.animate()
.alpha(0f)
.setDuration(200)
.withEndAction { splashView.remove() }
.start()
}
आखिर में splashView.remove() छोड़ देने से स्प्लैश व्यू आपके कंटेंट के ऊपर हमेशा के लिए अटैच रह जाता है — एक-लाइन की चूक जो आसानी से हो जाती है, जो हॉट-रीलोड डेव लूप में ठीक दिखती है और फिर हमेशा के लिए फ्रीज़ हुई लॉन्च स्क्रीन शिप कर देती है।
जो वाकई मायने रखता था
जब आप कुछ भी कॉन्फ़िगर नहीं करते तब सिस्टम आपको जो डिफ़ॉल्ट देता है वह टूटा हुआ नहीं है, लेकिन जानबूझकर भी नहीं है — स्प्लैश थीम को super.onCreate() से पहले इंस्टॉल करें, आइकन को दोबारा इस्तेमाल किए गए एडेप्टिव लॉन्चर आइकन के बजाय असली 240dp सीमा के लिए साइज़ करें, और keepOnScreenCondition का इस्तेमाल तभी करें जब कोई खास, समयबद्ध डेटा हो जिसकी पहली फ्रेम को वाकई ज़रूरत हो। एग्ज़िट एनिमेशन वह हिस्सा है जिसे पूरी तरह छोड़ना आसान है, और ज़्यादातर ऐप्स के लिए यही सही फ़ैसला है — यहाँ जीत सफ़ेद फ़्लैश और खाली डैशबोर्ड की टिमटिमाहट को हटाना है, अपने आप में मूवमेंट जोड़ना नहीं।
// संबंधित पठन
जर्नल से और भी
Jetpack Compose में Material You डायनामिक कलर: जब वॉलपेपर जीत जाए तो अपना ब्रांड कलर बचाए रखना
dynamicColorScheme() आपके पैलेट को यूज़र के वॉलपेपर से बने पैलेट से बदल देता है। ब्रांड रंगों को खोने के बजाय उन्हें हार्मोनाइज़ करने की एक व्यावहारिक गाइड।
Android का In-App Review API: बिना परेशान किए रेटिंग माँगना
Google के In-App Review API की एक व्यावहारिक गाइड — यह असल में कैसे काम करता है, इसे कब ट्रिगर करना चाहिए, और सामान्य 'हमें रेट करें' पॉपअप आपकी Play Store रेटिंग को चुपचाप कैसे नुकसान पहुँचा रहा है।
2026 में Android पर एक्सेसिबिलिटी: एक TalkBack और Compose semantics चेकलिस्ट जो वाकई काम करती है
2026 के लिए एक व्यावहारिक Android एक्सेसिबिलिटी चेकलिस्ट — TalkBack, Compose semantics, टच टारगेट, और रिलीज़ से पहले हर स्क्रीन पर जो टेस्टिंग पास मैं करता हूं।