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
बेज़ल को बताता है कि किस adb बाइनरी का इस्तेमाल करना है--adb_arg
का इस्तेमाल,adb
की कमांड लाइन में अतिरिक्त आर्ग्युमेंट जोड़ने के लिए किया जा सकता है. इसका एक फ़ायदा यह है कि अगर आपके वर्कस्टेशन से एक से ज़्यादा डिवाइस कनेक्ट हैं, तो यह चुना जा सकता है कि आपको किस डिवाइस पर ऐप्लिकेशन इंस्टॉल करना है:bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
--start_app
अपने-आप ऐप्लिकेशन चालू कर देगा
किसी भी तरह का संदेह होने पर, उदाहरण देखें या हमसे संपर्क करें.
परिचय
डेवलपर के टूलचेन का सबसे अहम एट्रिब्यूट, स्पीड है: कोड में बदलाव करने और उसे एक सेकंड में चलने के बीच का फ़र्क़ बहुत ज़्यादा है. साथ ही, आपको यह जानने के लिए मिनटों या कभी-कभी घंटों तक इंतज़ार करना पड़ता है कि आपके बदलावों से आपको उम्मीद के मुताबिक नतीजे मिल रहे हैं या नहीं.
दुर्भाग्य से, .apk बनाने के लिए पारंपरिक Android टूलचेन में कई मोनोलिथिक, क्रम से चलने वाले चरण ज़रूरी होते हैं और एक Android ऐप्लिकेशन बनाने के लिए इन सभी को पूरा करना पड़ता है. Google में, 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 में एक छोटा स्टब ऐप्लिकेशन डाला जाता है, जो डिवाइस पर मौजूद मोबाइल-इंस्टॉल डायरेक्ट्री से Android संसाधन, Java कोड, और नेटिव कोड लोड करता है. इसके बाद, यह कंट्रोल को असली ऐप्लिकेशन पर ट्रांसफ़र करता है. यह ऐप्लिकेशन के लिए पूरी तरह से पारदर्शी है. हालांकि, कुछ खास मामलों में ऐसा नहीं होता, जिनके बारे में यहां बताया गया है.
डेटा को अलग-अलग हिस्सों में बांटकर डीकोड करना
शार्ड डेक्सिंग का तरीका आसान है: .Jर फ़ाइलें बन जाने के बाद, एक टूल उन्हें करीब-करीब बराबर साइज़ की अलग-अलग .Jर फ़ाइलों में बदल देता है. इसके बाद, उन फ़ाइलों पर dx
शुरू करता है जिनमें पिछले बिल्ड के बाद बदलाव किए गए थे. वह तर्क जो
तय करता है कि कौन से शार्ड को डेक्स करना है, यह खास तौर पर Android के लिए नहीं है: यह सिर्फ़ 'बेज़ल' के बदलाव की काट-छांट करने वाला
सामान्य एल्गोरिदम इस्तेमाल करता है.
शार्डिंग एल्गोरिदम के पहले वर्शन में बस .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 पर सही तरीके से काम करता है और बाद में, Delvik को एक गड़बड़ी मिली है. इस गड़बड़ी की वजह से अगर किसी ऐप्लिकेशन के कोड को कुछ मामलों में कई .dex फ़ाइलों में बांटा गया है, तो वह ऐप्लिकेशन गलत है. उदाहरण के लिए, जब Java एनोटेशन का किसी खास तरीके से इस्तेमाल किया जाता है. जब तक आपका ऐप्लिकेशन इन गड़बड़ियों से नहीं जुड़ा है, तब तक यह Dalvik के साथ भी काम करेगा. हालांकि, ध्यान दें कि हमारा मकसद Android के पुराने वर्शन के लिए सहायता उपलब्ध कराना नहीं है