Android (LIRC) Einstellungen mithilfe der SharedPreferences speichern und laden

Hallo,

im letzten Beitrag sind wir mehr auf die grafische Seite unserer LIRC App eingegangen. In diesen Beitrag können wir bisschen mehr programmieren ;). Bis jetzt verwendet unsere App eine fest eingetragene IP und Port Nummer. Was aber wenn sich die IP Adresse oder die Port Nummer ändert? Momentan müssten wir diese Werte in der Klasse ändern und die APP neu compilieren. Warum erstellen wir uns nicht einfach eine Activity die nicht nur diese Einstellungen fest speichert sondern auch die TCP Verbindung überprüfen kann? Fangen wir also an:

1.) Config Layout erstellen

Damit wir die Einstellungen speichern können, müssen wie dir erst irgendwo eingeben können. Ich habe ein neues (RelativeLayout) Layout erstellt und es activity_config.xml genannt. Danach habe ich zwei Eingabefelder (für die IP und Port Nummer) und zwei Buttons (zum speichern der Einstellungen und zum überprüfen ob die angegebene TCP Verbindung erreichbar ist) erstellt. Am Ende könnte es in diese Richtung gehen:

AC1

Für den weiteren Text werde ich diese ID’s für die oben dargestellen Objekte nutzen:

edit_ip
edit_port
btn_check
btn_save

2.) Config Klasse (optional)

Ich habe für die IP und die Port Nummer eine extra Klasse erstellt wo ich mithilfe der Getter und Setter die jeweiligen Daten setzten und holen kann. Diese Klasse ist aber vollkommen optional und muss überhaupt nicht erstellt werden. Falls ihr es doch machen wollt braucht ihr nur eine neue Klasse (ich habe meine Config gennant) und diesen Code:

package de.bobek.irremote;

public class Config {
     private static String ip = new String();
     private static int port;
 
     public static String getIp() {
          return ip;
     }
     public static void setIp(String ip) {
          Config.ip = ip;
     }
     
     public static int getPort() {
          return port;
     }
     public static void setPort(int port) {
          Config.port = port;
     } 
}

Wie Ihr sieht werden hier nur zwei Variablen erstellt einmal ‚ip‘ als String und einmal ‚port‘ als int. Danach folgen die entsprechenden Getter und Setter.

3.) SharedPreferences

SharedPreferences ist eine Android Funktionsbibliothek und eignet sich super zum Speichern von APP Einstellungen. Die Daten sind auch nach dem Beenden der APP Verfügbar und können beim erneuten Start geladen werden. So wie gehen wir also vor? Zuerst erstellen wir eine neue Klasse für die ‚conig_activity‘. Dort initialisieren wir die zwei Edit Felder und Buttons. Nun richten wir unsere Aufmerksamkeit zuerst auf den Button btn_save. Bei einem Klick sollen die beiden Eingabefelder ausgelesen werden und die Einstellungen über die Setter in die Config Klasse gespeichert werden. Das ist simpel und reicht uns erstmal vollkommen aus:

 @Override
 public void onClick(View v) {
 switch (v.getId()){
      case R.id.config_btn_save:
           Config.setIp(edit_ip.getText().toString());
           Config.setPort(Integer.parseInt(edit_port.getText().toString()));
           Toast.makeText(getApplicationContext(),"Einstellungen gespeichert!",Toast.LENGTH_SHORT).show();
           break;
      case R.id.config_btn_check:
           //Verbindungsstest
           break;
      }
 }

Die Port Nummer müssen wir entsprechend als Integer casten. Zusätzlich können wir uns eine kleine Meldung ausgeben lassen das die Einstellungen gespeichert worden sind.

So aber wo nutzen wir die SharedPreferences Methoden? Ganz einfach in unserer MainActivity Klasse also die Klasse die beim starten der APP als erste aufgerufen wird. Dort müssen wir zwei Zustände behandeln. Einmal das Speichern und einmal das Laden der Einstellungen.

Das Speichern:

