Android ऐप्लिकेशन का साइज़ बदलना

1. परिचय

Android डिवाइस का नेटवर्क लगातार बेहतर होता रहता है. फ़ोन में पहले से मौजूद हार्डवेयर कीबोर्ड के शुरुआती दिनों से लेकर, फ़्लिप किए जा सकने वाले डिवाइस, फ़ोल्ड किए जा सकने वाले डिवाइस, और टैबलेट के साथ-साथ, फ़्री फ़ॉर्म वाली विंडो भी उपलब्ध हैं. Android ऐप्लिकेशन, अलग-अलग डिवाइसों पर आज से ज़्यादा काम नहीं करते.

यह डेवलपर के लिए अच्छी खबर है, लेकिन कुछ ऐप्लिकेशन ऑप्टिमाइज़ेशन ज़रूरी हैं, ताकि ऐप्लिकेशन को इस्तेमाल करने में उपयोगकर्ताओं की उम्मीदों पर खरा उतरा जा सके. साथ ही, अलग-अलग स्क्रीन साइज़ के हिसाब से उपयोगकर्ताओं को बेहतरीन अनुभव दिया जा सके. एक-एक करके हर नए डिवाइस को टारगेट करने के बजाय, रिस्पॉन्सिव/अडैप्टिव यूज़र इंटरफ़ेस (यूआई) और बेहतर आर्किटेक्चर आपके ऐप्लिकेशन को, हर साइज़ और साइज़ के डिवाइस पर शानदार दिखने और उस पर शानदार तरीके से काम करने में मदद कर सकते हैं, भले ही आपके मौजूदा और आने वाले समय के उपयोगकर्ता हों!

हमने रिस्पॉन्सिव/ज़रूरत के हिसाब से ढल जाने वाले यूज़र इंटरफ़ेस (यूआई) को प्रेशर टेस्ट के तौर पर उपलब्ध कराया है. इस टूल की मदद से, Android के फ़्री फ़ॉर्म को रीसाइज़ किया जा सकता है और इसे किसी भी डिवाइस के लिए तैयार किया जा सकता है. यह कोड लैब, साइज़ बदलने के नतीजों को समझने में आपकी मदद करेगा. साथ ही, किसी ऐप्लिकेशन का साइज़ आसानी और आसानी से बदलने के लिए, कुछ सबसे सही तरीके लागू करेगा.

आपको क्या बनाना होगा

Android ऐप्लिकेशन का साइज़ बदलने के सबसे सही तरीके बताने के लिए, इस लेख में Android ऐप्लिकेशन के फ़्री-फ़ॉर्म का साइज़ बदलने और उसे ऑप्टिमाइज़ करने के तरीकों के बारे में बताया जाएगा. आपका ऐप्लिकेशन ये काम करेगा:

आपके पास एक ऐसा मेनिफ़ेस्ट होना चाहिए जो इस फ़ाइल के साथ काम कर सके

  • उन पाबंदियों को हटाएं जो किसी ऐप्लिकेशन को आसानी से साइज़ बदलने से रोकती हैं

साइज़ बदलने पर स्थिति बनाए रखें

  • rememberSaveable का इस्तेमाल करके साइज़ बदलने पर यूज़र इंटरफ़ेस (यूआई) स्थिति बनाए रखता है
  • यूज़र इंटरफ़ेस (यूआई) को शुरू करने के लिए, बैकग्राउंड में होने वाले काम को डुप्लीकेट करने से बचें

आपको इनकी ज़रूरत होगी

  1. मूलभूत Android ऐप्लिकेशन बनाने का ज्ञान
  2. Compose में ViewModel और स्टेट के बारे में जानकारी
  3. ऐसा टेस्ट डिवाइस जो फ़्री-फ़ॉर्म विंडो का साइज़ बदलने की सुविधा देता है, जैसे कि इनमें से कोई एक:

कोडलैब के इस मॉड्यूल को इस्तेमाल करते समय, अगर आपको कोई समस्या आती है, जैसे कि कोड में गड़बड़ी, व्याकरण से जुड़ी गड़बड़ियां, गलत शब्द वगैरह, तो कृपया कोडलैब के नीचे बाएं कोने में मौजूद गलती की शिकायत करें लिंक का इस्तेमाल करके, समस्या की शिकायत करें.

