नियमित अपडेट के असर को कम करें

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

आम तौर पर, नियमित अपडेट तीन तरह के होते हैं:

  • उपयोगकर्ता ने इसकी प्रोसेस शुरू की. उपयोगकर्ता के कुछ व्यवहार के आधार पर अपडेट करना. जैसे, रीफ़्रेश करने के लिए नीचे की ओर खींचना.
  • ऐप्लिकेशन से शुरू की गई. बार-बार अपडेट करना.
  • सर्वर से शुरू किया गया. सर्वर से मिली सूचना के जवाब में अपडेट किया जा रहा है.

इस विषय में, इन सभी के बारे में बताया गया है. साथ ही, बैटरी की खपत कम करने के लिए, इन्हें ऑप्टिमाइज़ करने के अन्य तरीकों के बारे में भी बताया गया है.

उपयोगकर्ता के अनुरोधों को ऑप्टिमाइज़ करना

उपयोगकर्ता की ओर से शुरू किए गए अनुरोध आम तौर पर, उपयोगकर्ता के किसी व्यवहार के जवाब में होते हैं. उदाहरण के लिए, नए लेख पढ़ने के लिए इस्तेमाल किया जाने वाला कोई ऐप्लिकेशन, उपयोगकर्ता को नए लेखों की जांच करने के लिए, पुल-टू-रिफ़्रेश जेस्चर करने की अनुमति दे सकता है. नेटवर्क के इस्तेमाल को ऑप्टिमाइज़ करते समय, उपयोगकर्ता के अनुरोधों का जवाब देने के लिए, यहां दी गई तकनीकों का इस्तेमाल किया जा सकता है.

उपयोगकर्ता के अनुरोधों को थ्रॉटल करना

अगर आपको लगता है कि उपयोगकर्ता के कुछ अनुरोधों को अनदेखा किया जा सकता है, तो उन्हें अनदेखा करें. जैसे, अगर मौजूदा डेटा अभी भी नया है, तो नए डेटा की जांच करने के लिए, कम समय में कई बार पुल-टू-रीफ़्रेश जेस्चर का इस्तेमाल करना. हर अनुरोध पर कार्रवाई करने से, रेडियो चालू रहता है. इससे काफ़ी ज़्यादा बैटरी खर्च हो सकती है. उपयोगकर्ता के अनुरोधों को थ्रॉटल करना ज़्यादा असरदार तरीका है. इससे एक तय समय में सिर्फ़ एक अनुरोध किया जा सकता है. इससे रेडियो के इस्तेमाल की फ़्रीक्वेंसी कम हो जाती है.

कैश मेमोरी का इस्तेमाल करना

अपने ऐप्लिकेशन के डेटा को कैश मेमोरी में सेव करके, ऐप्लिकेशन के लिए ज़रूरी जानकारी की एक लोकल कॉपी बनाई जाती है. इसके बाद, आपका ऐप्लिकेशन जानकारी की उसी लोकल कॉपी को कई बार ऐक्सेस कर सकता है. इसके लिए, उसे नए अनुरोध करने के लिए नेटवर्क कनेक्शन खोलने की ज़रूरत नहीं होगी.

आपको डेटा को ज़्यादा से ज़्यादा समय के लिए कैश मेमोरी में सेव करना चाहिए. इसमें स्टैटिक रिसॉर्स और मांग पर डाउनलोड किए जाने वाले कॉन्टेंट शामिल हैं. जैसे, फ़ुल साइज़ वाली इमेज. एचटीटीपी कैश हेडर का इस्तेमाल करके, यह पक्का किया जा सकता है कि कैश मेमोरी में सेव करने की रणनीति की वजह से, आपका ऐप्लिकेशन पुराना डेटा न दिखाए. नेटवर्क से मिले जवाबों को कैश मेमोरी में सेव करने के बारे में ज़्यादा जानने के लिए, बार-बार डाउनलोड करने से बचें लेख पढ़ें.

Android 11 और इसके बाद के वर्शन पर, आपका ऐप्लिकेशन उन बड़े डेटासेट का इस्तेमाल कर सकता है जिनका इस्तेमाल अन्य ऐप्लिकेशन, मशीन लर्निंग और मीडिया चलाने जैसे कामों के लिए करते हैं. जब आपके ऐप्लिकेशन को शेयर किए गए डेटासेट को ऐक्सेस करना होता है, तो नई कॉपी डाउनलोड करने से पहले, वह कैश मेमोरी में सेव किए गए वर्शन की जांच कर सकता है. शेयर किए गए डेटासेट के बारे में ज़्यादा जानने के लिए, शेयर किए गए डेटासेट ऐक्सेस करना लेख पढ़ें.

ज़्यादा डेटा को कम समय में डाउनलोड करने के लिए, ज़्यादा बैंडविड्थ का इस्तेमाल करना