Für das Speichern verwenden wir die Methode onDestory() aus der Superklasse Activity (mehr infos : http://developer.android.com/images/activity_lifecycle.png) . Dort werden wir über die  SharedPreferences die Einstellungen speichern sobald die Activity geschlossen wird.

public static final String PREFS_NAME = "Config";
...
@Override
public void onDestroy() {
     super.onDestroy();
     SharedPreferences settings = getSharedPreferences(MainActivity.PREFS_NAME, 0);
     SharedPreferences.Editor editor = settings.edit();
     editor.putString("net_ip", Config.getIp());
     editor.putInt("net_port", Config.getPort());
     editor.commit();
}

Es wird hier ein SharedPreferences Objekt erstellt. Diesem Objekt fügen wir einen sogennanten Editor hinzu wo wir die entsprechenden Einstellungen aus der Config Klasse holen und sie mit der Methode commit() zu den SharedPreferences hinzufügen.

Das Laden:

So jetzt könnt ihr euch bestimmt denken von wo die Einstellungen geladen werden. Ganz Genau! Einfach dann wenn die onCreate Methode der MainActivity aufgerufen wird. Hierfür können wir uns aber eine extra Methode erstellen und die dann dort aufrufen:

protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);

     LoadPreferences();
}
...
private void LoadPreferences(){
 SharedPreferences sharedPreferences = getSharedPreferences(MainActivity.PREFS_NAME,0);
 Config.setIp(sharedPreferences.getString("net_ip","192.168.1.100"));
 Config.setPort(sharedPreferences.getInt("net_port", 8765));
}

Nun wird beim Aufruf von onCreate() die LoadPreferences() Methode aufgerufen. Dort werden aus dem SharedPreferences die beiden Werte wieder geholt und der Klasse Config übergeben. Nebenbei die beiden Werte „192.168.1.100“ und 8765 dienen nur als Default Werte falls beim Laden etwas schief läuft.

Damit wir auch die geladenen Werte sehen können müssen wir Entsprechend die onCreate Methode der config_activity Klasse anpassen:

@Override
protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
 
     setContentView(R.layout.activity_config);
 
     ...
     edit_ip = (EditText)findViewById(R.id.edit_ip);
     edit_port = (EditText)findViewById(R.id.edit_port);
     ... 
 
     edit_ip.setText(Config.getIp());
     edit_port.setText(String.valueOf(Config.getPort()));
}

Hier werden die Werte wieder aus der Config Klasse genommen und in die beiden Eingabefelder gesetzt. Die Port Nummer müssen wir dann wieder zurück zum String umwandeln.

4.) Connection Klasse anpassen

Sooo… nun werden unsere Einstellungen gespeichert und wir brauchen keine Angst mehr zu haben das sich die IP oder Port Nummer ändern könnte. Für den letzten Schritt müssen wir aber noch unsere Connection Klasse anpassen, die momentan noch so aussieht:

package de.bobek.irremote;
import android.os.AsyncTask;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;

public class Connection extends AsyncTask<String,Void,Void>{

private Socket socket;
private PrintWriter pw;
private String IP = "192.168.1.000";
private int Port = 8765;

@Override
protected Void doInBackground(String... Command) {
     try {
          socket = new Socket(IP,Port);
          pw = new PrintWriter(socket.getOutputStream(),true);
          pw.println(Command[0].toString());
          pw.flush();
          socket.close();
     } catch (IOException e) {
          e.printStackTrace();
     }
     return null;
     }
}

Die AsyncTask Klasse bietet uns die Methode onPreExecute() an, die Ausgeführt wird bevor die doInBackground Methode aufgerufen wird. Da könnte man sich doch ganz einfach die Werte aus der Config Klasse holen oder nicht 🙂 ? Wir brauchen also nichts weiter zu machen als die fest vergebenen Werte für die IP und Port Nummer zu löschen und sie erst in der onPreExecute() Methode zu definieren bzw. setzen:

...
private Socket socket;
private PrintWriter pw;
private String IP;
private int Port;