2. शुरू करें

GitHub से रिपॉज़िटरी का क्लोन बनाएं.

git clone https://github.com/android/large-screen-codelabs/

...या रिपॉज़िटरी की ZIP फ़ाइल डाउनलोड करके उसे एक्सट्रैक्ट करें

प्रोजेक्ट इंपोर्ट करें

  • Android Studio खोलना
  • प्रोजेक्ट इंपोर्ट करें या फ़ाइल->नया->प्रोजेक्ट इंपोर्ट करें चुनें
  • उस जगह पर जाएं जहां आपने प्रोजेक्ट को क्लोन किया है या एक्सट्रैक्ट किया है
  • साइज़ बदलने वाला फ़ोल्डर खोलें.
  • प्रोजेक्ट को start फ़ोल्डर में खोलें. इसमें स्टार्टर कोड होता है.

ऐप्लिकेशन आज़माएं

  • ऐप्लिकेशन बनाएं और चलाएं
  • ऐप्लिकेशन का साइज़ बदलने की कोशिश करें

आपकी क्या राय है?

आपके टेस्ट डिवाइस पर यह सुविधा काम करती है या नहीं, इसके आधार पर आपको शायद पता चला होगा कि ऐप्लिकेशन इस्तेमाल करने वाले लोगों का अनुभव अच्छा नहीं रहा. ऐप्लिकेशन का साइज़ नहीं बदला जा सकता और यह शुरुआती आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) में ही अटक गया है. क्या हो रहा है?

मेनिफ़ेस्ट से जुड़ी पाबंदियां

अगर ऐप्लिकेशन की AndroidManifest.xml फ़ाइल देखें, तो आपको पता चल सकता है कि कुछ पाबंदियां जोड़ी गई हैं. इनकी वजह से हमारा ऐप्लिकेशन, विंडो का साइज़ बदलने के लिए, फ़्री फ़ॉर्म की सुविधा में ठीक से काम नहीं कर पा रहा है.

AndroidManifest.xml

            android:maxAspectRatio="1.4"
            android:resizeableActivity="false"
            android:screenOrientation="portrait">

अपने मेनिफ़ेस्ट से समस्या वाली इन तीन लाइनों को हटाएं. इसके बाद, ऐप्लिकेशन को फिर से बनाएं और अपने टेस्ट डिवाइस पर इसे फिर से बनाने की कोशिश करें. आप यह देखेंगे कि ऐप्लिकेशन का फ़्री-फ़ॉर्म साइज़ बदलने पर अब पाबंदी नहीं लगी है. मेनिफ़ेस्ट से इस तरह की पाबंदियों को हटाना, अपने ऐप्लिकेशन को फ़्री-फ़ॉर्म विंडो का साइज़ बदलने के लिए ऑप्टिमाइज़ करने की दिशा में एक अहम कदम है.

3. साइज़ बदलने के कॉन्फ़िगरेशन में बदलाव

ऐप्लिकेशन की विंडो का साइज़ बदलने पर, ऐप्लिकेशन का कॉन्फ़िगरेशन अपडेट हो जाता है. इन अपडेट का असर आपके ऐप्लिकेशन पर पड़ सकता है. उन्हें समझने और उनका अनुमान लगाने से आपके उपयोगकर्ताओं को एक शानदार अनुभव मिल सकता है. सबसे ज़्यादा आसानी से दिखने वाले बदलाव, आपके ऐप्लिकेशन की विंडो की चौड़ाई और ऊंचाई में होते हैं. हालांकि, इन बदलावों का असर आसपेक्ट रेशियो और स्क्रीन की दिशा पर भी पड़ता है.

कॉन्फ़िगरेशन में हुए बदलावों पर नज़र रखी जा रही है

Android व्यू सिस्टम के साथ बनाए गए ऐप्लिकेशन में खुद ये बदलाव देखने के लिए, View.onConfigurationChanged को बदला जा सकता है. Jetpack Compose में, हमारे पास LocalConfiguration.current का ऐक्सेस है. यह View.onConfigurationChanged को कॉल करने पर अपने-आप अपडेट हो जाता है.