वायरलेस रेडियो से कनेक्ट होने पर, ज़्यादा बैंडविड्थ के लिए आम तौर पर ज़्यादा बैटरी खर्च होती है. इसका मतलब है कि 5G, आम तौर पर LTE से ज़्यादा बैटरी खर्च करता है. वहीं, LTE, 3G से ज़्यादा बैटरी खर्च करता है.

इसका मतलब है कि रेडियो टेक्नोलॉजी के आधार पर, रेडियो की स्थिति अलग-अलग होती है. हालांकि, आम तौर पर, ज़्यादा बैंडविड्थ वाले रेडियो के लिए, स्थिति में बदलाव होने के बाद बैटरी पर पड़ने वाला असर ज़्यादा होता है. टेल-टाइम के बारे में ज़्यादा जानने के लिए, रेडियो स्टेट मशीन देखें.

साथ ही, ज़्यादा बैंडविथ का मतलब है कि ज़्यादा डेटा को पहले से फ़ेच किया जा सकता है. इससे कम समय में ज़्यादा डेटा डाउनलोड किया जा सकता है. हालांकि, टेल-टाइम में बैटरी की खपत ज़्यादा होती है. इसलिए, हर ट्रांसफ़र सेशन के दौरान रेडियो को ज़्यादा समय तक चालू रखना ज़्यादा फ़ायदेमंद होता है. इससे अपडेट की फ़्रीक्वेंसी कम हो जाती है.

उदाहरण के लिए, अगर एलटीई रेडियो में 3G की तुलना में दोगुना बैंडविड्थ और दोगुना ऊर्जा खर्च होती है, तो आपको हर सेशन में चार गुना ज़्यादा डेटा डाउनलोड करना चाहिए. इसके अलावा, 10 एमबी तक का डेटा भी डाउनलोड किया जा सकता है. इतना डेटा डाउनलोड करते समय, यह ध्यान रखना ज़रूरी है कि प्रीफ़ेचिंग का असर, उपलब्ध लोकल स्टोरेज पर न पड़े. साथ ही, प्रीफ़ेचिंग की कैश मेमोरी को समय-समय पर फ़्लश करें.

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

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 लाइब्रेरी का इस्तेमाल करके, काम को बेहतर तरीके से शेड्यूल किया जा सकता है. इससे यह तय किया जा सकता है कि कुछ शर्तें पूरी हुई हैं या नहीं. जैसे, नेटवर्क की उपलब्धता और बैटरी का स्टेटस. उदाहरण के लिए, मान लें कि आपके पास Worker नाम की एक सबक्लास है, जो DownloadHeadlinesWorker नाम की सबक्लास से नई खबरों की हेडलाइन फ़ेच करती है. इस वर्कर को हर घंटे चलाने के लिए शेड्यूल किया जा सकता है. हालांकि, इसके लिए ज़रूरी है कि डिवाइस किसी ऐसे नेटवर्क से कनेक्ट हो जिसमें डेटा के इस्तेमाल पर कोई शुल्क नहीं लगता हो और डिवाइस की बैटरी कम न हो. अगर डेटा को वापस पाने में कोई समस्या आती है, तो फिर से कोशिश करने की कस्टम रणनीति का इस्तेमाल किया जा सकता है. इसके बारे में यहां बताया गया है:

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);

Android प्लैटफ़ॉर्म, WorkManager के अलावा कई अन्य टूल भी उपलब्ध कराता है. इनकी मदद से, नेटवर्किंग से जुड़े टास्क पूरे करने के लिए बेहतर शेड्यूल बनाया जा सकता है. जैसे, पोलिंग. इन टूल का इस्तेमाल करने के बारे में ज़्यादा जानने के लिए, बैकग्राउंड प्रोसेसिंग से जुड़ी गाइड देखें.

सर्वर से शुरू किए गए अनुरोधों को ऑप्टिमाइज़ करना

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

Firebase Cloud Messaging की मदद से सर्वर अपडेट भेजना

Firebase Cloud Messaging (FCM) एक हल्का-फुल्का मेकेनिज़्म है. इसका इस्तेमाल, सर्वर से किसी ऐप्लिकेशन इंस्टेंस तक डेटा ट्रांसफ़र करने के लिए किया जाता है. FCM का इस्तेमाल करके, आपका सर्वर किसी डिवाइस पर चल रहे ऐप्लिकेशन को यह सूचना दे सकता है कि उसके लिए नया डेटा उपलब्ध है.

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

FCM को लगातार बने रहने वाले टीसीपी/आईपी कनेक्शन का इस्तेमाल करके लागू किया जाता है. इससे, लगातार बने रहने वाले कनेक्शन की संख्या कम हो जाती है. साथ ही, प्लैटफ़ॉर्म को बैंडविड्थ को ऑप्टिमाइज़ करने और बैटरी लाइफ़ पर पड़ने वाले असर को कम करने की अनुमति मिलती है.