Réduire l'impact des mises à jour régulières

Les requêtes envoyées par votre application au réseau sont une cause majeure de décharge de la batterie parce qu'ils allument les radios cellulaires ou Wi-Fi consommant beaucoup d'énergie. Au-delà de la puissance nécessaires pour envoyer et recevoir des paquets, ces radios dépensent une puissance supplémentaire juste s'allume et reste éveillé. Quelque chose d'aussi simple qu'une requête réseau toutes les 15 secondes peuvent maintenir la radio mobile allumée en continu et décharger rapidement la batterie. .

Il existe trois types généraux de mises à jour régulières:

  • Déclenchement par l'utilisateur. effectuer une mise à jour basée sur un comportement utilisateur, tel que un geste d'extraction pour actualiser.
  • Déclenchement par l'application. Effectuer une mise à jour récurrente.
  • Déclenchement par le serveur. Effectuer une mise à jour en réponse à une notification d'un un serveur.

Dans cette rubrique, nous nous penchons sur chacun de ces éléments et nous présentons d'autres façons de les utiliser. optimisé pour réduire la décharge de la batterie.

Optimiser les requêtes déclenchées par l'utilisateur

Les requêtes lancées par l'utilisateur se produisent généralement en réponse à certains comportements de l'utilisateur. Pour exemple, une application utilisée pour lire les derniers articles d'actualité peut permettre à l'utilisateur effectuer un geste d'extraction pour actualiser afin de rechercher de nouveaux articles. Vous pouvez utiliser techniques pour répondre aux requêtes des utilisateurs tout en optimisant l'utilisation du réseau.

Limiter les requêtes des utilisateurs

Vous pouvez ignorer certaines requêtes déclenchées par l'utilisateur s'il n'est pas nécessaire tels que plusieurs gestes d'extraction pour actualiser sur une courte période rechercher de nouvelles données tant que les données actuelles sont encore à jour. Action pour chacun d'eux pourrait gaspiller une quantité importante d'énergie en maintenant le signal radio activé. A une approche plus efficace consiste à limiter les requêtes lancées par l'utilisateur une seule demande peut être effectuée sur une période donnée, ce qui réduit la fréquence cette radio est utilisée.

Utiliser un cache

En mettant en cache les données de votre application, vous créez une copie locale des informations que votre application doit référencer. Votre application peut alors accéder sans avoir à ouvrir un réseau pour effectuer de nouvelles requêtes.

Vous devez mettre en cache les données de manière aussi agressive que possible, y compris les données statiques et des téléchargements à la demande tels que des images en taille réelle. Vous pouvez utiliser HTTP des en-têtes de cache pour vous assurer que votre stratégie de mise en cache n'entraîne pas l'affichage de données obsolètes. Pour en savoir plus sur la mise en cache des réponses réseau, consultez Éviter les redondances téléchargements.

Sous Android 11 ou version ultérieure, votre application peut utiliser les mêmes grands ensembles de données que d'autres applications pour des cas d'utilisation tels que le machine learning et la lecture de contenus multimédias. Lorsque votre a besoin d'accéder à un ensemble de données partagé, elle peut d'abord rechercher une version mise en cache avant d'essayer de télécharger une nouvelle copie. Pour en savoir plus sur les jeux de données partagés, Consultez la page Accéder aux ensembles de données partagés.

Utiliser une plus grande bande passante pour télécharger plus de données moins souvent

Lorsqu'elles sont connectées via un signal radio sans fil, une bande passante plus élevée provient généralement le coût de la batterie est plus élevé, ce qui signifie que la 5G consomme généralement plus d'énergie. par rapport au LTE, qui est elle-même plus chère que la 3G.

Cela signifie que même si l'état radio sous-jacent varie en fonction la technologie radio, d'une manière générale, sur l'impact relatif de la batterie la latence de modification est plus importante pour les signaux radio à bande passante plus élevée. Pour en savoir plus sur tail-time, consultez la section The radio state machine.

Parallèlement, plus la bande passante est élevée, plus vous pouvez précharger plus de manière agressive, en téléchargeant plus de données en même temps. Peut-être moins intuitivement, comme le coût de la batterie de fin de vie est relativement plus élevé, plus efficace pour maintenir le signal radio actif pendant de plus longues périodes lors de chaque transfert pour réduire la fréquence des mises à jour.