सैंपल ऐप्लिकेशन में, कॉन्फ़िगरेशन के इन बदलावों को देखने के लिए, अपने ऐप्लिकेशन में कोई ऐसा कंपोज़ेबल जोड़ें जो LocalConfiguration.current की वैल्यू दिखाता हो. इसके अलावा, इस तरह के किसी कंपोज़ेबल के साथ एक नया सैंपल प्रोजेक्ट भी बनाया जा सकता है. इन्हें देखने के लिए यूज़र इंटरफ़ेस (यूआई) का उदाहरण कुछ ऐसा होगा:

val configuration = LocalConfiguration.current
val isPortrait = configuration.orientation ==
    Configuration.ORIENTATION_PORTRAIT
val screenLayoutSize =
        when (configuration.screenLayout and
                Configuration.SCREENLAYOUT_SIZE_MASK) {
            SCREENLAYOUT_SIZE_SMALL -> "SCREENLAYOUT_SIZE_SMALL"
            SCREENLAYOUT_SIZE_NORMAL -> "SCREENLAYOUT_SIZE_NORMAL"
            SCREENLAYOUT_SIZE_LARGE -> "SCREENLAYOUT_SIZE_LARGE"
            SCREENLAYOUT_SIZE_XLARGE -> "SCREENLAYOUT_SIZE_XLARGE"
            else -> "undefined value"
        }
Column(
    horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxWidth()
) {
    Text("screenWidthDp: ${configuration.screenWidthDp}")
    Text("screenHeightDp: ${configuration.screenHeightDp}")
    Text("smallestScreenWidthDp: ${configuration.smallestScreenWidthDp}")
    Text("orientation: ${if (isPortrait) "portrait" else "landscape"}")
    Text("screenLayout SIZE: $screenLayoutSize")
}

लागू करने का उदाहरण, observing-configuration-changes प्रोजेक्ट फ़ोल्डर में देखा जा सकता है. इसे अपने ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में जोड़ने की कोशिश करें और इसे अपने टेस्ट डिवाइस पर चलाएं. साथ ही, अपने ऐप्लिकेशन के कॉन्फ़िगरेशन में बदलाव होने पर यूज़र इंटरफ़ेस (यूआई) का अपडेट देखें.

ऐप्लिकेशन का साइज़ बदलने पर, ऐप्लिकेशन के इंटरफ़ेस में कॉन्फ़िगरेशन की बदली हुई जानकारी रीयल टाइम में दिखेगी

आपके ऐप्लिकेशन के कॉन्फ़िगरेशन में हुए इन बदलावों की मदद से, आपको तुरंत यह पता चल सकता है कि छोटे हैंडसेट पर स्प्लिट स्क्रीन मोड से टैबलेट या डेस्कटॉप पर फ़ुल स्क्रीन मोड में दिखना चाहिए. यह न सिर्फ़ अलग-अलग स्क्रीन पर अपने ऐप्लिकेशन के लेआउट की जांच करने का एक अच्छा तरीका है, बल्कि इससे यह भी जांच की जा सकती है कि आपका ऐप्लिकेशन तेज़ी से कॉन्फ़िगरेशन में हुए बदलाव के इवेंट को कितनी अच्छी तरह से संभाल सकता है.

4. गतिविधि के लाइफ़साइकल इवेंट लॉग करने की सेटिंग

आपके ऐप्लिकेशन की फ़्री-फ़ॉर्म विंडो का साइज़ बदलने का एक और उदाहरण, Activity लाइफ़साइकल में होने वाले कई बदलाव हैं. इन बदलावों को रीयल टाइम में देखने के लिए, अपने onCreate तरीके में लाइफ़साइकल ऑब्ज़र्वर जोड़ें. साथ ही, onStateChanged को ओवरराइड करके, हर नए लाइफ़साइकल इवेंट को लॉग करें.

lifecycle.addObserver(object : LifecycleEventObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        Log.d("resizing-codelab-lifecycle", "$event was called")
    }
})

