आपका ऐप्लिकेशन नेटवर्क से जो अनुरोध करता है वे बैटरी खर्च होने की मुख्य वजह होते हैं. ऐसा इसलिए होता है, क्योंकि ये अनुरोध, ज़्यादा बैटरी खर्च करने वाले मोबाइल या वाई-फ़ाई रेडियो को चालू करते हैं. पैकेट भेजने और पाने के लिए ज़रूरी पावर के अलावा, ये रेडियो सिर्फ़ चालू होने और चालू रखने के लिए भी ज़्यादा पावर खर्च करते हैं. हर 15 सेकंड में नेटवर्क का अनुरोध करने जैसी कोई छोटी सी बात भी, मोबाइल रेडियो को लगातार चालू रख सकती है और बैटरी की लाइफ़ को तेज़ी से खत्म कर सकती है.
नियमित अपडेट तीन तरह के होते हैं:
- उपयोगकर्ता ने इसकी प्रोसेस शुरू की. उपयोगकर्ता के किसी व्यवहार के आधार पर अपडेट करना. जैसे, पुल-टू-रीफ़्रेश जेस्चर.
- ऐप्लिकेशन से शुरू की गई. बार-बार अपडेट करना.
- सर्वर से शुरू किया गया. किसी सर्वर से मिली सूचना के जवाब में अपडेट करना.
इस विषय में इनमें से हर एक के बारे में बताया गया है. साथ ही, बैटरी खर्च को कम करने के लिए, इन्हें ऑप्टिमाइज़ करने के अन्य तरीकों के बारे में भी बताया गया है.
उपयोगकर्ता से मिले अनुरोधों को ऑप्टिमाइज़ करना
आम तौर पर, उपयोगकर्ता के अनुरोध, उपयोगकर्ता के किसी व्यवहार के जवाब में होते हैं. उदाहरण के लिए, नए समाचार लेख पढ़ने के लिए इस्तेमाल किए जाने वाले ऐप्लिकेशन में, उपयोगकर्ता को नए लेख देखने के लिए, 'पुल-टू-रिफ़्रेश' जेस्चर करने की अनुमति दी जा सकती है. नेटवर्क के इस्तेमाल को ऑप्टिमाइज़ करते समय, उपयोगकर्ता के अनुरोधों का जवाब देने के लिए, यहां दी गई तकनीकों का इस्तेमाल किया जा सकता है.
उपयोगकर्ता के अनुरोधों को कम करना
अगर उपयोगकर्ता के कुछ अनुरोधों की ज़रूरत नहीं है, तो उन्हें अनदेखा किया जा सकता है. जैसे, मौजूदा डेटा के अपडेट होने के बावजूद, नए डेटा की जांच करने के लिए, कुछ समय के अंदर कई बार 'पुल-टू-रिफ़्रेश' जेस्चर का इस्तेमाल करना. हर अनुरोध पर कार्रवाई करने से, रेडियो को चालू रखने पर काफ़ी बिजली खर्च हो सकती है. उपयोगकर्ता से मिले अनुरोधों को कम करना, एक बेहतर तरीका है. इससे, किसी तय समयावधि में सिर्फ़ एक अनुरोध किया जा सकता है. इससे रेडियो का इस्तेमाल कम होता है.
कैश मेमोरी का इस्तेमाल करना
अपने ऐप्लिकेशन के डेटा को कैश मेमोरी में सेव करके, उस जानकारी की एक स्थानीय कॉपी बनाई जाती है जिसका रेफ़रंस आपके ऐप्लिकेशन को चाहिए. इसके बाद, आपका ऐप्लिकेशन जानकारी की एक ही लोकल कॉपी को कई बार ऐक्सेस कर सकता है. इसके लिए, उसे नए अनुरोध करने के लिए नेटवर्क कनेक्शन खोलने की ज़रूरत नहीं पड़ती.
आपको ज़्यादा से ज़्यादा डेटा को कैश मेमोरी में सेव करना चाहिए. इसमें स्टैटिक संसाधन और ऑन-डिमांड डाउनलोड, जैसे कि फ़ुल साइज़ इमेज शामिल हैं. एचटीटीपी कैश मेमोरी हेडर का इस्तेमाल करके, यह पक्का किया जा सकता है कि कैश मेमोरी सेव करने की रणनीति की वजह से, आपका ऐप्लिकेशन पुराना डेटा न दिखाए. नेटवर्क के जवाबों को कैश मेमोरी में सेव करने के बारे में ज़्यादा जानने के लिए, अनावश्य डाउनलोड से बचना लेख पढ़ें.
Android 11 और उसके बाद के वर्शन पर, आपका ऐप्लिकेशन उन बड़े डेटासेट का इस्तेमाल कर सकता है जिनका इस्तेमाल, दूसरे ऐप्लिकेशन मशीन लर्निंग और मीडिया चलाने जैसे कामों के लिए करते हैं. जब आपके ऐप्लिकेशन को शेयर किए गए डेटासेट को ऐक्सेस करना होता है, तो वह नई कॉपी डाउनलोड करने से पहले, कैश मेमोरी में सेव किए गए वर्शन की जांच कर सकता है. शेयर किए गए डेटासेट के बारे में ज़्यादा जानने के लिए, शेयर किए गए डेटासेट ऐक्सेस करना लेख पढ़ें.
कम समय में ज़्यादा डेटा डाउनलोड करने के लिए, ज़्यादा बैंडविड्थ का इस्तेमाल करना
वायरलेस रेडियो से कनेक्ट होने पर, आम तौर पर ज़्यादा बैंडविड्थ के लिए ज़्यादा बैटरी खर्च होती है. इसका मतलब है कि 5G, आम तौर पर LTE से ज़्यादा ऊर्जा खर्च करता है. यह 3G से भी ज़्यादा महंगा है.
इसका मतलब है कि रेडियो की मौजूदा स्थिति, रेडियो टेक्नोलॉजी के आधार पर अलग-अलग होती है. हालांकि, आम तौर पर, बैंडविड्थ वाले रेडियो के लिए, स्थिति में बदलाव होने में लगने वाले समय का असर बैटरी पर ज़्यादा होता है. टेल-टाइम के बारे में ज़्यादा जानकारी के लिए, रेडियो स्टेट मशीन देखें.
साथ ही, ज़्यादा बैंडविथ का मतलब है कि एक ही समय में ज़्यादा डेटा डाउनलोड करके, ज़्यादा तेज़ी से प्रीफ़ेच किया जा सकता है. शायद यह बात आपके लिए ज़्यादा काम की न हो, क्योंकि बैटरी के खत्म होने के बाद भी डिवाइस को चालू रखने की लागत ज़्यादा होती है. इसलिए, हर ट्रांसफ़र सेशन के दौरान रेडियो को लंबे समय तक चालू रखना ज़्यादा फ़ायदेमंद होता है. इससे अपडेट की फ़्रीक्वेंसी कम होती है.
उदाहरण के लिए, अगर किसी एलटीई रेडियो का बैंडविड्थ 3G के मुकाबले दोगुना है और ऊर्जा की लागत भी दोगुनी है, तो आपको हर सेशन के दौरान चार गुना ज़्यादा डेटा डाउनलोड करना चाहिए. इसके अलावा, 10 एमबी तक का डेटा भी डाउनलोड किया जा सकता है. इतना डेटा डाउनलोड करते समय, यह ध्यान रखना ज़रूरी है कि डिवाइस के उपलब्ध स्टोरेज पर, प्रीफ़ेच करने की सुविधा का क्या असर पड़ेगा. साथ ही, प्रीफ़ेच कैश मेमोरी को समय-समय पर फ़्लश करना न भूलें.
डिफ़ॉल्ट नेटवर्क के लिए, ConnectivityManager
का इस्तेमाल करके, किसी लिसनर को रजिस्टर किया जा सकता है. साथ ही, डिवाइस के मौजूदा कनेक्शन टाइप का पता लगाने के लिए, TelephonyManager
का इस्तेमाल करके, PhoneStateListener
को रजिस्टर किया जा सकता है. कनेक्शन टाइप पता चलने के बाद,
प्रीफ़ेच करने के रूटीन में बदलाव किया जा सकता है:
Kotlin
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager private var hasWifi = false private var hasCellular = false private var cellModifier: Float = 1f private val networkCallback = object : ConnectivityManager.NetworkCallback() { // Network capabilities have changed for the network override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities) hasCellular = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) hasWifi = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_WIFI) } } private val phoneStateListener = object : PhoneStateListener() { override fun onPreciseDataConnectionStateChanged( dataConnectionState: PreciseDataConnectionState ) { cellModifier = when (dataConnectionState.networkType) { TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1/2f else -> 1f } } private class NetworkState { private var defaultNetwork: Network? = null private var defaultCapabilities: NetworkCapabilities? = null fun setDefaultNetwork(network: Network?, caps: NetworkCapabilities?) = synchronized(this) { defaultNetwork = network defaultCapabilities = caps } val isDefaultNetworkWifi get() = synchronized(this) { defaultCapabilities?.hasTransport(TRANSPORT_WIFI) ?: false } val isDefaultNetworkCellular get() = synchronized(this) { defaultCapabilities?.hasTransport(TRANSPORT_CELLULAR) ?: false } val isDefaultNetworkUnmetered get() = synchronized(this) { defaultCapabilities?.hasCapability(NET_CAPABILITY_NOT_METERED) ?: false } var cellNetworkType: Int = TelephonyManager.NETWORK_TYPE_UNKNOWN get() = synchronized(this) { field } set(t) = synchronized(this) { field = t } private val cellModifier: Float get() = synchronized(this) { when (cellNetworkType) { TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1 / 2f else -> 1f } } val prefetchCacheSize: Int get() = when { isDefaultNetworkWifi -> MAX_PREFETCH_CACHE isDefaultNetworkCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt() else -> DEFAULT_PREFETCH_CACHE } } private val networkState = NetworkState() private val networkCallback = object : ConnectivityManager.NetworkCallback() { // Network capabilities have changed for the network override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { networkState.setDefaultNetwork(network, networkCapabilities) } override fun onLost(network: Network?) { networkState.setDefaultNetwork(null, null) } } private val telephonyCallback = object : TelephonyCallback(), TelephonyCallback.PreciseDataConnectionStateListener { override fun onPreciseDataConnectionStateChanged(dataConnectionState: PreciseDataConnectionState) { networkState.cellNetworkType = dataConnectionState.networkType } } connectivityManager.registerDefaultNetworkCallback(networkCallback) telephonyManager.registerTelephonyCallback(telephonyCallback) private val prefetchCacheSize: Int get() { return when { hasWifi -> MAX_PREFETCH_CACHE hasCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt() else -> DEFAULT_PREFETCH_CACHE } } }
Java
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); private boolean hasWifi = false; private boolean hasCellular = false; private float cellModifier = 1f; private ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onCapabilitiesChanged( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities); hasCellular = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); hasWifi = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_WIFI); } }; private PhoneStateListener phoneStateListener = new PhoneStateListener() { @Override public void onPreciseDataConnectionStateChanged( @NonNull PreciseDataConnectionState dataConnectionState ) { switch (dataConnectionState.getNetworkType()) { case (TelephonyManager.NETWORK_TYPE_LTE | TelephonyManager.NETWORK_TYPE_HSPAP): cellModifier = 4; Break; case (TelephonyManager.NETWORK_TYPE_EDGE | TelephonyManager.NETWORK_TYPE_GPRS): cellModifier = 1/2.0f; Break; default: cellModifier = 1; Break; } } }; cm.registerDefaultNetworkCallback(networkCallback); tm.listen( phoneStateListener, PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE ); public int getPrefetchCacheSize() { if (hasWifi) { return MAX_PREFETCH_SIZE; } if (hasCellular) { return (int) (DEFAULT_PREFETCH_SIZE * cellModifier); } return DEFAULT_PREFETCH_SIZE; }
ऐप्लिकेशन से शुरू किए गए अनुरोधों को ऑप्टिमाइज़ करना
ऐप्लिकेशन से शुरू किए गए अनुरोध, आम तौर पर शेड्यूल के हिसाब से होते हैं. जैसे, कोई ऐसा ऐप्लिकेशन जो बैकएंड सेवा को लॉग या आंकड़े भेजता है. ऐप्लिकेशन से शुरू किए गए अनुरोधों को मैनेज करते समय, उन अनुरोधों की प्राथमिकता पर ध्यान दें. साथ ही, यह भी देखें कि उन्हें एक साथ भेजा जा सकता है या नहीं. इसके अलावा, यह भी देखें कि डिवाइस चार्ज होने या बिना शुल्क वाले नेटवर्क से कनेक्ट होने तक, उन्हें बाद में भेजा जा सकता है या नहीं. इन अनुरोधों को ध्यान से शेड्यूल करके और WorkManager जैसी लाइब्रेरी का इस्तेमाल करके ऑप्टिमाइज़ किया जा सकता है.
नेटवर्क के अनुरोधों को एक साथ भेजना
मोबाइल डिवाइस पर, रेडियो चालू करने, कनेक्ट करने, और रेडियो को चालू रखने की प्रोसेस में ज़्यादा बैटरी खर्च होती है. इस वजह से, अलग-अलग अनुरोधों को अलग-अलग समय पर प्रोसेस करने से, ज़्यादा बैटरी खर्च हो सकती है और बैटरी लाइफ़ कम हो सकती है. नेटवर्क के अनुरोधों के एक सेट को कतार में लगाकर, उन्हें एक साथ प्रोसेस करना ज़्यादा असरदार तरीका है. इससे सिस्टम को रेडियो को सिर्फ़ एक बार चालू करने की बिजली की लागत चुकानी पड़ती है. इसके बावजूद, उसे ऐप्लिकेशन से अनुरोध किया गया सारा डेटा मिल जाता है.
WorkManager का इस्तेमाल करना
WorkManager
लाइब्रेरी का इस्तेमाल करके, काम को बेहतर तरीके से शेड्यूल किया जा सकता है. इसमें यह भी देखा जाता है कि नेटवर्क की उपलब्धता और पावर की स्थिति जैसी खास शर्तें पूरी हुई हैं या नहीं. उदाहरण के लिए, मान लें कि आपके पास DownloadHeadlinesWorker
नाम का एक Worker
सबक्लास है, जो खबरों की नई हेडलाइन दिखाता है. इस वर्कफ़्लो को हर घंटे चलने के लिए शेड्यूल किया जा सकता है. हालांकि, इसके लिए ज़रूरी है कि डिवाइस, बिना शुल्क वाले नेटवर्क से कनेक्ट हो और उसकी बैटरी कम न हो. साथ ही, डेटा को वापस पाने में कोई समस्या होने पर, कस्टम रीट्राइ करने की रणनीति का इस्तेमाल किया जा सकता है. इस बारे में यहां बताया गया है:
Kotlin
val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresBatteryNotLow(true) .build() val request = PeriodicWorkRequestBuilder<DownloadHeadlinesWorker>(1, TimeUnit.HOURS) .setConstraints(constraints) .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES) .build() WorkManager.getInstance(context).enqueue(request)
Java
Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresBatteryNotLow(true) .build(); WorkRequest request = new PeriodicWorkRequest.Builder(DownloadHeadlinesWorker.class, 1, TimeUnit.HOURS) .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES) .build(); WorkManager.getInstance(this).enqueue(request);
WorkManager के अलावा, Android प्लैटफ़ॉर्म पर कई अन्य टूल भी उपलब्ध हैं. इनकी मदद से, नेटवर्क से जुड़े टास्क, जैसे कि पोलिंग को पूरा करने के लिए बेहतर शेड्यूल बनाया जा सकता है. इन टूल का इस्तेमाल करने के बारे में ज़्यादा जानने के लिए, बैकग्राउंड में प्रोसेसिंग करने के बारे में गाइड देखें.
सर्वर से शुरू किए गए अनुरोधों को ऑप्टिमाइज़ करना
सर्वर से शुरू किए गए अनुरोध, आम तौर पर सर्वर से मिली सूचना के जवाब में होते हैं. उदाहरण के लिए, खबरों के नए लेख पढ़ने के लिए इस्तेमाल किए जाने वाले ऐप्लिकेशन को, उपयोगकर्ता के हिसाब से कॉन्टेंट दिखाने की सेटिंग के मुताबिक लेखों के नए बैच के बारे में सूचना मिल सकती है. इसके बाद, ऐप्लिकेशन उन लेखों को डाउनलोड कर लेता है.
Firebase Cloud Messaging की मदद से सर्वर अपडेट भेजना
Firebase Cloud Messaging (FCM) एक हल्का प्रोसेस है. इसका इस्तेमाल, किसी सर्वर से किसी खास ऐप्लिकेशन इंस्टेंस में डेटा भेजने के लिए किया जाता है. FCM का इस्तेमाल करके, आपका सर्वर किसी डिवाइस पर चल रहे आपके ऐप्लिकेशन को सूचना दे सकता है कि उसके लिए नया डेटा उपलब्ध है.
पोलिंग की तुलना में, इस इवेंट-ड्रिवन मॉडल में आपके ऐप्लिकेशन को नया कनेक्शन बनाने की ज़रूरत सिर्फ़ तब होती है, जब उसे पता हो कि डाउनलोड करने के लिए डेटा उपलब्ध है. पोलिंग में, आपके ऐप्लिकेशन को नए डेटा के लिए, नियमित तौर पर सर्वर को पिंग करना पड़ता है. यह मॉडल, ऐप्लिकेशन में जानकारी अपडेट करते समय, ग़ैर-ज़रूरी कनेक्शन को कम करता है और इंतज़ार का समय कम करता है.
FCM को लगातार चालू रहने वाले टीसीपी/आईपी कनेक्शन का इस्तेमाल करके लागू किया जाता है. इससे, हमेशा चालू रहने वाले कनेक्शन की संख्या कम हो जाती है. साथ ही, प्लैटफ़ॉर्म को बैंडविड्थ को ऑप्टिमाइज़ करने और बैटरी लाइफ़ पर पड़ने वाले असर को कम करने में मदद मिलती है.