Funksteckdosen über den Status des Smartphones (Akku, Wlan etc.) steuern – Teil 2

Im zweiten Teil des Beitrags kümmern wir uns um die wirkliche Automatisierung des zuvor erstellten BroadcastReceiver. Im ersten Teil, konnten wir ja den Receiver über die Activity starten und im System registrieren. Aber wie bereits angemerkt wollen wir ein Dienst (Service) bauen der uns diese Arbeit abnimmt. Dieser Dienst sollte auch beim hochfahren des Smartphones gestartet werden. Solche Dienste kommen immer wieder vor, sei es Facebook oder andere APPs, die Dateien im Hintergrund verarbeiten und ggf. anzeigen. Dabei gibt es zwei Formen von Diensten zwischen welchen man unterscheiden muss. Zum einen gibt es die „Bound Services“. Diese Dienste „hängen“ an der Application fest, welche die Services gestartet hat und sind solange aktiv wie die Application selbst. Die andere Form „Started Services“ behandelt Dienste welche selbstständig weiter laufen auch dann wenn die Application welche den Dienst gestartet hat nicht mehr existiert. Ein Dienst der ständig im Hintergrund läuft wird von den Android Leuten nicht sonderlich gern gesehen. Der Hauptgrund ist sicherlich der erhöhte Akkuverbrauch, bzw. früher auch der  begrenzte Arbeitsspeicher der nicht allzu viele laufende Dienste ermöglichte. Heute ist das Problem mit dem Speicher so gut wie weg und ich persönlich konnte bei einem Dienst mehr keine negativen Auswirkungen auf meine Akkulaufzeit feststellen. Trotzdem wollte ich euch diese Thematik nicht vorenthalten.

Service Klasse erstellen

Wir haben aus dem Teil 1 bereits unseren BroadcastReceiver der über die MainActivity registriert wird. Das wollen wir aber nicht mehr, da die Lebensdauer des Receivers abhängig von der Activity ist. Daher erstellen wir eine neue Klasse „Service“ und lassen die über die „android.app.Service“ Klasse erweitern. Die fertige Klasse sieht so aus:

public class Service extends android.app.Service {
    private BroadcastReceiver br;
  
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();

    }
  
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
        filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    
        br = new MainReceiver();
        registerReceiver(br,filter);
    
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(br);
        super.onDestroy();
    }
}

Von der Android Klasse „Service“ bekommen wir mehrere Methoden direkt mit implementiert. Die erste Methode „onBind“ interessiert uns gar nicht. Diese Methode wird nur für die „Bound Services“ verwendet. Die nächste Methode „onCreate“ wird aufgerufen wenn der Dienst erstellt wird. Da brauchen wir erstmal kein Code. Die nächste Methode „onStartCommand“ ist da schon interessanter. Der Code da drin wird beim Start des Dienstes ausgeführt. Was direkt auffällt ist der uns schon bekannte Code. Dieser wurde einfach 1 zu 1 von der Klasse MainActivity aus dem ersten Teil übernommen. Der einzige Unterschied ist der Returnwert (int) der Methode. Verwenden wir den Rückgabewert über die Variable „START_STICKY“ dann versucht Android den Dienst, falls dieser beendet wurde, neuzustarten. Dies kann passieren wenn das System selber entscheidet, zum Beispiel bei einem geringen Arbeitsspeicher, unnötige Dienste zu beenden. Die letzte Methode „onDestroy“ wird beim beenden des Dienstes ausgeführt. Hier entfernen wir dann einfach den registrierten BroadcastReceiver aus dem System. Nun können wir einmal testen ob unser Dienst auch läuft wenn wir ihn starten und dann die APP beenden. Dafür ändern wir noch ein bisschen unsere MainActivity Klasse ab:

public class MainActivity extends android.app.Activity implements OnClickListener {

    private Button btn;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
    
        btn = (Button) findViewById(R.id.main_btn);
        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent i = new Intent(this, Service.class);
        this.startService(i);
        Toast.makeText(this, "Service started",Toast.LENGTH_SHORT).show();
    }
}

Ich habe im Layout ein Button hinzugefügt, diesen dann einfach in der Klasse deklariert und die Methode onClick übernommen. In der Methode wird ein neues Intent erstellt welcher die Service.class nutzt. Über die Methode „startService“ können wir dann unseren Dienst starten. Wenn wir nun die APP beenden und uns mal in den Einstellungen die laufenden Prozesse anschauen, werden wir feststellen, dass der Dienst trotzdem am Leben ist und weiter läuft.Screenshot_20160307-115930

 

BOOT_COMPLETED

Nun haben wir unseren kleinen Helfer im Hintergrund, der den Broadcast Receiver ohne eine Activity am Leben hält. Eine Sache müssen wir aber noch ändern. Starten wir das Telefon neu, so wird unser Dienst nicht wieder aufgerufen. Solange „nichts“ aktives den Dienst startet, kann auch unser Broadcast Receiver nicht registriert werden. Es gibt aber unter Android auch die Möglichkeit Broadcast Receiver direkt über die Manifest Datei zu deklarieren. Wird dann die APP zum ersten mal gestartet, so wird dann auch der im Manifest hinterlegte Receiver im System registriert. Ganz richtig gelesen im System und unabhängig von der APP selbst. Aber auf was reagiert dann der Broadcast Receiver? Dafür gibt es das Intent „BOOT_COMPLETED“ welches, nachdem das System hochgefahren wurde, ausgelöst wird. Schauen wir uns erstmal den Broadcast Receiver an (meine Klasse habe ich BootReceiver genannt):

public class BootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i = new Intent(context, Service.class);
        context.startService(i);
    }
}

Die Klasse wird über die Klasse „BroadcastReceiver“ erweitert. Dadurch bekommen wir die uns schon bekannte (aus Teil 1) Methode „onReceive“ implementiert. In der Methode machen wir genau das gleiche was unser Button aus der MainKlasse macht und zwar den Dienst starten. Das war die Klasse, nichts weiter. Wer sich jetzt wundert wo wir auf das passende Intent „horchen“ sollte sich keine Sorgen machen. Der Intent-Filter wird nämlich direkt im Manifest deklariert:

<application>
    ...
    <receiver android:name="BootReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"></action>
        </intent-filter>
    </receiver>
</application>

Wir deklarieren also den BroadcastReceiver über die Manifest Datei und setzen den Intent-Filter auf die Aktion „BOOT_COMPLETED“. Damit aber unsere APP auch das Intent abgreifen kann, müssen wir zusätzlich die Permission „RECEIVE_BOOT_COMPLETED“ hinzufügen. Im Grunde sind wir jetzt fertig und können unsere APP neu installieren. Wichtig bei, über die Manifest Datei deklarierten Receivern ist, dass die APP mindestens einmal gestartet werden muss. Seit Android 3.1 werden alle installierten APP’s im „stopped“ Modus installiert. Erst wenn man die APP zum ersten mal startet, werden alle Komponente im System registriert. Bis ich es heraus fand, bin ich fast dran verzweifelt (mehr Infos – Antwort von Cancer). Soooo…. damit wären wir nun auch mit dem zweiten Teil fertig. Jetzt müssten nur noch, die bereits bekannten Klassen (Stichwort: Asynctask) aus den anderen Beiträgen eingesetzt werden, damit man die Steuerung der Dosen übernehmen kann. Alle die schon die anderen Projekte gelesen haben können an dieser Stelle bestimmt weiter machen und für den Rest gilt, einfach auf den nächsten Beitrag warten.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.