डेटा लॉग करने की यह प्रोसेस चालू होने के बाद, टेस्ट डिवाइस पर ऐप्लिकेशन को फिर से चलाएं. ऐप्लिकेशन को छोटा करके उसे फ़ोरग्राउंड में लाने की कोशिश करते समय, logcat की मदद लें.

देखें कि छोटा किए जाने पर आपका ऐप्लिकेशन रोक दिया गया है और जब उसे फ़ोरग्राउंड में लाया जाता है, तो उसे फिर से शुरू किया जाता है. इसका असर आपके ऐप्लिकेशन पर पड़ सकता है. इस कोडलैब के आने वाले सेक्शन में, आपको गेम लगातार जारी रखने पर फ़ोकस करने को कहा जाएगा.

साइज़ बदलते समय, ऐक्टिविटी लाइफ़साइकल के तरीकों को शुरू करने की जानकारी देने वाला Logcat

अब Logcat देखें कि ऐप्लिकेशन का साइज़ सबसे छोटे साइज़ से बड़े साइज़ में बदलने पर, किस ऐक्टिविटी लाइफ़साइकल कॉलबैक को कॉल किया जाता है

आपके टेस्ट डिवाइस के आधार पर, आपको अलग-अलग तरह की गतिविधियां दिख सकती हैं. हालांकि, आपने देखा होगा कि ऐप्लिकेशन की विंडो के साइज़ में ज़्यादा बदलाव होने पर, आपकी गतिविधि खत्म हो जाती है और उसे फिर से बना दिया जाता है. हालांकि, स्क्रीन में थोड़ा बदलाव करने पर ऐसा नहीं होता. ऐसा इसलिए है, क्योंकि एपीआई 24 या इसके बाद के वर्शन पर, साइज़ में बड़े बदलाव करने पर ही Activity को फिर से शामिल किया जाता है.

आपने कॉन्फ़िगरेशन में कुछ ऐसे सामान्य बदलाव देखे हैं जिनकी उम्मीद फ़्री-फ़ॉर्म विंडोइंग एनवायरमेंट में की जा सकती है. हालांकि, कुछ अन्य बदलावों के बारे में आपको जानकारी होनी चाहिए. उदाहरण के लिए, अगर आपके टेस्ट डिवाइस से कोई बाहरी मॉनिटर कनेक्ट है, तो आपके पास यह देखने का विकल्प होगा कि आपके Activity को बंद कर दिया गया है. साथ ही, डिसप्ले डेंसिटी जैसे कॉन्फ़िगरेशन में हुए बदलावों के हिसाब से, इसे फिर से बनाया गया है.

कॉन्फ़िगरेशन में हुए बदलावों की वजह से होने वाली कुछ दिक्कतों को दूर करने के लिए, अडैप्टिव यूज़र इंटरफ़ेस (यूआई) लागू करने के लिए, WindowSizeClass जैसे हाई लेवल के एपीआई का इस्तेमाल करें. (अलग-अलग स्क्रीन साइज़ के लिए सहायता लेख भी देखें.)

5. कंटिन्युइटी - कंपोज़ेबल को बनाए रखना आकार बदलने पर आंतरिक स्थिति

पिछले सेक्शन में, आपने कॉन्फ़िगरेशन में कुछ ऐसे बदलाव देखे हैं जो आपके ऐप्लिकेशन की फ़्री-फ़ॉर्म विंडो का साइज़ बदलने वाले एनवायरमेंट में दिख सकते हैं. इस सेक्शन में, इन बदलावों के दौरान आपको अपने ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) की स्थिति को लगातार बनाए रखना होगा.

क्लिक करने पर ईमेल पता दिखाने के लिए, NavigationDrawerHeader कंपोज़ेबल फ़ंक्शन (ReplyHomeScreen.kt में मौजूद) को बड़ा करके शुरू करें.

