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