Riduci al minimo l'effetto degli aggiornamenti regolari

Le richieste inviate dalla tua app alla rete sono una delle principali cause di consumo della batteria perché attivano le radio cellulari o Wi-Fi che consumano molta energia. Oltre alla potenza necessaria per inviare e ricevere pacchetti, queste radio consumano energia extra solo per accendersi e rimanere attive. Un'operazione semplice come una richiesta di rete ogni 15 secondi può tenere la radio mobile sempre accesa e consumare rapidamente la batteria corrente.

Esistono tre tipi generali di aggiornamenti regolari:

  • Avviata dall'utente. Esecuzione di un aggiornamento in base ad alcuni comportamenti degli utenti, ad esempio un gesto di pull-to-refresh.
  • Avvio da app. Eseguire un aggiornamento su base ricorrente.
  • Avviata dal server. Esecuzione di un aggiornamento in risposta a una notifica da un server.

Questo argomento prende in esame ciascuno di questi aspetti e illustra altri modi per ottimizzarli ottimizzato per ridurre il consumo eccessivo della batteria.

Ottimizza le richieste avviate dall'utente

Le richieste avviate dall'utente si verificano in genere in risposta ad alcuni comportamenti degli utenti. Per Ad esempio, un'app utilizzata per leggere gli ultimi articoli potrebbe consentire all'utente di eseguire un gesto pull-to-refresh per controllare i nuovi articoli. Puoi utilizzare lo le seguenti tecniche per rispondere alle richieste avviate dall'utente durante l'ottimizzazione l'utilizzo della rete.

Regolare la velocità delle richieste degli utenti

Ti consigliamo di ignorare alcune richieste avviate dall'utente se non c'è bisogno di ad esempio più gesti di pull-to-refresh in un breve periodo di tempo per verificare la presenza di nuovi dati mentre quelli correnti sono ancora aggiornati. Interazione su ogni di rete potrebbe sprecare una quantità significativa di energia mantenendo attiva la radio. Un approccio più efficiente è limitare le richieste avviate dall'utente in modo da poter effettuare una sola richiesta in un determinato periodo di tempo, riducendo la frequenza d'uso della radio.

Utilizza una cache

Se i dati della tua app vengono memorizzati nella cache, crei una copia locale delle informazioni a cui deve fare riferimento l'app. L'app può quindi accedere alla stessa copia locale delle informazioni più volte senza dover aprire una connessione di rete per effettuare nuove richieste.

Devi memorizzare i dati nella cache nel modo più aggressivo possibile, inclusi i dati statici risorse e download on demand, come le immagini a grandezza originale. Puoi utilizzare le intestazioni della cache HTTP per assicurarti che la tua strategia di memorizzazione nella cache non causi la visualizzazione di dati non aggiornati nella tua app. Per ulteriori informazioni sulla memorizzazione nella cache delle risposte di rete, consulta Evita i ridondanti download.

Su Android 11 e versioni successive, la tua app può utilizzare gli stessi set di dati di grandi dimensioni utilizzati da altre app per casi d'uso come il machine learning e la riproduzione di contenuti multimediali. Quando un'app deve accedere a un set di dati condiviso, può prima verificare la presenza di una versione memorizzata nella cache prima di tentare di scaricare una nuova copia. Per scoprire di più sui set di dati condivisi, consulta Accedere a set di dati condivisi.

Utilizza una larghezza di banda maggiore per scaricare più dati con minore frequenza

Quando si è connessi tramite una radio wireless, la larghezza di banda maggiore viene generalmente di un maggiore costo della batteria, il che significa che il 5G consuma in genere più energia rispetto all'LTE, che a sua volta è più costoso del 3G.

Ciò significa che mentre lo stato radio sottostante varia in base alla radio, in generale l'impatto relativo sulla batteria dello stato il cambiamento di tempo di coda è maggiore per le radio con larghezza di banda superiore. Per ulteriori informazioni tail-time, consulta Lo stato della radio una macchina virtuale.

