Nachdem ich beschrieben habe, wie man auch einfache Weise zwischen einem Service und einer Activity kommunizieren kann, möchte ich natürlich den Service auch als solchen nutzen, nämlich als stillen Wächter im Hintergrund der (fast) immer aktiv ist.

Dazu muss ich den Service irgendwie automatisch starten. Wie man das macht findet man relativ schnell - nun will ich es hier einmal am oben genannten Beispiel zeigen.

Um einen Service zu starten, wenn etwas bestimmtes passiert, müssen wir drei Dinge tun:

  1. Unseren Service auf die gewünschten Ereignisse "lauschen" lassen.
  2. Uns die Erlaubnis holen diese Ereignisse zu bedienen.
  3. Den Service starten sobald das Ereignis eintrifft.
Um das zu bewerkstelligen muss man sogenannte Intents nutzen. Dafür gibt es Intents selber, Intent Categories, Broadcastreceiver usw. und das ist IMHO nicht sonderlich intuitiv - daher habe ich darüber einen eigenen Artikel geschrieben.

"Lauschen" kann man mit Hilfe eines so genannten BroadcastReceivers, das ist einfach eine Klasse die die Klasse BroadcastReceiver erweitert und deren Methode onReceive() automatisch aufgerufen wird sobald ein registriertes Broadcast-Event eintritt.

In meinem Fall füge ich die folgende Klasse "ServiceBroadcastReceiver" einfach neue Klasse meinem Service hinzu:

    public class ServiceBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action != null) {
                if (
                    action.equals(Intent.ACTION_BOOT_COMPLETED)
                    || action.equals(Intent.ACTION_USER_PRESENT)
                ) {
                    Intent startServiceIntent = new Intent(context, MyService.class);
                    context.startService(startServiceIntent);
                }
            }
        }
    }
Wichtig: Man kann den Receiver augenscheinlich nicht als Nested Class innerhalb einer anderen Klasse wie z.B. dem Service deklarieren, wenn man ihn über das Manifest registrieren möchte.

Das obige Codebeispiel sorgt dafür, dass im Falle der Ereignisse "Gerät wurde gestartet" (Intent.ACTION_BOOT_COMPLETED) und "Bildschirm wurde entsperrt" (Intent.ACTION_USER_PRESENT) der Service "MyService" gestartet wird. Falls der Service schon läuft passiert nichts.

Damit lauscht der Receiver allerdings noch nicht, er muss noch in der AndroidManifest.xml als Receiver eingetragen werden, damit das System weiß, dass dort überhaupt ein Receiver ist.

Ein kleiner Hinweis zum Namen: Wenn ich einen Klassennamen in das Manifest eintrage, kann ich das auf mehrere Arten tun:

  1. Vollständiger Name. Sowas wie "com.hell.the.what.android.MyClass"
  2. Name im festgelegten "default" package. Sowas wie ".MyClass" - bedeutet, dass "MyClass" in dem package gesucht wird, dass im Manifest als attribut "package" festgelegt wurde.
  3. Nur der Name. Sowas wie "MyClass" - sollte im Zweifelsfall genauso funktionieren wie 2, ist aber AFAIK nicht spezifiziert.
(Spezialfall: Der Vollständige Name einer "Nested Class" wird als Name der umgebenden Klasse, dahinter ein "$" und dahinter der Name der verschachtelten Klasse geschrieben: ".MyClass$NestedClass"  - Aber wie oben bereits gesagt, funktioniert das augenscheinlich nicht bei Receivern.)

In diesem Fall fügt man folgendes in das AndroidManifest ein:

    <receiver android:name=".ServiceBroadcastReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"></action>
            <action android:name="android.intent.action.USER_PRESENT"></action>
            <category android:name="android.intent.category.DEFAULT"></category>
        </intent-filter>
    </receiver>
Damit sagt man nichts anderes als "Im Falle von BOOT_COMPLETED oder USER_PRESENT, rufe meinen Receiver auf". Die Default Category muss hinzugefügt werden, damit der Matcher auf die beiden Ereignisse anspricht - ansonsten passiert einfach nichts.