Apps für Android programmieren leicht gemacht!

Raspberry Pi – Bewegungsmelder – Protokoll

Auf Anfrage hin werde ich in diesem Artikel erklären, wie ihr die normalerweise nur als Benachrichtigung ausgegebenen Nachrichten von eurem Raspberry Pi Bewegungsmelder auch innerhalb der App protokollieren und anzeigen lassen könnt.

Ich beziehe mich dabei darauf, dass ihr die entsprechende Android App bereits zum Laufen bekommen habt und nun die sagen wir mal 20 letzten Nachrichten innerhalb der App protokollieren und anzeigen lassen wollt.
Die Hürde, die wir hier überwinden müssen, ist die Speicherung der Nachrichten und das Entfernen der Nachrichten, die das Limit von 20 Nachrichten überschreiten.

Wir nutzen bereits innerhalb unserer App die sogenannten SharedPreferences, um zu protokollieren, ob wir uns bereits bei Google für den Cloud Messaging Dienst angemeldet haben. Diese SharedPreferences sind ein dauerhafter Speicher. Gedacht sind sie eigentlich, um  beispielsweise einzelne Einstellungen zu speichern und wieder abzurufen.

Da wir hier eine Liste von bis zu 20 protokollierten Nachrichten speichern wollen, würde sich ein Array (eine Liste) von Strings (Texten) anbieten.
Das Problem ist nur, dass wir mit Hilfe der SharedPreferences keine ganzen Arrays (Listen) speichern können, sondern immer nur ein Element des Arrays.

Um dieses Problem zu umgehen werden wir einfach 20 verschiedene SharedPreferences Einträge erstellen. Zum Glück müssen wir hierfür keine 20 neuen Variablen erstellen, sondern lediglich bei Erstellung der SharedPreferences einen neuen Namen übergeben.

Schlussendlich müssen wir die Daten, welche im SharedPreferences gespeichert wurden, nur noch auslesen und in einer ListeView innerhalb der Android App wieder anzeigen.

 

Beispiel:

Um das Ganze ein wenig besser erklären zu können gibt es noch einmal ein kleines Beispiel.
Nehmen wir an wir benutzen für den Namen der 20 verschiedenen SharedPreferences einfach die Namen „protokoll_[0-19]“, so können wir eine Schleife benutzen. Innerhalb der Schleife können wir die Zahl hinter „protokoll_“ jedes mal um 1 hoch zählen.

Speichern wir also eine neue Nachricht ab, dann gehen wir wie folgt vor:
Wir lesen alle Protokolle einzeln aus den SharedPreferneces aus und schreiben sie, wie wir es eh wollten in ein Array (Liste).
Nun können wir beginnen die neue Nachricht zu speichern und alle weiteren um 1 Stelle nach hinten zu verschieben. Hierzu speichern wir als Erstes die neue Nachricht an die Stelle 0 ab. Unser Array nutzen wir nun, um Schrittweise jede Stelle des Arrays (beginnend bei 0) in die SharedPreferences (beginnend bei 1, da 0 die neue Nachricht ist) abzuspeichern. Die letzte Stelle des Arrays (Stelle 19, aber Nachricht 20, da Java bei 0 beginnt zu zählen) vernachlässigen wir.

Hier noch ein kleines Schaubild:

schaubild - protokoll

Wollen wir das Protokoll auslesen, dann gehen wir einfach 20 Schritte mal die SharedPreferences durch und schreiben ja nach Schritt die Ausgabe in ein Array.
Sind wir nach 20 Stellen damit fertig geben wir einfach das Array zurück.

 

Programmierung

Auslesen:

Da zum Speichern von neuen Nachrichten im Protokoll auch das Auslesen der Daten gehört, fangen wir damit an die Daten auszulesen.

Da wir unsere Protokolle einzeln unter unterschiedlichen Namen speichern, werden wir die unterschiedlichen Namen nun mittels einer Schleife auslesen.

public String[] leseProtokoll(){
    String[] rueckgabe = new String[Einstellungen.PROTOKOLL_SIZE];
    SharedPreferences sharedPreferences =
            PreferenceManager.getDefaultSharedPreferences(this);
    
    for(int i = 0; i < Einstellungen.PROTOKOLL_SIZE; i++) {
        rueckgabe[i] = sharedPreferences.getString("protokoll_" + i, "---");
    }
    
    return rueckgabe;
}

Diese Funktion fügen wir nun in die Klassen MainActivity und GCMListener ein.

 

Speichern:

Diesen Code fügen wir einfach in die Klasse GCMListener ein:

public void speichereProtokoll(String neueNachricht){

    String[] alteNachrichten = leseProtokoll();
    SharedPreferences sharedPreferences =
            PreferenceManager.getDefaultSharedPreferences(this);
    SharedPreferences.Editor editor = sharedPreferences.edit();

    editor.putString("protokoll_0", neueNachricht);

    for(int i = 1; i < Einstellungen.PROTOKOLL_SIZE; i++){
        editor.putString("protokoll_" + i, alteNachrichten[i-1]);
    }

    editor.commit();
}

 

Protokolllänge:

Damit wir unsere Protokolllänge so variabel wie möglich definieren können werden wir einfach eine Zahl für die Länge in der Klasse „Einstellungen“ definieren:

public static final int PROTOKOLL_SIZE = 20;

 

Protokollierung:

Nun haben wir die nötigen Schritte unternommen, um unsere Nachrichten protokollieren zu können, gespeichert werden sie allerdings noch nicht.
Wechselt in die Klasse „GCMListener“ und fügt unter der Zeile „sendNotification(ort + “ – “ + formatter.format(date), getIdentifier(ort));“ ein:

speichereProtokoll(ort + " - " + formatter.format(date));

Marvin

Ich bin 23 Jahre jung und studiere zurzeit Wirtschaftsinformatik an der Georg-August-Universität in Göttingen. Ich bin ein Mensch, der sich neben der Programmierung noch für tausend andere Dinge interessiert, die mal mehr und mal weniger verrückt sind. Vor allem aber bin ich Feuer und Flamme mit der Programmierung von eigenen kleinen Apps und Programmen, die mein Leben bereichern.

7 Kommentare

*Pflichtfeld

  • Hallo,

    mir ist nicht klar wo ich den letzten Abschnitt einfügen soll

    ArrayAdapter itemsAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, leseProtokoll());
    ListView listView = (ListView) findViewById(R.id.protokollListe);
    listView.setAdapter(itemsAdapter);

    LG

    • Hallo Michael,

      in der Hauptklasse MainActivity gibt es eine Funktion namens „onCreate“.
      Dort fügst du den letzten Code ein.
      Allerdings muss er ganz nach unten innerhalb der Funktion. Das sieht in etwas so aus:
      @Override
      protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      .......
      HIER KOMMT NOCH MEHR
      .......

      HIER DEN ABSCHNITT VON OBEN EINFÜGEN

      }

      [Nächste Funktion]

  • Error:(52, 9) error: cannot find symbol class ArrayAdapter
    Error:(52, 49) error: cannot find symbol class ArrayAdapter
    Error:(53, 9) error: cannot find symbol class ListView
    Error:(53, 30) error: cannot find symbol class ListView

    • Hallo Michael,

      die Textpassagen „ArrayAdapater“ und „ListView“ sollten rot geschrieben sein. Dies liegt daran, dass diese beiden Klassen nicht in die Klasse MainActivity importiert wurden.
      Bitte klicke mal auf diese beiden rot geschriebenen Texte. Nun sollte sich ein kleines Pop-Up auf machen und du wirst dazu aufgefordert Alt+Enter zu klicken. Android Studio wird durch diese Tastenkombination dazu angewiesen die beiden benötigten Klassen in die Klasse MainActivity zu importieren.

      Alternativ kannst du in der Klasse MainActivity auch nach ganz oben gehen. Hier sollte/sollten eine Zeile/mehrere Zeilen stehen, die mit „import“ anfängt/anfangen.
      Füge diese beiden Zeilen hinzu:
      import android.widget.ArrayAdapter;
      import android.widget.ListView;

      Gruß,
      Marvin

  • Ok, danke
    Das Läuft.

    Jetzt habe ich noch folgendes Problem.

    Da ich mir ja nicht die NAchrichten von einem Bewegungsmelder schicken lassen funktioinert ja das ja nicht:
    sendNotification(ort + “ – “ + formatter.format(date), getIdentifier(ort));
    speichereProtokoll(ort + “ – “ + formatter.format(date));

    also habe ich folgendes eingefügt:

    sendNotification(jsonStringMessage, getIdentifier(jsonStringMessage));
    speichereProtokoll(jsonStringMessage);

    Leider speichert er mir damit immer nur die Letzte Benachrichtigung und überschreibt die alte. Damit ist immer genau eine Benachrichtigung gespeichert.

    ———–

    09-09 01:49:44.549 17933-17933/de.test.test E/FirebaseInstanceId: Failed to resolve target intent service, skipping classname enforcement
    09-09 01:49:44.559 968-1888/system_process W/ActivityManager: Unable to start service Intent { act=com.google.firebase.MESSAGING_EVENT pkg=de.test.test (has extras) } U=0: not found
    09-09 01:49:44.567 17933-17933/de.test.test E/FirebaseInstanceId: Error while delivering the message: ServiceIntent not found.

    ———–

    Ausserdem sehe ich in der App 20 Platzhalter mit „—„. Ist das Gewollt? Kann man nur etwas anzeigen wenn auch eine Nachricht vorhanden ist?

    LG

    • Hallo Michael,

      ich habe einen kleinen Fehler gemacht, bei der Programmierung.
      Das kommt davon, wenn man den Code nur in den Artikel schreibt und nicht in Android Studio testet.

      Zur Lösung des Problems musst du nur die Zeile editor.putString("protokoll_" + i, alteNachrichten[i]); in der Funktion „speichereProtokoll“ etwas ändern.
      Aus dem alteNachrichten[i] muss alteNachrichten[i-1] werden.

      Vielen Dank für den Hinweis, ich habe den Fehler aus dem Artikel bereits heraus genommen.

      Die Firebase Fehlermeldung kannst du getrost ignorieren.
      Firebase ist ein Service, der ebenfalls das Senden von Nachrichten erlauben würde. Wir haben uns in diesem Artikel allerdings für den GCM Dienst entschieden.

      Die Platzhalter sind bedingt durch das Auslesen der Protokolle zu sehen.
      Wenn wir jetzt schreiben würden sharedPreferences.getString("protokoll_234", "-1-");, dann würde die Funktion den Text „-1-“ ausgeben, weil unter dem Namen „protokoll_234“ kein Text gespeichert ist. Wir können so Fehler vermeiden. Wenn unsere App ein Protokoll ausliest und einen Text erwartet, aber kein Text zurück gegeben wird, dann könnte die App abstürzen. Durch einen Platzhalter passiert das nicht.

      Gruß,
      Marvin