Allo stesso tempo, maggiore è la larghezza di banda, pertanto puoi precaricare in modo aggressivo, scaricando più dati nello stesso momento. Forse meno intuitivamente, poiché il costo della batteria per il tempo di attesa è relativamente più elevato, è anche più efficiente mantenere la radio attiva per periodi più lunghi durante ogni sessione di trasferimento per ridurre la frequenza degli aggiornamenti.

Ad esempio, se una radio LTE ha una larghezza di banda raddoppiata e un costo energetico raddoppiato del 3G, dovresti scaricare una quantità di dati quadruplicata durante ogni sessione oppure potenzialmente fino a 10 MB. Quando scarichi così tanti dati, è importante considera l'effetto del precaricamento sullo spazio di archiviazione locale disponibile e fai svuotare regolarmente la cache di precaricamento.

Puoi utilizzare ConnectivityManager per registrare un ascoltatore per la rete predefinita e TelephonyManager per registrare un PhoneStateListener per determinare il tipo di connessione del dispositivo corrente. Una volta noto il tipo di connessione, puoi modificare le routine di precaricamento di conseguenza:

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

Ottimizza le richieste avviate dall'app

Le richieste avviate dall'app di solito avvengono in base a una pianificazione, ad esempio un'app che invia i log o le analisi a un servizio di backend. Quando gestisci le richieste avviate dall'app, tieni conto della loro priorità, se possono essere raggruppate e se possono essere differite fino a quando il dispositivo non è in carica o connesso a una rete senza limiti di traffico. Queste richieste possono essere ottimizzate con attenzione pianificazione e l'uso di librerie come WorkManager.

Richieste di rete batch

Su un dispositivo mobile, la procedura per accendere la radio, stabilire una connessione e mantenere attiva la radio utilizza una grande quantità di energia. Per questo motivo, l'elaborazione di singole richieste in momenti casuali può consumare significativa e ridurre la durata della batteria. Un approccio più efficiente è mettere in coda un insieme di richieste di rete e elaborarle insieme. Ciò consente al sistema di pagare la corrente costo di accendere la radio una sola volta e continuare a ricevere tutti i dati richiesti un'app.

Utilizzare WorkManager

Puoi utilizzare la raccolta WorkManager per lavorare in modo efficiente che valuta se vengono soddisfatte determinate condizioni, come la disponibilità di rete e stato dell'alimentazione. Ad esempio, supponi di avere una sottoclasse Worker chiamata DownloadHeadlinesWorker che recupera gli ultimi titoli delle notizie. Questo worker può essere programmato per essere eseguito ogni ora, a condizione che il dispositivo sia connesso a un la rete illimitata e la batteria del dispositivo non sono in esaurimento, con una strategia di ripetizione personalizzata in caso di problemi durante il recupero dei dati, come illustrato di seguito:

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

Oltre a WorkManager, la piattaforma Android offre molti altri strumenti per aiutarti a creare una pianificazione efficiente per il completamento delle attività di networking, come sondaggi. Per scoprire di più sull'uso di questi strumenti, consulta la Guida all'elaborazione in background.

Ottimizza le richieste avviate dal server

Le richieste avviate dal server si verificano in genere in risposta a una notifica inviata da un server web. Ad esempio, un'app utilizzata per leggere gli ultimi articoli di notizie potrebbe ricevere una notifica relativa a un nuovo batch di articoli in linea con le preferenze di personalizzazione dell'utente, che poi scarica.

Invia aggiornamenti del server con Firebase Cloud Messaging

Messaggistica cloud Firebase (FCM) è un'applicazione leggera meccanismo di trasmissione dei dati da un server a una particolare istanza di app. Utilizzando FCM, il tuo server può notificare alla tua app in esecuzione su un determinato dispositivo che sono disponibili nuovi dati.

Rispetto al polling, in cui la tua app deve inviare regolarmente ping al server per eseguire query nuovi dati, questo modello basato su eventi consente alla tua app di creare una nuova connessione solo quando sa che ci sono dati da scaricare. Il modello riduce al minimo le inutili connessioni e riduce la latenza durante l'aggiornamento delle informazioni all'interno dell'app.

FCM viene implementato utilizzando una connessione TCP/IP permanente. In questo modo di connessioni permanenti e consente alla piattaforma di ottimizzare la larghezza di banda e ridurre al minimo il relativo impatto sulla durata della batteria.