Android के लिए, तेज़ी से इटरेटिव डेवलपमेंट करना
इस पेज पर बताया गया है कि bazel mobile-install
, Android के लिए बार-बार डेवलपमेंट करने की प्रोसेस को कैसे ज़्यादा तेज़ बनाता है. इसमें, ऐप्लिकेशन इंस्टॉल करने के पारंपरिक तरीके की चुनौतियों के मुकाबले, इस तरीके के फ़ायदों के बारे में बताया गया है.
खास जानकारी
Android ऐप्लिकेशन में छोटे-मोटे बदलावों को तुरंत इंस्टॉल करने के लिए, यह तरीका अपनाएं:
- उस ऐप्लिकेशन का
android_binary
नियम ढूंढें जिसे आपको इंस्टॉल करना है. proguard_specs
एट्रिब्यूट हटाकर, Proguard को बंद करें.multidex
एट्रिब्यूट कोnative
पर सेट करें.dex_shards
एट्रिब्यूट को10
पर सेट करें.- यूएसबी के ज़रिए, ART (Dalvik नहीं) का इस्तेमाल करने वाले डिवाइस को कनेक्ट करें और उस पर यूएसबी डीबगिंग चालू करें.
bazel mobile-install :your_target
रन करें. ऐप्लिकेशन को चालू होने में सामान्य से थोड़ा ज़्यादा समय लगेगा.- कोड या Android संसाधनों में बदलाव करें.
bazel mobile-install --incremental :your_target
रन करें.- अब आपको ज़्यादा इंतज़ार नहीं करना पड़ेगा.
Bazel के लिए कुछ कमांड-लाइन विकल्प, जो आपके काम आ सकते हैं:
--adb
से Bazel को पता चलता है कि किस adb बाइनरी का इस्तेमाल करना है--adb_arg
का इस्तेमाल,adb
की कमांड लाइन में अतिरिक्त तर्क जोड़ने के लिए किया जा सकता है. इसका एक फ़ायदेमंद इस्तेमाल यह है कि अगर आपके वर्कस्टेशन से एक से ज़्यादा डिवाइस कनेक्ट हैं, तो यह चुना जा सकता है कि आपको किस डिवाइस पर इंस्टॉल करना है:bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
--start_app
ऐप्लिकेशन अपने-आप चालू हो जाता है
अगर आपको कोई शंका है, तो उदाहरण देखें या हमसे संपर्क करें.
परिचय
डेवलपर के टूलचेन की सबसे अहम विशेषताओं में से एक है स्पीड: कोड में बदलाव करने और उसे एक सेकंड के अंदर चलते हुए देखने में बहुत अंतर होता है. इसके अलावा, इस बात का इंतज़ार करना कि आपके बदलावों से आपको उम्मीद के मुताबिक नतीजे मिले हैं या नहीं, इसमें कभी-कभी मिनटों और घंटों का समय लग जाता है.
माफ़ करें, .apk बनाने के लिए Android की पारंपरिक टूलचेन में कई मोनोलिथिक और क्रमवार चरण शामिल होते हैं. Android ऐप्लिकेशन बनाने के लिए, इन सभी चरणों को पूरा करना ज़रूरी होता है. Google में, Google Maps जैसे बड़े प्रोजेक्ट में एक लाइन के बदलाव को बनाने के लिए पांच मिनट इंतज़ार करना सामान्य बात थी.
bazel mobile-install
, Android के लिए बार-बार डेवलपमेंट करने की प्रोसेस को बहुत तेज़ बना देता है. इसके लिए, यह बदलावों को कम करने, काम को बांटने, और Android के इंटरनल को स्मार्ट तरीके से मैनेज करने की तकनीकों का इस्तेमाल करता है. इन सभी तकनीकों का इस्तेमाल, आपके ऐप्लिकेशन के कोड में कोई बदलाव किए बिना किया जाता है.
ऐप्लिकेशन को पारंपरिक तरीके से इंस्टॉल करने में आने वाली समस्याएं
Android ऐप्लिकेशन बनाने में कुछ समस्याएं आती हैं. जैसे:
Dexing. डिफ़ॉल्ट रूप से, "dx" को बिल्ड में सिर्फ़ एक बार शुरू किया जाता है. साथ ही, इसे यह नहीं पता होता कि पिछली बिल्ड का काम कैसे फिर से इस्तेमाल किया जाए: यह हर तरीके को फिर से डेक्स करता है, भले ही सिर्फ़ एक तरीका बदला गया हो.
डिवाइस पर डेटा अपलोड किया जा रहा है. adb, यूएसबी 2.0 कनेक्शन के पूरे बैंडविड्थ का इस्तेमाल नहीं करता है. इसलिए, बड़े ऐप्लिकेशन को अपलोड होने में ज़्यादा समय लग सकता है. पूरे ऐप्लिकेशन को अपलोड किया जाता है. भले ही, सिर्फ़ छोटे-छोटे हिस्सों में बदलाव किया गया हो. उदाहरण के लिए, कोई संसाधन या कोई एक तरीका. इसलिए, यह एक बड़ी समस्या हो सकती है.
नेटिव कोड में कंपाइल करना. Android L में ART को पेश किया गया था. यह एक नया Android रनटाइम है. यह ऐप्लिकेशन को Dalvik की तरह, सिर्फ़-इन-टाइम कंपाइल करने के बजाय, पहले से ही कंपाइल कर देता है. इससे ऐप्लिकेशन बहुत तेज़ी से काम करते हैं. हालांकि, इन्हें इंस्टॉल करने में ज़्यादा समय लगता है. यह उपयोगकर्ताओं के लिए एक अच्छा समझौता है, क्योंकि वे आम तौर पर किसी ऐप्लिकेशन को एक बार इंस्टॉल करते हैं और उसे कई बार इस्तेमाल करते हैं. हालांकि, इससे डेवलपमेंट की प्रोसेस धीमी हो जाती है. ऐसा तब होता है, जब किसी ऐप्लिकेशन को कई बार इंस्टॉल किया जाता है और हर वर्शन को ज़्यादा से ज़्यादा कुछ बार चलाया जाता है.
bazel mobile-install
का अप्रोच
bazel mobile-install
में ये सुधार किए गए हैं:
शार्ड की गई डेक्सिंग. ऐप्लिकेशन का Java कोड बनाने के बाद, Bazel क्लास फ़ाइलों को लगभग बराबर साइज़ के हिस्सों में बांट देता है. इसके बाद, वह उन पर
dx
को अलग-अलग तौर पर लागू करता है.dx
को उन शार्ड पर लागू नहीं किया जाता जिनमें पिछली बिल्ड के बाद से कोई बदलाव नहीं हुआ है.इंक्रीमेंटल फ़ाइल ट्रांसफ़र. Android के संसाधनों, .dex फ़ाइलों, और नेटिव लाइब्रेरी को मुख्य .apk से हटा दिया जाता है. इन्हें मोबाइल-इंस्टॉल डायरेक्ट्री में सेव किया जाता है. इससे पूरे ऐप्लिकेशन को फिर से इंस्टॉल किए बिना, कोड और Android संसाधनों को अलग-अलग अपडेट किया जा सकता है. इसलिए, फ़ाइलों को ट्रांसफ़र करने में कम समय लगता है. साथ ही, डिवाइस पर सिर्फ़ उन .dex फ़ाइलों को फिर से कंपाइल किया जाता है जिनमें बदलाव किया गया है.
ऐप्लिकेशन के कुछ हिस्सों को .apk फ़ाइल के बाहर से लोड किया जा रहा है. .apk में एक छोटा स्टब ऐप्लिकेशन डाला जाता है. यह ऐप्लिकेशन, डिवाइस पर मौजूद मोबाइल-इंस्टॉल डायरेक्ट्री से Android संसाधनों, Java कोड, और नेटिव कोड को लोड करता है. इसके बाद, कंट्रोल को असली ऐप्लिकेशन पर ट्रांसफ़र कर देता है. यह सब ऐप्लिकेशन के लिए पारदर्शी होता है. हालाँकि, कुछ खास मामलों में ऐसा नहीं होता है. इनके बारे में यहाँ बताया गया है.
शार्ड किया गया डेक्सिंग
शार्ड की गई डेक्सिंग का तरीका काफ़ी आसान है: .jar फ़ाइलें बनने के बाद, टूल उन्हें लगभग एक ही साइज़ की अलग-अलग .jar फ़ाइलों में शार्ड करता है. इसके बाद, उन फ़ाइलों पर dx
को लागू करता है जिनमें पिछली बिल्ड के बाद बदलाव किए गए हैं. यह तय करने का लॉजिक कि किन शार्ड को डेक्स करना है, Android के लिए खास नहीं है. यह सिर्फ़ Bazel के सामान्य बदलावों को हटाने वाले एल्गोरिदम का इस्तेमाल करता है.
शार्डिंग एल्गोरिदम के पहले वर्शन में, .class फ़ाइलों को वर्णमाला के क्रम में लगाया जाता था. इसके बाद, सूची को बराबर साइज़ के हिस्सों में काट दिया जाता था. हालांकि, यह तरीका सही नहीं था. अगर कोई क्लास जोड़ी या हटाई जाती थी (भले ही, वह नेस्ट की गई हो या गुमनाम हो), तो वर्णमाला के क्रम में उसके बाद आने वाली सभी क्लास एक से शिफ़्ट हो जाती थीं. इससे उन शार्ड को फिर से डेक्स करना पड़ता था. इसलिए, अलग-अलग क्लास के बजाय Java पैकेज को शार्ड करने का फ़ैसला लिया गया. निश्चित तौर पर, अगर कोई नया पैकेज जोड़ा या हटाया जाता है, तो इससे कई शार्ड इंडेक्स हो जाते हैं. हालांकि, ऐसा किसी एक क्लास को जोड़ने या हटाने की तुलना में बहुत कम होता है.
शार्ड की संख्या को BUILD फ़ाइल से कंट्रोल किया जाता है. इसके लिए, android_binary.dex_shards
एट्रिब्यूट का इस्तेमाल किया जाता है. आदर्श रूप से, Bazel अपने-आप यह तय कर लेता है कि कितने शार्ड सबसे सही हैं. हालांकि, Bazel को फ़िलहाल कोई भी कार्रवाई करने से पहले, कार्रवाइयों के सेट के बारे में पता होना चाहिए. उदाहरण के लिए, बिल्ड के दौरान एक्ज़ीक्यूट की जाने वाली कमांड. इसलिए, यह शार्ड की सबसे सही संख्या का पता नहीं लगा सकता, क्योंकि इसे यह नहीं पता कि ऐप्लिकेशन में आखिर में कितनी Java क्लास होंगी. आम तौर पर, जितने ज़्यादा शार्ड होंगे, बिल्ड और इंस्टॉलेशन उतना ही तेज़ होगा. हालांकि, इससे ऐप्लिकेशन के शुरू होने की प्रोसेस धीमी हो जाती है, क्योंकि डाइनैमिक लिंकर को ज़्यादा काम करना पड़ता है. आम तौर पर, 10 से 50 शार्ड के बीच का डेटा सबसे अच्छा होता है.
इंक्रीमेंटल फ़ाइल ट्रांसफ़र
ऐप्लिकेशन बनाने के बाद, अगला चरण उसे इंस्टॉल करना है. इसके लिए, कम से कम मेहनत करनी पड़े. इंस्टॉल करने की प्रोसेस में ये चरण शामिल हैं:
- .apk फ़ाइल इंस्टॉल करना. आम तौर पर, इसके लिए
adb install
का इस्तेमाल किया जाता है - .dex फ़ाइलों, Android रिसॉर्स, और नेटिव लाइब्रेरी को mobile-install डायरेक्ट्री में अपलोड करना
पहले चरण में, ज़्यादा इंक्रीमेंटैलिटी नहीं होती: ऐप्लिकेशन इंस्टॉल किया जाता है या नहीं. फ़िलहाल, Bazel इस चरण को पूरा करने के लिए उपयोगकर्ता पर निर्भर करता है. इसके लिए, उपयोगकर्ता को --incremental
कमांड लाइन विकल्प का इस्तेमाल करना होता है. ऐसा इसलिए है, क्योंकि Bazel हर मामले में यह तय नहीं कर सकता कि इस चरण को पूरा करना ज़रूरी है या नहीं.
दूसरे चरण में, ऐप्लिकेशन की बिल्ड फ़ाइलों की तुलना डिवाइस पर मौजूद मेनिफ़ेस्ट फ़ाइल से की जाती है. इस फ़ाइल में, डिवाइस पर मौजूद ऐप्लिकेशन की फ़ाइलों और उनके चेकसम की सूची होती है. डिवाइस पर नई फ़ाइलें अपलोड की जाती हैं, बदली गई फ़ाइलें अपडेट की जाती हैं, और हटाई गई फ़ाइलें डिवाइस से मिटा दी जाती हैं. अगर मेनिफ़ेस्ट मौजूद नहीं है, तो यह माना जाता है कि हर फ़ाइल को अपलोड करना होगा.
ध्यान दें कि डिवाइस पर किसी फ़ाइल में बदलाव करके, इंक्रीमेंटल इंस्टॉलेशन के एल्गोरिदम को गुमराह किया जा सकता है. हालांकि, मेनिफ़ेस्ट में मौजूद इसके चेकसम में बदलाव नहीं किया जा सकता. डिवाइस पर फ़ाइलों के चेकसम की गिनती करके, इस समस्या से बचा जा सकता था. हालांकि, ऐसा करने से ऐप्लिकेशन को इंस्टॉल करने में लगने वाला समय बढ़ जाता. इसलिए, इस तरीके को सही नहीं माना गया.
स्टब ऐप्लिकेशन
स्टब ऐप्लिकेशन में, डिवाइस पर मौजूद mobile-install
डायरेक्ट्री से DEX फ़ाइलें, नेटिव कोड, और Android संसाधन लोड करने का काम होता है.
असल लोडिंग, BaseDexClassLoader
को सबक्लास करके लागू की जाती है. साथ ही, यह एक ऐसी तकनीक है जिसके बारे में अच्छी तरह से बताया गया है. ऐसा ऐप्लिकेशन की किसी भी क्लास के लोड होने से पहले होता है, ताकि APK में मौजूद किसी भी ऐप्लिकेशन क्लास को डिवाइस पर मौजूद mobile-install
डायरेक्ट्री में रखा जा सके. इससे उन्हें adb install
के बिना अपडेट किया जा सकता है.
ऐसा ऐप्लिकेशन की किसी भी क्लास को लोड करने से पहले होना चाहिए, ताकि किसी भी ऐप्लिकेशन क्लास को .apk में न रखना पड़े. इसका मतलब है कि उन क्लास में बदलाव करने के लिए, ऐप्लिकेशन को पूरी तरह से फिर से इंस्टॉल करना होगा.
इसके लिए, Application
में बताई गई AndroidManifest.xml
क्लास को स्टब ऐप्लिकेशन से बदल दिया जाता है. जब ऐप्लिकेशन शुरू होता है, तब यह क्लास कंट्रोल करता है. साथ ही, Android फ़्रेमवर्क के इंटरनल पर Java रिफ़्लेक्शन का इस्तेमाल करके, क्लास लोडर और रिसॉर्स मैनेजर को सबसे पहले (इसके कंस्ट्रक्टर) में बदलता है.
स्टब ऐप्लिकेशन, मोबाइल-इंस्टॉल की मदद से इंस्टॉल की गई नेटिव लाइब्रेरी को दूसरी जगह कॉपी करता है. यह ज़रूरी है, क्योंकि डाइनैमिक लिंकर को फ़ाइलों पर X
बिट सेट करने की ज़रूरत होती है. हालांकि, ऐसा किसी भी ऐसी जगह के लिए नहीं किया जा सकता जिसे नॉन-रूट adb
ऐक्सेस कर सकता है.
ये सभी काम पूरे होने के बाद, स्टब ऐप्लिकेशन, असली Application
क्लास को इंस्टैंशिएट करता है. साथ ही, Android फ़्रेमवर्क में मौजूद असली ऐप्लिकेशन के सभी रेफ़रंस को खुद में बदल देता है.
नतीजे
परफ़ॉर्मेंस
आम तौर पर, bazel mobile-install
की वजह से, छोटे बदलाव के बाद बड़े ऐप्लिकेशन को बनाने और इंस्टॉल करने की प्रोसेस 4 से 10 गुना तेज़ हो जाती है.
यहां दिए गए नंबर, Google के कुछ प्रॉडक्ट के लिए कैलकुलेट किए गए हैं:
हालांकि, यह बदलाव के टाइप पर निर्भर करता है. जैसे, बेस लाइब्रेरी में बदलाव करने के बाद फिर से कंपाइल करने में ज़्यादा समय लगता है.
सीमाएं
स्टब ऐप्लिकेशन की ट्रिक्स, हर मामले में काम नहीं करती हैं. यहां कुछ ऐसे उदाहरण दिए गए हैं जिनमें यह सुविधा उम्मीद के मुताबिक काम नहीं करती:
जब
Context
कोContentProvider#onCreate()
मेंApplication
क्लास में कास्ट किया जाता है. इस तरीके को ऐप्लिकेशन स्टार्टअप के दौरान कॉल किया जाता है. इससे पहले कि हमApplication
क्लास के इंस्टेंस को बदल सकें, इसलिएContentProvider
अब भी स्टब ऐप्लिकेशन को रेफ़रंस करेगा, न कि असली ऐप्लिकेशन को. यह एक बग नहीं है, क्योंकि आपकोContext
को इस तरह से डाउनकास्ट नहीं करना चाहिए. हालाँकि, ऐसा Google के कुछ ऐप्लिकेशन में होता है.bazel mobile-install
से इंस्टॉल किए गए रिसॉर्स, सिर्फ़ ऐप्लिकेशन में उपलब्ध होते हैं. अगरbazel mobile-install
के ज़रिए अन्य ऐप्लिकेशन इन रिसॉर्स को ऐक्सेस करते हैं, तो ये रिसॉर्स, आखिरी बार किए गए नॉन-इंक्रीमेंटल इंस्टॉल से लिए जाएंगे.PackageManager#getApplicationResources()
ऐसे डिवाइस जिन पर ART काम नहीं करता. स्टब ऐप्लिकेशन, Froyo और इसके बाद के वर्शन पर अच्छी तरह से काम करता है. हालांकि, Dalvik में एक बग है. इसकी वजह से, कुछ मामलों में ऐप्लिकेशन को गलत माना जाता है. जैसे, जब Java एनोटेशन का इस्तेमाल खास तरीके से किया जाता है. ऐसा तब होता है, जब ऐप्लिकेशन का कोड कई .dex फ़ाइलों में डिस्ट्रिब्यूट किया जाता है. जब तक आपका ऐप्लिकेशन इन गड़बड़ियों को ठीक नहीं करता, तब तक यह Dalvik के साथ भी काम करना चाहिए. हालांकि, ध्यान दें कि हमारा फ़ोकस Android के पुराने वर्शन के लिए सहायता देने पर नहीं है