Par exemple, si une radio LTE offre deux fois plus de bande passante et deux fois plus d'énergie de 3G, téléchargez quatre fois plus de données à chaque session. potentiellement jusqu'à 10 Mo. Lorsque vous téléchargez autant de données, il est important de Tenez compte de l'effet du préchargement sur le stockage local disponible et videz régulièrement votre cache de préchargement.

Vous pouvez utiliser ConnectivityManager pour vous inscrire un écouteur pour le réseau par défaut, TelephonyManager pour vous inscrire un PhoneStateListener pour déterminer le type de connexion actuel de l'appareil. Une fois le type de connexion connu, vous pouvez modifier vos routines de préchargement en conséquence:

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

Optimiser les requêtes déclenchées par l'application

Les requêtes déclenchées par l'application sont généralement exécutées selon un calendrier, par exemple une application qui envoie des journaux ou des analyses à un service de backend. Lorsqu'il s'agit d'actions déclenchées par une application tenez compte de leur priorité et déterminez si elles peuvent être regroupées ensemble, et si elles peuvent être différées jusqu'à ce que l'appareil soit en charge ou connecté à un réseau illimité. Vous pouvez optimiser ces requêtes la planification et à l'aide de bibliothèques telles que WorkManager.

Requêtes réseau par lot

Sur un appareil mobile, le processus qui consiste à allumer la radio, à établir une connexion, et maintenir la radio activée utilise une grande quantité d'énergie. Pour cette raison, le traitement de requêtes individuelles à des moments aléatoires peut et réduire l'autonomie de la batterie. Une approche plus efficace consiste à mettre en file d'attente un ensemble et les traiter ensemble. Cela permet au système de payer l'énergie d'allumer la radio une seule fois, tout en continuant à recevoir toutes les données demandées par une application.

Utiliser WorkManager

Vous pouvez utiliser la bibliothèque WorkManager pour effectuer le travail selon un calendrier efficace qui vérifie si des conditions spécifiques sont remplies, comme la disponibilité du réseau, et l'état de l'alimentation. Par exemple, supposons que vous ayez Sous-classe Worker appelée DownloadHeadlinesWorker, qui récupère les derniers titres de l'actualité. Ce nœud de calcul peuvent être programmés pour s'exécuter toutes les heures, à condition que l'appareil soit connecté à un réseau illimité et la batterie de l'appareil n'est pas faible, avec une stratégie de nouvelle tentative personnalisée en cas de problème de récupération des données, comme indiqué ci-dessous:

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

Outre WorkManager, la plate-forme Android propose plusieurs autres outils pour vous aider à créer un calendrier efficace pour l'exécution des tâches de réseautage, telles que que le sondage. Pour en savoir plus sur l'utilisation de ces outils, consultez la Guide sur le traitement en arrière-plan

Optimiser les requêtes déclenchées par le serveur

Les requêtes initiées par le serveur se produisent généralement en réponse à une notification d'un Google Cloud. Par exemple, une application utilisée pour lire les derniers articles d'actualité peut recevoir une notification concernant un nouveau lot d'articles correspondant au de personnalisation, qu'il télécharge ensuite.

Envoyer des informations sur le serveur avec Firebase Cloud Messaging

Firebase Cloud Messaging (FCM) est un modèle mécanisme utilisé pour transmettre des données d'un serveur à une instance d'application particulière. À l'aide de FCM, votre serveur peut notifier votre application exécutée sur un appareil particulier qui de nouvelles données sont disponibles.

Par rapport à la scrutation, où votre application doit régulièrement pinguer le serveur pour demander nouvelles données, ce modèle basé sur les événements permet à votre application de créer une connexion uniquement lorsqu'il sait qu'il y a des données à télécharger. Le modèle limite le minimum et réduit la latence lors de la mise à jour des informations dans votre application.

FCM est implémenté à l'aide d'une connexion TCP/IP persistante. Cela réduit de connexions persistantes et permet à la plate-forme d'optimiser la bande passante et minimiser l'impact associé sur l'autonomie de la batterie.