iX 10/2017
S. 136
Praxis
App-Programmierung
Aufmacherbild

Speicherhunger und Stromverbrauch unter Android bremsen

Auf Sparflamme

Selbst preiswerte Android-Smartphones verfügen heute über mindestens 2 GByte Arbeitsspeicher und Mehrkernprozessoren. Dennoch klagen Nutzer in App-Bewertungen über lange Reaktionszeiten und ruckelnde Animationen. Schuld daran ist meist eine schlampige Programmierung. Dabei lassen sich Anwendungen einfach speicher- und akkuschonend entwickeln.

Schon immer musste sich Android nachsagen lassen, nicht so flüssig wie Apples Betriebssystem zu laufen. Anfangs schoben Entwickler die Schuld der damaligen App-Laufzeitumgebung in die Schuhe. Zu Unrecht, denn Dalvik legte Google speziell auf ressourcenarme Geräten aus. Nachteilig ist vielmehr, dass Android im Vergleich zu iOS App-Designern mehr Freiheiten lässt – insbesondere im Zusammenhang mit Hintergrundaktivitäten. Heute kaum zu glauben, aber die einer App maximal zur Verfügung stehende Heap Size auf dem Google G1 betrug gerade einmal 16 Megabyte. Folglich musste man sich genau überlegen, wann man Objekte instanziiert oder Threads erzeugt. Nachlässigkeit strafte die Nutzer mit Speichermangel und schlechter Performance. Mit einem Just-in-Time-Compiler und später durch den Austausch der Laufzeitumgebung durch ART (Android Runtime) versuchte Google, die Geschwindigkeit von Apps und System zu steigern. Darunter fallen Project Svelte in Android 4.4, das die Speicherauslastung optimieren soll, und Project Volta in Version 5 zum Senken des Stromverbrauchs.

Dennoch halten sich noch immer dieselben Beschwerden. Der Grund: Zu viele Entwickler verwenden Programmierschnittstellen nicht richtig oder ineffizient. Was aber nicht zwangsläufig an ihrem Unvermögen liegt – vielmehr hat Google lange gebraucht, die vielen Ideen und Konzepte in Android umfassend und nachvollziehbar zu erklären. Hinzu kommt, dass es fürs Umsetzen einer Idee oft mehrere APIs gibt und je nach Zielplattform unterschiedliche Varianten die passendere Wahl sind. Ein Beispiel ist der Zugriff auf Webressourcen per Apaches HttpClient oder java.net.HttpURLConnection. Letztere galt in frühen Android-Versionen als fehleranfällig, sodass Apache den Vorzug erhielt. Wer auf Letzteres gesetzt hat, musste sich mit Android 5.1 zwingend umstellen: Die Klassen des Pakets org.apache.http erklärte Google für veraltet und entfernte sie mit Version 6. Wer sie weiter einsetzen möchte, muss die Implementierung über build.gradle einbinden.

Nebenläufigkeit im Auge behalten

Android-Apps bestehen aus den vier Grundbausteinen Activity, Service, Broadcast Receiver und Content Provider. Sie registriert man durch entsprechende Tags in der Manifestdatei. Broadcast Receiver sind Empfänger von Nachrichten, die das System oder andere Apps an einen oder mehrere Empfänger geschickt haben. Content Provider bieten über Uniform Resource Identifier den Zugriff auf Kontakte, Termine sowie beliebige weitere datensatzartige Strukturen. Services implementieren lange laufende Funktionen ohne Nutzerinteraktion, also Hintergrundaktivitäten wie das kontinuierliche Nachladen von Daten aus dem Netz. Activities repräsentieren die für den Anwender sichtbaren Komponenten einer App. Sie stellen die Nutzeroberfläche dar, nehmen Eingaben entgegen und zeigen Ergebnisse an. Bei ihrer Implementierung machen neue Programmierer über kurz oder lang Bekanntschaft mit einer Eigenheit, die Googles Betriebssystem mit praktisch allen verbreiteten UI-Toolkits gemein hat: Die UI-Bibliothek ist single-threaded. Welche Konsequenzen das hat, zeigt die Klasse KomplizierteBerechnungActivity. Die vollständige Beispiel-App können Leser vom iX-Listing-Server herunterladen.

Listing 1: KomplizierteBerechnungActivity.java

package com.thomaskuenneth.performancedemos;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

public class KomplizierteBerechnungActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.komplizierte_berechnung);
        TextView output = (TextView) findViewById(R.id.output);
        Button calculate = (Button) findViewById(R.id.calculate);
        calculate.setOnClickListener((c) -> {
            output.setText(R.string.blocked);
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                output.setText(R.string.app_name);
            }
            output.setText(R.string.not_blocked);
        });
    }
}
Nie gerne gesehen: Android fragt bei nicht reagierenden Apps nach, was es nun tun soll (Abb. 1).

Listing 1 simuliert eine länger laufende Berechnung. Sie zeigt ein häufig auftretendes Ärgernis: Nach dem Anklicken der Schaltfläche „Berechnen“ reagiert die ebenfalls angezeigte Checkbox zunächst nicht mehr auf Eingaben. Erst nach Abschluss der Kalkulation verhält sie sich wieder normal. Die Ursache hierfür liegt darin, dass Android Anwendungscode standardmäßig in einem Thread ausführt, den das System gleichzeitig für alle Aktionen der Oberfläche vorsieht. Blockiert man eben jenen main-Thread, etwa durch Thread.sleep(20000);, funktioniert nichts mehr. Glücklicherweise hängt in diesem Fall nicht das ganze System, sondern bloß die App. Dauert die Blockade zu lange, erscheint die von Anwendern und Entwicklern gleichermaßen gefürchtete Rückfrage (Abbildung 1).