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 Maps जैसे बड़े प्रोजेक्ट में, एक लाइन में बदलाव करने के लिए पांच मिनट तक इंतज़ार करना आम बात थी.
bazel mobile-install
, बदलावों को कम करने, काम को अलग-अलग हिस्सों में बांटने, और Android के इंटरनल को बेहतर बनाने के लिए, कई तरीकों का इस्तेमाल करता है. इससे, Android के लिए बार-बार डेवलपमेंट करने की प्रोसेस काफ़ी तेज़ हो जाती है. इसके लिए, आपके ऐप्लिकेशन के कोड में कोई बदलाव नहीं किया जाता.
ऐप्लिकेशन को पारंपरिक तरीके से इंस्टॉल करने में आने वाली समस्याएं
Android ऐप्लिकेशन बनाने में कुछ समस्याएं आ सकती हैं. जैसे:
डेक्सिंग. डिफ़ॉल्ट रूप से, "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 के पैकेज को शेयर करने का फ़ैसला लिया गया. हालांकि, अगर कोई नया पैकेज जोड़ा या हटाया जाता है, तो फिर भी कई शर्ड को इंडेक्स किया जाता है. हालांकि, ऐसा किसी एक क्लास को जोड़ने या हटाने के मुकाबले बहुत कम होता है.
android_binary.dex_shards
एट्रिब्यूट का इस्तेमाल करके, BUILD फ़ाइल से स्HARD की संख्या कंट्रोल की जाती है. आम तौर पर, Bazel अपने-आप यह तय कर लेता है कि कितने शर्ड सबसे सही हैं. हालांकि, फ़िलहाल Bazel को किसी भी कार्रवाई को पूरा करने से पहले, कार्रवाइयों का सेट (उदाहरण के लिए, बिल्ड के दौरान चलाए जाने वाले निर्देश) पता होना चाहिए. इसलिए, यह शर्ड की सबसे सही संख्या तय नहीं कर सकता, क्योंकि यह नहीं जानता कि ऐप्लिकेशन में आखिर में कितनी Java क्लास होंगी. आम तौर पर, जितने ज़्यादा शर्ड होंगे, बिल्ड और इंस्टॉल उतना ही तेज़ होगा. हालांकि, ऐप्लिकेशन को शुरू होने में ज़्यादा समय लगेगा, क्योंकि डाइनैमिक लिंकर को ज़्यादा काम करना होगा. आम तौर पर, 10 से 50 शर्ड के बीच का आंकड़ा सबसे सही होता है.
इंक्रीमेंटल फ़ाइल ट्रांसफ़र
ऐप्लिकेशन बनाने के बाद, अगला चरण उसे इंस्टॉल करना है. इंस्टॉलेशन के लिए, ये चरण अपनाएं:
- .apk इंस्टॉल करना (आम तौर पर
adb install
का इस्तेमाल करके) - .dex फ़ाइलों, Android रिसॉर्स, और नेटिव लाइब्रेरी को मोबाइल-इंस्टॉल डायरेक्ट्री में अपलोड करना
पहले चरण में, ज़्यादा बढ़ोतरी नहीं होती: ऐप्लिकेशन या तो इंस्टॉल होता है या नहीं. फ़िलहाल, Bazel यह तय करने के लिए उपयोगकर्ता पर निर्भर करता है कि उसे --incremental
कमांड लाइन विकल्प के ज़रिए यह चरण पूरा करना चाहिए या नहीं. ऐसा इसलिए, क्योंकि यह सभी मामलों में यह तय नहीं कर सकता कि यह ज़रूरी है या नहीं.
दूसरे चरण में, बिल्ड से ऐप्लिकेशन की फ़ाइलों की तुलना, डिवाइस पर मौजूद मेनिफ़ेस्ट फ़ाइल से की जाती है. इस फ़ाइल में, डिवाइस पर मौजूद ऐप्लिकेशन की फ़ाइलों और उनके चेकसम की सूची होती है. नई फ़ाइलें डिवाइस पर अपलोड की जाती हैं, बदली गई फ़ाइलों को अपडेट किया जाता है, और हटाई गई फ़ाइलों को डिवाइस से मिटा दिया जाता है. अगर मेनिफ़ेस्ट मौजूद नहीं है, तो यह माना जाता है कि हर फ़ाइल को अपलोड करना ज़रूरी है.
ध्यान दें कि डिवाइस पर किसी फ़ाइल में बदलाव करके, इंक्रीमेंटल इंस्टॉलेशन एल्गोरिदम को गुमराह किया जा सकता है. हालांकि, मेनिफ़ेस्ट में मौजूद उसके चेकसम में बदलाव नहीं किया जा सकता. डिवाइस पर मौजूद फ़ाइलों का चेकसम कैलकुलेट करके, इस समस्या से बचा जा सकता था. हालांकि, इंस्टॉलेशन में लगने वाले समय को बढ़ाने के लिए, ऐसा करना सही नहीं लगा.
स्टब ऐप्लिकेशन
स्टब ऐप्लिकेशन में, डिवाइस पर मौजूद mobile-install
डायरेक्ट्री से डेक्स, नेटिव कोड, और Android संसाधनों को लोड करने की सुविधा होती है.
असल लोडिंग, BaseDexClassLoader
को सबक्लास करके लागू की जाती है. यह एक ऐसी तकनीक है जिसके बारे में काफ़ी जानकारी उपलब्ध है. यह ऐप्लिकेशन की किसी भी क्लास के लोड होने से पहले होता है, ताकि APK में मौजूद किसी भी ऐप्लिकेशन क्लास को डिवाइस पर मौजूद mobile-install
डायरेक्ट्री में रखा जा सके. इससे, उन्हें adb install
के बिना अपडेट किया जा सकता है.
यह ऐप्लिकेशन की किसी भी क्लास के लोड होने से पहले होना चाहिए, ताकि किसी भी ऐप्लिकेशन क्लास को .apk में शामिल न करना पड़े. इसका मतलब है कि उन क्लास में बदलाव करने के लिए, उन्हें फिर से पूरी तरह से इंस्टॉल करना होगा.
ऐसा करने के लिए, AndroidManifest.xml
में बताई गई Application
क्लास को स्टब ऐप्लिकेशन से बदलें. यह ऐप्लिकेशन शुरू होने पर कंट्रोल ले लेता है. साथ ही, Android फ़्रेमवर्क के अंदरूनी हिस्सों पर Java रिफ़्लेक्शन का इस्तेमाल करके, क्लास लोडर और रिसॉर्स मैनेजर को सबसे पहले (इसके कन्स्ट्रक्टर) सही तरीके से बदल देता है.
स्टब ऐप्लिकेशन, मोबाइल-इंस्टॉल की मदद से इंस्टॉल की गई नेटिव लाइब्रेरी को किसी दूसरी जगह पर कॉपी भी करता है. ऐसा करना ज़रूरी है, क्योंकि डाइनैमिक लिंकर को फ़ाइलों पर X
बिट सेट करने की ज़रूरत होती है. ऐसा किसी ऐसी जगह के लिए नहीं किया जा सकता जिसे नॉन-रूट adb
ऐक्सेस कर सकता है.
ये सभी काम करने के बाद, स्टब ऐप्लिकेशन असल Application
क्लास को इंस्टैंशिएट करता है. साथ ही, Android फ़्रेमवर्क में अपने सभी रेफ़रंस को असल ऐप्लिकेशन में बदल देता है.
नतीजे
परफ़ॉर्मेंस
आम तौर पर, bazel mobile-install
की मदद से, बड़े ऐप्लिकेशन बनाने और इंस्टॉल करने में लगने वाला समय, छोटे बदलाव के बाद चार से 10 गुना कम हो जाता है.
यहां दिए गए आंकड़े, Google के कुछ प्रॉडक्ट के लिए कैलकुलेट किए गए हैं:
हालांकि, यह इस बात पर निर्भर करता है कि बदलाव किस तरह का है: किसी बेस लाइब्रेरी में बदलाव करने के बाद, फिर से कंपाइल करने में ज़्यादा समय लगता है.
सीमाएं
स्टब ऐप्लिकेशन की चालें हर मामले में काम नहीं करतीं. यहां कुछ ऐसे मामले दिए गए हैं जिनमें यह सुविधा उम्मीद के मुताबिक काम नहीं करती:
जब
Context
कोContentProvider#onCreate()
मेंApplication
क्लास पर कास्ट किया जाता है. इस तरीके को ऐप्लिकेशन के शुरू होने के दौरान,Application
क्लास के इंस्टेंस को बदलने से पहले कॉल किया जाता है. इसलिए,ContentProvider
अब भी असली ऐप्लिकेशन के बजाय स्टब ऐप्लिकेशन का रेफ़रंस देगा. ऐसा हो सकता है कि यह कोई गड़बड़ी न हो, क्योंकिContext
को इस तरह डाउनकास्ट नहीं किया जाना चाहिए. हालांकि, ऐसा Google के कुछ ऐप्लिकेशन में होता है.bazel mobile-install
से इंस्टॉल किए गए रिसॉर्स, सिर्फ़ ऐप्लिकेशन में ही उपलब्ध होते हैं. अगरPackageManager#getApplicationResources()
के ज़रिए अन्य ऐप्लिकेशन रिसॉर्स ऐक्सेस करते हैं, तो ये रिसॉर्स, इंक्रीमेंटल इंस्टॉल के बजाय, पिछले इंस्टॉल से होंगे.ऐसे डिवाइस जिन पर ART काम नहीं कर रहा है. स्टब ऐप्लिकेशन, Froyo और उसके बाद के वर्शन पर अच्छी तरह से काम करता है. हालांकि, Dalvik में एक गड़बड़ी है, जिसकी वजह से कुछ मामलों में ऐप्लिकेशन का कोड कई .dex फ़ाइलों में डिस्ट्रिब्यूट होने पर, उसे गलत माना जाता है. उदाहरण के लिए, जब Java एनोटेशन का इस्तेमाल खास तरीके से किया जाता है. जब तक आपका ऐप्लिकेशन इन गड़बड़ियों से नहीं जुड़ा है, तब तक यह Dalvik के साथ भी काम करेगा. हालांकि, ध्यान दें कि हमारा मकसद Android के पुराने वर्शन के लिए सहायता उपलब्ध कराना नहीं है