@Composable
private fun NavigationDrawerHeader(
    modifier: Modifier = Modifier
) {
    var showDetails by remember { mutableStateOf(false) }
    Column(
        modifier = modifier.clickable {
                showDetails = !showDetails
            }
    ) {


        Row(
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            ReplyLogo(
                modifier = Modifier
                    .size(dimensionResource(R.dimen.reply_logo_size))
            )
            ReplyProfileImage(
                drawableResource = LocalAccountsDataProvider
                    .userAccount.avatar,
                description = stringResource(id = R.string.profile),
                modifier = Modifier
                    .size(dimensionResource(R.dimen.profile_image_size))
            )
        }
        AnimatedVisibility (showDetails) {
            Text(
                text = stringResource(id = LocalAccountsDataProvider
                        .userAccount.email),
                style = MaterialTheme.typography.labelMedium,
                modifier = Modifier
                    .padding(
                        start = dimensionResource(
                            R.dimen.drawer_padding_header),
                        end = dimensionResource(
                            R.dimen.drawer_padding_header),
                        bottom = dimensionResource(
                            R.dimen.drawer_padding_header)
                ),


            )
        }
    }
}

अपने ऐप्लिकेशन में बड़ा किया जा सकने वाला हेडर जोड़ने के बाद,

  1. अपने टेस्ट डिवाइस पर ऐप्लिकेशन चलाएं
  2. हेडर को बड़ा करने के लिए उस पर टैप करें
  3. विंडो का साइज़ बदलकर देखें

आप देखेंगे कि बहुत ज़्यादा आकार बदलने पर हेडर की स्थिति हट जाती है.

ऐप्लिकेशन के नेविगेशन पैनल पर मौजूद हेडर को टैप करके बड़ा किया जाता है. हालांकि, ऐप्लिकेशन का साइज़ बदलने के बाद यह छोटा हो जाता है

यूज़र इंटरफ़ेस (यूआई) की स्थिति इस वजह से उपलब्ध नहीं है कि remember की मदद से आपको बदलाव करते समय स्थिति बनाए रखने में मदद मिलती है. हालांकि, यह गतिविधि या मनोरंजन की प्रोसेस के दौरान नहीं होती. स्टेट होस्टिंग का इस्तेमाल करना आम बात है. कंपोज़ेबल को स्टेटलेस बनाने के लिए, कंपोज़ेबल के कॉलर को मूव किया जाता है. इससे, इस समस्या से पूरी तरह बचा जा सकता है. इसलिए, कंपोज़ेबल फ़ंक्शन में यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति को इंटरनल बनाए रखने के लिए, remember का इस्तेमाल किया जा सकता है.

इन समस्याओं को हल करने के लिए, remember को rememberSaveable से बदलें. ऐसा इसलिए होता है, क्योंकि rememberSaveable याद रखी गई वैल्यू को savedInstanceState में सेव करके वापस ला देता है. remember को rememberSaveable में बदलें, ऐप्लिकेशन को टेस्ट डिवाइस पर चलाएं, और ऐप्लिकेशन का साइज़ फिर से बदलने की कोशिश करें. आप देखेंगे कि बड़े किए जा सकने वाले हेडर की स्थिति, ज़रूरत के हिसाब से साइज़ बदलने के दौरान पहले जैसी ही रहती है.

6. बैकग्राउंड में होने वाले काम के दोहराव से बचना

आपने देखा कि rememberSaveable का इस्तेमाल करके, कंपोज़ेबल को सुरक्षित रखने के लिए क्या किया जा सकता है इंटरनल यूज़र इंटरफ़ेस (यूआई) की स्थिति, कॉन्फ़िगरेशन बदलावों के ज़रिए होती है. यह फ़्री-फ़ॉर्म विंडो का साइज़ बदलने की वजह से बार-बार हो सकती है. हालांकि, ऐप्लिकेशन में अक्सर यूज़र इंटरफ़ेस (यूआई) की स्थिति और लॉजिक को कंपोज़ेबल से दूर ले जाना चाहिए. साइज़ बदलने के दौरान स्थिति को बनाए रखने के लिए, स्टेटस के मालिकाना हक को ViewModel पर ले जाना, सबसे अच्छे तरीकों में से एक है. अपने राज्य को ViewModel में ले जाने पर, आपको बैकग्राउंड में लंबे समय तक काम करने में समस्याएं आ सकती हैं. जैसे, फ़ाइल सिस्टम का भारी ऐक्सेस या स्क्रीन शुरू करने के लिए ज़रूरी नेटवर्क कॉल.