@Override
 protected void onPreExecute() {
 IP = Config.getIp();
 Port = Config.getPort();
}
@Override
protected Void doInBackground(String... Command) {
...

Damit kann unsere Connection Klasse immer die aktuellsten Werte für die Verbindung benutzen.

Wie überprüfen wir aber ob der TCP Server erreichbar ist? Dazu später mehr…

Update 22.11.2015 Verbindung überprüfen

Nun stellt sich die Frage wie man überprüfen kann, ob die Verbindung zum Server aufgebaut werden konnte. Ich werde hier die einfachste Methode vorstellen. Zuerst müssen wir die „Connection“ Klasse anpassen:

package de.bobek.irremote;
import android.os.AsyncTask;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;

public class Connection extends AsyncTask<String,Void,String>{

private Socket socket;
private PrintWriter pw;
private String IP = "192.168.1.000";
private int Port = 8765;
private String Status = "";

@Override
protected String doInBackground(String... Command) {
     try {
          socket = new Socket(IP,Port);
          if (!Command[0].equals("CHECKCONN"){
               pw = new PrintWriter(socket.getOutputStream(),true);
               pw.println(Command[0].toString());
               pw.flush();
          }
          Status = "Verbindung erfolgreich!";
          socket.close();
     } catch (IOException e) {
          Status = "Verbindung fehlgeschlagen! " + e.toString();
     }
     return Status;
     }
}

Als erstes müssen wir den letzten Parameter der AsyncTask Klasse in ein String umwandeln. Denn die Methode „doInBackground“ liefert uns nun die zuvor erstelle private Variable „Status“ zurück. Wir greifen nur auf die mögliche „IOException“ zurück welche ausgelöst wird wenn die IP und der Port nicht auf den angegebenen Daten aufgemacht werden konnte. Die IF Abfrage wird dann aufgelöst wenn das erste Element von „Command“ nicht „CHECKCONN“ gleicht. Somit können wir gewährleisten das wir hier nur den Socket überprüfen und es nicht versucht wird möglicherweise auf einen nicht existierenden Socket Daten zu übertragen. Konnte die Verbindung aufgebaut werden wird die Variable „Status“ mit „Verbindung erfolgreich“ gefüllt. Wird die Exception ausgelöst müssen wir die Variable „Status“ entsprechend anders füllen.Am Ende der Funktion wird Status zurückgegeben.  Das letzte was wir machen müssen ist die Funktion dem Button „Check Status“ zuweisen. Das sieht dann so aus:

 @Override
 public void onClick(View v) {
 switch (v.getId()){
      case R.id.config_btn_save:
           Config.setIp(edit_ip.getText().toString());
           Config.setPort(Integer.parseInt(edit_port.getText().toString()));
           Toast.makeText(getApplicationContext(),"Einstellungen gespeichert!",Toast.LENGTH_SHORT).show();
           break;
      case R.id.config_btn_check:
           String Status = new Connection().execute("CHECKCONN").get():
           Toast.makeText(getApplicationContext(),Status,Toast.LENGTH_SHORT).show();
           break;
      }
 }

Drücken wir nun den Button „Check Status“ nachdem wir die Einstellungen gespeichert haben (wollen wir es direkt testen ohne es speichern zu müssen, dann muss entsprechend über die Config Setter die Variablen aus den „EditText“ gesetzt werden) übergeben  wir der Methode „execute“ den String „CHECKCONN“.Mit get() zwingen wir den Thread so lange zu pausieren bis uns die Methode eine Antwort liefert (D.h die APP pausiert, weil der MainThread auch angehalten wird). Die Antwort wird dann in den String „Status“ gespeichert. Danach wird diese Variable über ein Toast ausgegeben. Das wäre eine einfache Methode wie man die IP und den Port auf die Richtigkeit überprüfen kann. Will man mehr Informationen vom Server haben empfehle ich einen eigenen Server zu basteln der uns entsprechend mehr Informationen zurückliefern kann, sei es zum Beispiel die aktuelle Server Laufzeit (Mehr Infos dazu findet ihr hier).

4 Gedanken zu „Android (LIRC) Einstellungen mithilfe der SharedPreferences speichern und laden

Schreibe einen Kommentar

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