Auswirkungen regelmäßiger Updates minimieren

Anfragen, die deine App an das Netzwerk sendet, sind eine Hauptursache für eine schnelle Akkuentladung da sie energieverbrauchende Mobilfunk- oder WLAN-Funkschnittstellen aktivieren. Jenseits der Macht zum Senden und Empfangen von Paketen benötigt, verbrauchen diese Funkschnittstellen zusätzliche Energie, wird eingeschaltet und bleibt aktiv. Etwas so Einfaches wie eine Netzwerkanfrage alle 15 Sekunden können dafür sorgen, dass das Mobilfunknetz ständig eingeschaltet bleibt und der Akku schnell belastet wird. Energie.

Es gibt drei Arten von regelmäßigen Updates:

  • Vom Nutzer initiiert. Aktualisierung aufgrund eines Nutzerverhaltens, z. B. eines Wisch-Gestos zum Aktualisieren
  • Von der App initiiert: Sie führen regelmäßig eine Aktualisierung durch.
  • Vom Server initiiert. Aktualisierung als Reaktion auf eine Benachrichtigung von einem Server

In diesem Thema werden diese Punkte näher betrachtet und weitere Möglichkeiten erörtert, optimiert, um den Akku zu schonen.

Vom Nutzer initiierte Anfragen optimieren

Von Nutzern initiierte Anfragen erfolgen in der Regel als Reaktion auf bestimmtes Nutzerverhalten. In einer App zum Lesen der neuesten Nachrichtenartikel kann der Nutzer beispielsweise durch Wischen nach oben nach neuen Artikeln suchen. Sie können die wie Sie auf von Nutzern initiierte Anfragen reagieren, Netzwerknutzung.

Nutzeranfragen drosseln

Sie können einige von Nutzern initiierte Anfragen ignorieren, wenn sie nicht erforderlich sind, z. B. mehrere Wischbewegungen zum Aktualisieren innerhalb kurzer Zeit, um nach neuen Daten zu suchen, während die aktuellen Daten noch aktuell sind. Wenn auf jede Anfrage reagiert wird, kann dies zu einem erheblichen Stromverbrauch führen, da das Funkmodul ständig aktiv bleibt. Eine effizientere Methode besteht darin, die von Nutzern initiierten Anfragen so zu drosseln, dass über einen bestimmten Zeitraum nur eine Anfrage gesendet werden kann. So wird die Häufigkeit der Nutzung des Funkschnittstellen verringert.

Cache verwenden

Durch das Speichern Ihrer App-Daten im Cache erstellen Sie eine lokale Kopie der Informationen. auf die Ihre App verweisen muss. Ihre App kann dann auf dieselben lokalen mehrfach kopieren, ohne ein Netzwerk öffnen zu müssen um neue Anfragen zu stellen.

Sie sollten Daten so offen wie möglich im Cache speichern, einschließlich statischer Daten. und On-Demand-Downloads wie Bilder in Originalgröße. Mithilfe von HTTP-Cache-Headern können Sie dafür sorgen, dass Ihre App aufgrund Ihrer Caching-Strategie keine veralteten Daten anzeigt. Weitere Informationen zum Caching von Netzwerkantworten finden Sie unter Redundante Downloads.

Unter Android 11 und höher kann Ihre App dieselben großen Datensätze verwenden, die andere Apps für Anwendungsfälle wie maschinelles Lernen und Medienwiedergabe verwenden. Wenn Ihre App auf einen freigegebenen Datensatz zugreifen muss, kann sie zuerst nach einer im Cache gespeicherten Version suchen, bevor sie versucht, eine neue Kopie herunterzuladen. Weitere Informationen zu freigegebenen Datasets Siehe Auf freigegebene Datasets zugreifen.

Höhere Bandbreite verwenden, um mehr Daten seltener herunterzuladen

Bei einer Verbindung über ein WLAN ist eine höhere Bandbreite in der Regel mit höheren Akkukosten verbunden. Das bedeutet, dass 5G in der Regel mehr Energie verbraucht als LTE, was wiederum teurer ist als 3G.

Das bedeutet, dass der zugrunde liegende Funkstatus zwar je nach Funktechnologie, im Allgemeinen die relativen Auswirkungen des Zustands auf die Batterie bei Funkverbindungen mit höherer Bandbreite größer ist. Weitere Informationen zur Tail-Time finden Sie unter Der Radio-Statusautomat.

Gleichzeitig bedeutet die höhere Bandbreite, dass Sie mehr aggressiv vorgehen und mehr Daten gleichzeitig herunterladen. Weniger intuitiv ist, dass es auch effizienter ist, das Funkgerät während jeder Übertragungssitzung länger aktiv zu halten, um die Häufigkeit der Aktualisierungen zu verringern, da die Akkukosten in der Endzeit relativ höher sind.