आपको जिस तरह की समस्याएं आ सकती हैं उनका उदाहरण देखने के लिए, ReplyViewModel में initializeUIState तरीके में लॉग स्टेटमेंट जोड़ें.

fun initializeUIState() {
    Log.d("resizing-codelab", "initializeUIState() called in the viewmodel")
    val mailboxes: Map<MailboxType, List<Email>> =
        LocalEmailsDataProvider.allEmails.groupBy { it.mailbox }
    _uiState.value =
        ReplyUiState(
            mailboxes = mailboxes,
            currentSelectedEmail = mailboxes[MailboxType.Inbox]?.get(0)
                ?: LocalEmailsDataProvider.defaultEmail
        )
}

अब अपने टेस्ट डिवाइस पर ऐप्लिकेशन चलाएं और ऐप्लिकेशन की विंडो का साइज़ कई बार बदलने की कोशिश करें.

Logcat देखने पर, आपको दिखेगा कि आपका ऐप्लिकेशन, शुरू करने का तरीका कई बार दिखाता है. यह समस्या उस काम के लिए हो सकती है जिसे आपको यूज़र इंटरफ़ेस (यूआई) को शुरू करने के लिए, सिर्फ़ एक बार चलाना है. अतिरिक्त नेटवर्क कॉल, फ़ाइल I/O या अन्य काम की वजह से, डिवाइस की परफ़ॉर्मेंस में रुकावट आ सकती है. साथ ही, इससे अनजाने में दूसरी समस्याएं भी पैदा हो सकती हैं.

बैकग्राउंड में ग़ैर-ज़रूरी काम न हो, इसके लिए अपनी गतिविधि के onCreate() तरीके से initializeUIState() को किया गया कॉल हटाएं. इसके बजाय, ViewModel के init तरीके में डेटा शुरू करें. इससे यह पक्का होता है कि ReplyViewModel को पहली बार इंस्टैंशिएट किए जाने पर, शुरू करने का तरीका सिर्फ़ एक बार चलता है:

init {
    initializeUIState()
}

ऐप्लिकेशन को फिर से चलाने की कोशिश करें. इससे यह देखा जा सकता है कि ऐप्लिकेशन की विंडो का साइज़ चाहे कितनी भी बार बदला गया हो, लेकिन सिम्युलेटेड इनिशलाइज़ेशन के ग़ैर-ज़रूरी टास्क सिर्फ़ एक बार चलते हैं. इसकी वजह यह है कि ViewModels, Activity की लाइफ़साइकल के दौरान भी बना रहता है. ViewModel बनाते समय, कोड को सिर्फ़ एक बार शुरू करने की प्रक्रिया का इस्तेमाल करके, हम इसे किसी भी Activity रीक्रिएशन से अलग करते हैं और ग़ैर-ज़रूरी काम को रोकते हैं. अगर आपका यूज़र इंटरफ़ेस (यूआई) शुरू करने के लिए, सर्वर कॉल बहुत महंगा होता है या फ़ाइल I/O बहुत ज़्यादा होती है, तो ऐसे में काफ़ी संसाधन सेव होंगे और उपयोगकर्ता अनुभव को बेहतर बनाया जा सकेगा.

7. बधाई हो!

आपने कर दिखाया! बहुत बढ़िया! अब आपने Android ऐप्लिकेशन के लिए, सबसे सही तरीके लागू किए हैं, ताकि ChromeOS और अन्य मल्टी-विंडो और मल्टी-स्क्रीन एनवायरमेंट पर Android ऐप्लिकेशन का साइज़ सही तरीके से बदला जा सके.

नमूना स्रोत कोड

GitHub से रिपॉज़िटरी का क्लोन बनाएं

git clone https://github.com/android/large-screen-codelabs/

...या रिपॉज़िटरी की ZIP फ़ाइल डाउनलोड करके उसे एक्सट्रैक्ट करें