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का इस्तेमाल किया जा सकता है. इसका एक फ़ायदा यह है कि अगर आपके वर्कस्टेशन से एक से ज़्यादा डिवाइस कनेक्ट हैं, तो यह चुना जा सकता है कि आपको किस डिवाइस पर ऐप्लिकेशन इंस्टॉल करना है: to if you have multiple devices connected to your workstation: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" को बिल्ड में सिर्फ़ एक बार लागू किया जाता है. साथ ही, इसे यह नहीं पता होता कि पिछली बिल्ड से काम को कैसे फिर से इस्तेमाल किया जाए. इसलिए, यह हर तरीके को फिर से dex करता है. भले ही, सिर्फ़ एक तरीका बदला गया हो.
डिवाइस पर डेटा अपलोड करना. adb, यूएसबी 2.0 कनेक्शन की पूरी बैंडविथ का इस्तेमाल नहीं करता. इसलिए, बड़े ऐप्लिकेशन को अपलोड करने में ज़्यादा समय लग सकता है. पूरा ऐप्लिकेशन अपलोड किया जाता है. भले ही, उसके सिर्फ़ छोटे-छोटे हिस्सों में बदलाव किया गया हो. जैसे, कोई संसाधन या कोई एक तरीका. इसलिए, यह एक बड़ी समस्या हो सकती है.
नेटिव कोड में कंपाइल करना. Android L में ART को पेश किया गया था. यह Android का नया रनटाइम है. यह ऐप्लिकेशन को Dalvik की तरह, सिर्फ़-इन-टाइम कंपाइल करने के बजाय, पहले से कंपाइल करता है. इससे ऐप्लिकेशन ज़्यादा तेज़ी से काम करते हैं. हालांकि, इन्हें इंस्टॉल करने में ज़्यादा समय लगता है. लोगों के लिए यह एक अच्छा विकल्प है, क्योंकि वे आम तौर पर किसी ऐप्लिकेशन को एक बार इंस्टॉल करते हैं और उसका कई बार इस्तेमाल करते हैं. हालांकि, इससे डेवलपमेंट की प्रोसेस धीमी हो जाती है, क्योंकि किसी ऐप्लिकेशन को कई बार इंस्टॉल किया जाता है और हर वर्शन को ज़्यादा से ज़्यादा कुछ ही बार चलाया जाता है.
bazel mobile-install का तरीका
bazel mobile-install की मदद से, ये सुधार किए जा सकते हैं:
शार्डेड dexing. ऐप्लिकेशन का Java कोड बनाने के बाद, Bazel क्लास फ़ाइलों को लगभग बराबर साइज़ के हिस्सों में बांटता है और उन पर अलग-अलग
dxलागू करता है. जिन हिस्सों में पिछली बिल्ड के बाद से कोई बदलाव नहीं हुआ है उन परdxलागू नहीं किया जाता.इंक्रीमेंटल फ़ाइल ट्रांसफ़र. Android के संसाधन, .dex फ़ाइलें, और नेटिव लाइब्रेरी, मुख्य .apk से हटा दी जाती हैं. इन्हें mobile-install डायरेक्ट्री में सेव किया जाता है. इससे पूरे ऐप्लिकेशन को फिर से इंस्टॉल किए बिना, कोड और Android के संसाधनों को अलग-अलग अपडेट किया जा सकता है. इसलिए, फ़ाइलों को ट्रांसफ़र करने में कम समय लगता है. साथ ही, डिवाइस पर सिर्फ़ उन .dex फ़ाइलों को फिर से कंपाइल किया जाता है जिनमें बदलाव किया गया है.
.apk के बाहर से ऐप्लिकेशन के हिस्सों को लोड करना. .apk में एक छोटा स्टब ऐप्लिकेशन डाला जाता है. यह डिवाइस पर मौजूद mobile-install डायरेक्ट्री से Android के संसाधन, Java कोड, और नेटिव कोड लोड करता है. इसके बाद, कंट्रोल को असली ऐप्लिकेशन पर ट्रांसफ़र कर देता है. यह सब ऐप्लिकेशन के लिए पारदर्शी होता है. हालांकि, कुछ खास मामलों में ऐसा नहीं होता. इनके बारे में नीचे बताया गया है.
शार्डेड Dexing
शार्डेड dexing का तरीका काफ़ी आसान है: .jar फ़ाइलें बनने के बाद, एक
टूल
उन्हें लगभग बराबर साइज़ की अलग-अलग .jar फ़ाइलों में बांटता है. इसके बाद, पिछली बिल्ड के बाद से बदली गई फ़ाइलों पर
dx लागू करता है. यह तय करने की प्रोसेस कि किन हिस्सों को dex करना है, Android के लिए खास नहीं है. इसमें Bazel के सामान्य बदलावों को कम करने वाले एल्गोरिदम का इस्तेमाल किया जाता है.
शार्डिंग एल्गोरिदम के पहले वर्शन में, .class फ़ाइलों को वर्णमाला के क्रम में रखा जाता था. इसके बाद, सूची को बराबर साइज़ के हिस्सों में बांटा जाता था. हालांकि, यह तरीका सही नहीं था: अगर कोई क्लास जोड़ी या हटाई जाती थी (चाहे वह नेस्टेड हो या एनॉनिमस), तो वर्णमाला के क्रम में उसके बाद आने वाली सभी क्लास एक-एक करके शिफ़्ट हो जाती थीं. इससे उन हिस्सों को फिर से dex करना पड़ता था. इसलिए, अलग-अलग क्लास के बजाय, Java पैकेज को शार्ड करने का फ़ैसला लिया गया. ज़ाहिर है कि अगर कोई नया पैकेज जोड़ा या हटाया जाता है, तो अब भी कई हिस्सों को dex करना पड़ता है. हालांकि, ऐसा किसी एक क्लास को जोड़ने या हटाने के मुकाबले बहुत कम होता है.
BUILD फ़ाइल (जिसमें android_binary.dex_shards एट्रिब्यूट का इस्तेमाल किया जाता है) से, हिस्सों की संख्या को कंट्रोल किया जाता है. आदर्श स्थिति में, Bazel अपने-आप यह तय कर लेगा कि कितने हिस्से सबसे सही हैं. हालांकि, फ़िलहाल Bazel को कोई भी कार्रवाई (जैसे, बिल्ड के दौरान लागू किए जाने वाले निर्देश) करने से पहले, उसके बारे में पता होना चाहिए. इसलिए, यह हिस्सों की सही संख्या तय नहीं कर सकता, क्योंकि इसे यह नहीं पता कि ऐप्लिकेशन में आखिर में कितनी Java क्लास होंगी. आम तौर पर, जितने ज़्यादा हिस्से होंगे, बिल्ड और इंस्टॉलेशन उतना ही तेज़ होगा. हालांकि, ऐप्लिकेशन शुरू होने में उतना ही ज़्यादा समय लगेगा, क्योंकि डाइनैमिक लिंकर को ज़्यादा काम करना पड़ता है. आम तौर पर, 10 से 50 हिस्से सबसे सही होते हैं.
इंक्रीमेंटल फ़ाइल ट्रांसफ़र
ऐप्लिकेशन बनाने के बाद, अगला चरण उसे इंस्टॉल करना होता है. कोशिश करें कि इसे कम से कम मेहनत में इंस्टॉल किया जाए. इंस्टॉल करने के लिए, यह तरीका अपनाएं:
- .apk इंस्टॉल करना (आम तौर पर,
adb installका इस्तेमाल करके) - .dex फ़ाइलें, Android के संसाधन, और नेटिव लाइब्रेरी को mobile-install डायरेक्ट्री में अपलोड करना
पहले चरण में, ज़्यादा इंक्रीमेंटैलिटी नहीं होती: ऐप्लिकेशन या तो इंस्टॉल होता है या नहीं. फ़िलहाल, Bazel इस चरण को पूरा करने के लिए, उपयोगकर्ता पर निर्भर करता है. इसके लिए, कमांड लाइन में --incremental विकल्प का इस्तेमाल किया जाता है. इसकी वजह यह है कि Bazel हर मामले में यह तय नहीं कर सकता कि यह चरण ज़रूरी है या नहीं.
दूसरे चरण में, बिल्ड से ऐप्लिकेशन की फ़ाइलों की तुलना, डिवाइस पर मौजूद मेनिफ़ेस्ट फ़ाइल से की जाती है. इस फ़ाइल में, डिवाइस पर मौजूद ऐप्लिकेशन की फ़ाइलों और उनके चेकसम की सूची होती है. डिवाइस पर नई फ़ाइलें अपलोड की जाती हैं, बदली गई फ़ाइलें अपडेट की जाती हैं, और हटाई गई फ़ाइलें डिवाइस से मिटा दी जाती हैं. अगर मेनिफ़ेस्ट मौजूद नहीं है, तो यह मान लिया जाता है कि हर फ़ाइल को अपलोड करना ज़रूरी है.
ध्यान दें कि डिवाइस पर किसी फ़ाइल में बदलाव करके, इंक्रीमेंटल इंस्टॉलेशन एल्गोरिदम को धोखा दिया जा सकता है. हालांकि, मेनिफ़ेस्ट में उसके चेकसम में बदलाव नहीं किया जा सकता. डिवाइस पर मौजूद फ़ाइलों का चेकसम कंप्यूट करके, इससे बचा जा सकता था. हालांकि, ऐसा करने से इंस्टॉलेशन में लगने वाला समय बढ़ जाता. इसलिए, ऐसा नहीं किया गया.
स्टब ऐप्लिकेशन
स्टब ऐप्लिकेशन की मदद से, डिवाइस पर मौजूद mobile-install डायरेक्ट्री से dexes, नेटिव कोड, और Android के संसाधन लोड किए जाते हैं.
असल में, लोडिंग की प्रोसेस को BaseDexClassLoader को सबक्लास करके लागू किया जाता है. यह एक अच्छी तरह से दस्तावेज़ में बताई गई तकनीक है. यह ऐप्लिकेशन की किसी भी क्लास के लोड होने से पहले होता है. इसलिए, apk में मौजूद किसी भी ऐप्लिकेशन क्लास को डिवाइस पर मौजूद mobile-install डायरेक्ट्री में रखा जा सकता है, ताकि उन्हें adb install के बिना अपडेट किया जा सके.
यह ऐप्लिकेशन की किसी भी क्लास के लोड होने से पहले होना चाहिए, ताकि .apk में कोई ऐप्लिकेशन क्लास न हो. इसका मतलब है कि उन क्लास में बदलाव करने के लिए, पूरे ऐप्लिकेशन को फिर से इंस्टॉल करना होगा.
इसके लिए, Application क्लास को
AndroidManifest.xml में बताए गए
स्टब ऐप्लिकेशन से बदल दिया जाता है. जब ऐप्लिकेशन शुरू होता है, तो यह कंट्रोल लेता है. साथ ही, Android फ़्रेमवर्क के इंटरनल पर Java रिफ़्लेक्शन का इस्तेमाल करके, क्लास लोडर और रिसोर्स मैनेजर को सबसे पहले (इसके कंस्ट्रक्टर) सही तरीके से बदलता है.
स्टब ऐप्लिकेशन, mobile-install से इंस्टॉल की गई नेटिव लाइब्रेरी को किसी दूसरी जगह पर कॉपी भी करता है. यह ज़रूरी है, क्योंकि डाइनैमिक लिंकर को फ़ाइलों पर 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 और उसके बाद के वर्शन पर अच्छी तरह से काम करता है. हालांकि, Dalvik में एक गड़बड़ी है. इसकी वजह से, कुछ मामलों में यह मानता है कि ऐप्लिकेशन सही नहीं है. जैसे, जब Java एनोटेशन का इस्तेमाल किसी खास तरीके से किया जाता है, तो ऐप्लिकेशन का कोड कई .dex फ़ाइलों में बांटा जाता है. जब तक आपका ऐप्लिकेशन इन गड़बड़ियों को ट्रिगर नहीं करता, तब तक यह Dalvik के साथ भी काम करेगा. हालांकि, ध्यान दें कि हमारा फ़ोकस, Android के पुराने वर्शन के लिए सहायता उपलब्ध कराना नहीं है