Wenn ein LTE-Funkschnittstelle beispielsweise die doppelte Bandbreite und die doppelten Energiekosten von 3G hat, sollten Sie bei jeder Sitzung viermal so viele Daten herunterladen – möglicherweise bis zu 10 MB. Wenn Sie so viele Daten herunterladen, die Auswirkungen Ihres Vorabrufs auf den verfügbaren lokalen Speicher und Ihren Prefetch-Cache regelmäßig.

Mit ConnectivityManager kannst du einen Listener für das Standardnetzwerk registrieren und mit TelephonyManager einen PhoneStateListener, um den aktuellen Geräteverbindungstyp zu ermitteln. Sobald der Verbindungstyp bekannt ist, können Sie Ihre prefetching-Abläufe entsprechend anpassen:

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

Von der App initiierte Anfragen optimieren

Von Apps initiierte Anfragen erfolgen in der Regel nach einem Zeitplan, z. B. bei einer App, die Protokolle oder Analysen an einen Back-End-Dienst sendet. Beim Umgang mit App-initiierten die Priorität dieser Anfragen zu prüfen und herauszufinden, ob sie in Batches zusammengefasst werden können. und ob sie erst verzögert werden können, wenn das Gerät aufgeladen wird oder die mit einem kostenlosen Netzwerk verbunden ist. Diese Anfragen können durch sorgfältiges Planen und mithilfe von Bibliotheken wie WorkManager optimiert werden.

Netzwerkanfragen im Batch

Auf einem Mobilgerät verbraucht das Einschalten des Funkschnittstellenmoduls, das Herstellen einer Verbindung und das Aktivhalten des Funkschnittstellenmoduls viel Strom. Daher kann die Verarbeitung einzelner Anfragen zu zufälligen Zeiten viel Energie verbrauchen und die Akkulaufzeit verkürzen. Ein effizienterer Ansatz besteht darin, eine Reihe von und gemeinsam verarbeiten. So muss das System nur einmal die Energiekosten für das Einschalten des Funkschnittstellenmoduls bezahlen und erhält trotzdem alle von einer App angeforderten Daten.

WorkManager verwenden

Mit der WorkManager-Bibliothek können Sie Aufgaben nach einem effizienten Zeitplan ausführen, bei dem berücksichtigt wird, ob bestimmte Bedingungen erfüllt sind, z. B. Netzwerkverfügbarkeit und Betriebsstatus. Angenommen, Sie haben eine Abgeleitete Worker-Klasse mit dem Namen DownloadHeadlinesWorker, die die neuesten Schlagzeilen abruft. Dieser Worker kann so geplant werden, dass sie stündlich ausgeführt werden. Voraussetzung ist, dass das Gerät an eine nicht getaktetes Netzwerk und Batteriestand des Geräts nicht niedrig, mit benutzerdefinierter Wiederholungsstrategie , wenn es Probleme beim Abrufen der Daten gibt:

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

Neben WorkManager bietet die Android-Plattform mehrere weitere Tools, mit denen Sie einen effizienten Zeitplan für die Ausführung von Netzwerkaufgaben wie Abfragen erstellen können. Weitere Informationen zur Verwendung dieser Tools finden Sie in der Leitfaden zur Hintergrundverarbeitung

Vom Server initiierte Anfragen optimieren

Vom Server initiierte Anfragen werden in der Regel als Reaktion auf eine Benachrichtigung von einem Server. Beispiel: Eine App, die die neuesten Nachrichtenartikel liest, eine Benachrichtigung über eine Reihe neuer Artikel erhalten, die es dann herunterlädt.

Serverupdates mit Firebase Cloud Messaging senden

Firebase Cloud Messaging (FCM) ist ein schlanker Mechanismus, mit dem Daten von einem Server an eine bestimmte App-Instanz übertragen werden. Mit FCM kann Ihr Server Ihre App, die auf einem bestimmten Gerät ausgeführt wird, benachrichtigen, dass neue Daten verfügbar sind.

Im Vergleich zu Polling, bei dem Ihre Anwendung den Server regelmäßig anpingen muss, um neue Daten haben, ermöglicht dieses ereignisgesteuerte Modell Ihrer App, eine neue Verbindung wenn es weiß, dass Daten heruntergeladen werden können. Das Modell minimiert unnötige Verbindungen und reduziert die Latenz beim Aktualisieren von Informationen in Ihrer App.

FCM wird über eine dauerhafte TCP/IP-Verbindung implementiert. So wird die Anzahl der dauerhaften Verbindungen minimiert und die Plattform kann die Bandbreite optimieren und die damit verbundenen Auswirkungen auf die Akkulaufzeit minimieren.