bazel मोबाइल-इंस्टॉल

Android के लिए, तेज़ी से इटरेटिव डेवलपमेंट

इस पेज पर बताया गया है कि bazel mobile-install, Android के लिए बार-बार डेवलपमेंट करने की प्रोसेस को कैसे ज़्यादा तेज़ बनाता है. इसमें, ऐप्लिकेशन इंस्टॉल करने के पारंपरिक तरीके की चुनौतियों के मुकाबले, इस तरीके के फ़ायदों के बारे में बताया गया है.

खास जानकारी

Android ऐप्लिकेशन में छोटे-छोटे बदलावों को तुरंत इंस्टॉल करने के लिए, यह तरीका अपनाएं:

  1. उस ऐप्लिकेशन का android_binary नियम ढूंढें जिसे आपको इंस्टॉल करना है.
  2. proguard_specs एट्रिब्यूट हटाकर, Proguard को बंद करें.
  3. multidex एट्रिब्यूट को native पर सेट करें.
  4. dex_shards एट्रिब्यूट को 10 पर सेट करें.
  5. यूएसबी के ज़रिए, ART (Dalvik नहीं) का इस्तेमाल करने वाले डिवाइस को कनेक्ट करें और उस पर यूएसबी डीबगिंग चालू करें.
  6. bazel mobile-install :your_target रन करें. ऐप्लिकेशन को चालू होने में सामान्य से थोड़ा ज़्यादा समय लगेगा.
  7. कोड या Android संसाधनों में बदलाव करें.
  8. bazel mobile-install --incremental :your_target रन करें.
  9. अब आपको ज़्यादा इंतज़ार नहीं करना पड़ेगा.

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 शार्ड के बीच की संख्या सबसे सही होती है.

इंक्रीमेंटल फ़ाइल ट्रांसफ़र

ऐप्लिकेशन बनाने के बाद, अगला चरण उसे इंस्टॉल करना है. इसके लिए, कम से कम मेहनत करनी चाहिए. इंस्टॉल करने की प्रोसेस में ये चरण शामिल हैं:

  1. .apk फ़ाइल इंस्टॉल करना. आम तौर पर, इसके लिए adb install का इस्तेमाल किया जाता है
  2. .dex फ़ाइलें, Android के संसाधन, और नेटिव लाइब्रेरी को मोबाइल-इंस्टॉल डायरेक्ट्री में अपलोड करना

पहले चरण में, ज़्यादा इंक्रीमेंटैलिटी नहीं होती: ऐप्लिकेशन इंस्टॉल किया जाता है या नहीं. Bazel, फ़िलहाल इस चरण को पूरा करने के लिए उपयोगकर्ता पर निर्भर करता है. उपयोगकर्ता, --incremental कमांड लाइन विकल्प के ज़रिए यह तय करता है कि Bazel को इस चरण को पूरा करना चाहिए या नहीं. ऐसा इसलिए है, क्योंकि Bazel यह तय नहीं कर सकता कि सभी मामलों में इस चरण को पूरा करना ज़रूरी है या नहीं.

दूसरे चरण में, बिल्ड से ऐप्लिकेशन की फ़ाइलों की तुलना, डिवाइस पर मौजूद मेनिफ़ेस्ट फ़ाइल से की जाती है. इस फ़ाइल में, डिवाइस पर मौजूद ऐप्लिकेशन की फ़ाइलों और उनके चेकसम की सूची होती है. डिवाइस पर नई फ़ाइलें अपलोड की जाती हैं, बदली गई फ़ाइलों को अपडेट किया जाता है, और हटाई गई फ़ाइलों को डिवाइस से मिटा दिया जाता है. अगर मेनिफ़ेस्ट मौजूद नहीं है, तो यह माना जाता है कि हर फ़ाइल को अपलोड करना होगा.

ध्यान दें कि डिवाइस पर किसी फ़ाइल में बदलाव करके, इंक्रीमेंटल इंस्टॉलेशन के एल्गोरिदम को गुमराह किया जा सकता है. हालांकि, मेनिफ़ेस्ट में मौजूद इसके चेकसम को गुमराह नहीं किया जा सकता. डिवाइस पर मौजूद फ़ाइलों के चेकसम की गिनती करके, इस समस्या से बचा जा सकता था. हालांकि, ऐसा करने से ऐप्लिकेशन को इंस्टॉल करने में लगने वाला समय बढ़ जाता. इसलिए, इस तरीके को सही नहीं माना गया.

स्टब ऐप्लिकेशन

स्टब ऐप्लिकेशन में, डिवाइस पर मौजूद mobile-install डायरेक्ट्री से DEX फ़ाइलें, नेटिव कोड, और Android संसाधन लोड करने का काम होता है.

असल लोडिंग, BaseDexClassLoader को सबक्लास करके लागू की जाती है. यह एक ऐसी तकनीक है जिसके बारे में अच्छी तरह से बताया गया है. ऐसा ऐप्लिकेशन की किसी भी क्लास के लोड होने से पहले होता है, ताकि APK में मौजूद किसी भी ऐप्लिकेशन क्लास को डिवाइस पर मौजूद mobile-install डायरेक्ट्री में रखा जा सके. इससे उन्हें adb install के बिना अपडेट किया जा सकता है.

ऐसा ऐप्लिकेशन की किसी भी क्लास को लोड करने से पहले होना चाहिए, ताकि किसी भी ऐप्लिकेशन क्लास को .apk में न रखा जाए. इसका मतलब है कि उन क्लास में बदलाव करने के लिए, ऐप्लिकेशन को पूरी तरह से फिर से इंस्टॉल करना होगा.

इसके लिए, AndroidManifest.xml में बताई गई Application क्लास को स्टब ऐप्लिकेशन से बदल दिया जाता है. जब ऐप्लिकेशन शुरू होता है, तब यह क्लास कंट्रोल करता है. साथ ही, 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 के पुराने वर्शन के लिए सहायता उपलब्ध कराना नहीं है