Serververbindung erhalten, wenn Activity gewechselt wird

  • Antworten:19
Timur
  • Forum-Beiträge: 18

31.05.2014, 11:07:52 via Website

Hi,

ich habe eine App, die unter anderem in einer Activity eine Verbindung zu einerm Server herstellt. Das klappt alles wunderbar. Wenn der Server nun eine Anfrage stellt, soll der User einige Werte abfragen. Dazu muss er die Activity wechseln.
Komme ich nun in die Activity mit dem Server zrück, ist die Verbindung unterbrochen.
Wie kann ich die Verbindung weiter laufen lassen, obwohl ich die Activity wechsel?

Ich habe einiges über Service gelesen. Leider kommen der und ich noch nicht auf einen Nenner =)
Ich hoffe ihr könnt mir ein bisschen weiter helfen.

Antworten
Michele
  • Forum-Beiträge: 1.525

31.05.2014, 14:03:44 via Website

Hallo Timur.

Bitte nicht wundern, ich habe deinen Thread mal in den passenden Bereich verschoben.

Zu deinem Thema. Das ist richtig. Nutze für den Hintergrund einen Service.
http://developer.android.com/guide/components/services.html
http://developer.android.com/reference/android/app/Service.html

LG

— geändert am 31.05.2014, 14:06:38

Antworten
Timur
  • Forum-Beiträge: 18

31.05.2014, 14:40:09 via Website

Danke Michele,

die Seite habe ich auch schon gelesen =)
So wie ich das verstehe ist für mich wohl ein BoundService der richtige.
Ich habe die Serveranbindung in drei Klassen unterteilt, damit sie nicht zu groß wird.
Die eigentliche Verbindung wird in der Klasse TCPClient ausgeführt.
Die eigentliche Anwendung ist in einer Klasse Werkstatt_Client eingefügt. Sie greift auf TCPClient zu.
Ist es richtig, dass ich die Klasse TCPClient nun in den Service einbinden muss und dann mit der Klasse Werkstatt_Client darauf zugreife?

Ich habe das jetzt mal ausprobiert.
Die Anwendung läuft noch =) ,leider nicht so, wie ich mir das vorstelle.
Wenn ich auf den Button senden drücke, öffnet sich eine Activity mit der ein paar Daten eingegeben werden müssen.
Wenn ich dann auf speichern klicke, will ich wieder zurück zu der Activity mit der Serveranbindung.
Das mache ich dann über

Intent intent = new Intent(Einstellungen.this, Werkstatt_Client.class);
startActivity(intent);

Leider ist die Verbindung nicht mehr vorhanden.
Wenn ich den zurück Button des Smartphones nutze, komme ich auch in die Serverklasse. Da funktioniert noch alles.

— geändert am 31.05.2014, 15:28:54

Antworten
Fabian Simon
  • Forum-Beiträge: 359

02.06.2014, 13:33:44 via Website

Ohne genau geschaut zu haben : das Zauberwort heißt static bzw. singleton

Antworten
Timur
  • Forum-Beiträge: 18

02.06.2014, 19:39:58 via Website

Hi Fabian,

ich habe jetzt mal einen eigenen Service geschrieben. Ich rufe den Service in der Activity auf, wenn ich auf einen Button drücke. Das klappt auch ganz gut. Nun versuche ich in dem Service die Verbindung zum Server herzustellen. Wenn ich das versuche, kann ich genau eine Nachricht senden und dann fliegt eine NullPointerException.

Vielleicht hilft das:

Serviceklasse:

public class MyService extends Service {


 private TCPClient mTcpClient;



public void connection(){
    mTcpClient.run();
}

}

TCPClient:

public class TCPClient {


    /**Diese Methode handhabt die gesendeten
     * und empfangenen Nachrichten*/
    public void run() {

        mRun = true;

        try {
            //IP-Adresse des Servers übergeben
            InetAddress serverAddr = InetAddress.getByName(SERVERIP);
            //Sicherheitsausgabe an den Server senden
            Log.e("TCP Client", "Kunde: Connecting...");
            //Öffnet einen Socket, um eine Verbindung mit dem Server herzustellen
            Socket socket = new Socket(serverAddr, SERVERPORT);
            try {
                //Sendet die Nachricht zum Server
                out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
                //Sicherheitsausgabe an den Server
                Log.e("TCP Client", "Kunde: Sent.");
                Log.e("TCP Client", "Kunde: Done.");
                //Empfange Nachrichen vom Server
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                //Der Client achtet auf eingehende Nachrichten vom Server
                while (mRun) {
                    serverMessage = in.readLine();
                    if (serverMessage != null && mMessageListener != null) {
                        //Ruft die Methode messageReveives von der MainActivity auf
                        mMessageListener.messageReceived(serverMessage);
                    }
                    serverMessage = null;
                }
                Log.e("RESPONSE FROM SERVER", "Werkstattserver: Received Message: '" + serverMessage + "'");
            } catch (Exception e) {
                //Sicherheitsausgabe an den Server zum Debuggen
                Log.e("TCP", "Werkstattserver: Error", e);
              //Dieser Block wird ausgeführt, wenn ein Error aufgetreten ist
            } finally {
                //Bei einem Error wird der Socket geschlossen.
                socket.close();
            }
        } catch (Exception e) {
            //Sicherheitsausgabe an den Server zum Debuggen
            Log.e("TCP", "Kunde: Error", e);
        }
    }

Activity:

public class Werkstatt_Client extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState)
    {

.
.
.


        //OnClickListener für den Button Senden
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(Werkstatt_Client.this,OBDService.class);
                bindService(intent,serviceconntection,Context.BIND_AUTO_CREATE);
                  message = eingabe.getText().toString();    
                //Nachricht zum Verlauf hinzufügen
                arrayList.add("Kunde: " + message);
                //Nachricht an Server senden
                if (mTcpClient != null) {
                    mTcpClient.sendMessage(message);
                }
                //aktualisiert die ListView bei neuen Nachrichten
                mAdapter.notifyDataSetChanged();
                eingabe.setText("");
            }
        });


    }

    private MyService service;
    private ServiceConnection serviceconntection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            // TODO Auto-generated method stub
            LocalBinder lbinder = (LocalBinder)binder;
            service = lbinder.getService();
            Log.e("","onServiceconnected");
            service.connection();

        }
    };    



    /**Diese Methode äffnet einen TCPClient
     * (Asynchroner Task)*/
    public class connectTask extends AsyncTask<String,String,TCPClient> {

        @Override
        protected TCPClient doInBackground(String... message) {

            //Öffnet einen TCP Client
            mTcpClient = new TCPClient(new TCPClient.OnMessageReceived() {
                @Override
                //Methode übernimmt den String
                public void messageReceived(String message) {
                    publishProgress(message);
                }
            });
            mTcpClient.run();

            return null;
        }
}

Und hier die passende LogCat:

06-02 19:11:59.236: E/AndroidRuntime(29287): FATAL EXCEPTION: main
06-02 19:11:59.236: E/AndroidRuntime(29287): java.lang.NullPointerException
06-02 19:11:59.236: E/AndroidRuntime(29287):    at com.example.obdii.MyService.verbindung(OBDService.java:65)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at com.example.obdii.Werkstatt_Client$1.onServiceConnected(Werkstatt_Client.java:96)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1098)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1115)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at android.os.Handler.handleCallback(Handler.java:615)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at android.os.Handler.dispatchMessage(Handler.java:92)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at android.os.Looper.loop(Looper.java:137)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at android.app.ActivityThread.main(ActivityThread.java:4921)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at java.lang.reflect.Method.invokeNative(Native Method)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at java.lang.reflect.Method.invoke(Method.java:511)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1038)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:805)
06-02 19:11:59.236: E/AndroidRuntime(29287):    at dalvik.system.NativeStart.main(Native Method)

— geändert am 03.06.2014, 12:53:29

Antworten
Fabian Simon
  • Forum-Beiträge: 359

03.06.2014, 08:39:35 via Website

Viel zu viel Code....
du solltest schon ungefähr wissen wo sich das Problem befindet.
Niemand hat ja Lust soviel Quellcode zulesen.

Schon mal aktive dem Debugger verwendet ??

Aber wenn du auf soviel Code stehst.... hier meine Lösung von Damals....

public class ConnectionStream  extends Thread {

    private InputStream input;
    private OutputStream output;

    private IMessage message; //ist ein eigenes Interface an die die antworten weitergeleitet werden
    private boolean close;
    public ConnectionStream(InputStream input, OutputStream output, IMessage message){
        this.input = input;
        this.output = output;
        this.message = message;
        StaticObjects.hasAStreamToBox = true;
    }

    @Override
    public void run() {
        super.run();
        byte[] buffer = new byte[1024];
        int bytes;
        while (!close){
            try{
                bytes = input.read(buffer);
                String readMessage = new String(buffer, 0, bytes);
                message.newMessage(this, readMessage);
            }catch (Exception e){
                message.exceptionThrow(this, e);
                break;
            }
        }
        message.connectionClosed(this);
    }


    public InputStream getInputStream(){
        return input;
    }

    public void close(){
        this.close = true;
    }

    public void write(String json){

        try {
            output.write(json.getBytes());
            output.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}

Antworten
Timur
  • Forum-Beiträge: 18

03.06.2014, 12:51:34 via Website

Ich habe jetzt mal ein wenig gekürzt. Das Problem liegt halt in der run()-Methode vom TCPClient. Wenn der Service versucht sie aufzurufen, fliegt die erste Exception und dadurch folgerichtig die zweite, weil die Activity einen Fehler beim aufrufen der passenden Methode vom Service hat.

Wenn ich den Server ohne SERVICE ausprobiere, also als ganz normale Anwendung funktioniert er. Das Problem liegt also nicht am Server/Anwendung sonder darin, dass eine Exception beim aufrufen fliegt.

Meine Frage also, wo ich nun den Fehler beim aufrufen gemacht habe?:D

Antworten
Fabian Simon
  • Forum-Beiträge: 359

03.06.2014, 13:27:09 via Website

Zeige mal die Methode MyService.verbindung ich vermute der Fehler befindet sich in dieser Methode

Antworten
Timur
  • Forum-Beiträge: 18

03.06.2014, 15:25:42 via Website

In der Methode MyService.verbindung wird nur die run()-Methode ausgeführt.

Sie sieht folgendermaßen aus:

public void verbindung(){
TCPClient.run();
}

Ich habe mal ein bisschen versucht mich hiernach (http://stackoverflow.com/questions/14985678/how-to-keep-the-android-client-connected-to-the-server-even-on-activity-changes) zu richten. Es klappt aber leider immer noch nicht so richtg

Antworten
Fabian Simon
  • Forum-Beiträge: 359

03.06.2014, 15:35:02 via Website

Hast du die Run methode mal step bei step gedebuggt ? wann bzw. in welcher zeile kommt das null point exception??

Antworten
Timur
  • Forum-Beiträge: 18

03.06.2014, 15:41:16 via Website

Socket socket = new Socket(serverAddr, SERVERPORT);

In der Zeile fliegt die Exception.

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

03.06.2014, 16:30:34 via Website

Und was ist jetzt in der Zeile null?
Kann es sein dass zusetzlich noch eine NewworkOnMain Exception fliegt?
Du rufst nämlich direkt die Run Methode deines TCP Clients aus.Normalerweise sollte das in einem Thread laufen.

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
Timur
  • Forum-Beiträge: 18

03.06.2014, 16:46:47 via Website

Es fliegt noch eine ConnectException.
Wenn ich da rauf klicke, dann wird mir die gleiche Zeile angezeigt, wie ich sie in meinem letzten Thread geschrieben habe.
Ich haben jetzt die run()-Methode in meinem Service stehen in einer Unterklasse connectSocket. Nun habe ich mal eine Instanz auf die Unterklasse gelegt und dessen run()-Methode ausgeführt. Damit meckert er immer noch rum.

— geändert am 03.06.2014, 17:04:36

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

03.06.2014, 17:34:11 via Website

Du solltest nicht einfach rumprobieren sondern einfach mal deinen Code Systematisch durchgegen und Prüfen welche Variablen gesetzt sind etc. mit dem Debugger Tool geht das recht einfach.
(erst jetzt ersten post gelesen)
Kannst du nicht einfach beim wieder öffnen der Activity die Verbindung resetten und neu aufmachen?
ist das nicht wesentlich einfacher?
Oder ist das Session basierte Zeugs wichtig oder gar von Vorteil für Server/Client.

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
Timur
  • Forum-Beiträge: 18

03.06.2014, 19:30:20 via Website

Es ist nicht so, dass ich wahrlos rumprobiere =)
Für mich ist die Service Geschichte nur so neu.
Leider muss die Session erhalten bleiben.
Sonst hätte ich es auch schon ganz anders erledigt.

Ich habe mir jetzt mal mit Hifle des Debuggers und der Funktion

boolean k = socket.isConnected();

Ausgeben lassen, ob ich mit dem Server verbunden bin.
Es kommt ein true zurück. Wenn ich jetzt aber versuche eine Nachricht zu senden, klappt das nicht.

Ich regel das über eine If/else-Abfrage. Ich prüfe, ob der TCPClient ungleich null ist und dann soll die Nachricht gesendet werden.
Leider ist der TCPClient gleich null.

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

03.06.2014, 20:28:12 via Website

Dann speichere doch die Instanz des TCP Clients irgendwo temporär ab und lade sie danach wieder in die Activity rein.

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
Timur
  • Forum-Beiträge: 18

04.06.2014, 15:44:12 via Website

Kannst du das ein bisschen näher erläutern?
Vielleicht sollte ich noch dazu sagen, dass ich in Android/Java gerade den Übergang von Anfänger zu Fortgeschritten mache =)
Ich muss aber auf jeden Fall über den Service gehen. Da führt kein weg dran vorbei.

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

04.06.2014, 17:40:39 via Website

Wieso, kannst du das nicht einfach über SharedPreferences etwas so [ http://stackoverflow.com/questions/7145606/how-android-sharedpreferences-save-store-object] speichern.

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
Timur
  • Forum-Beiträge: 18

05.06.2014, 10:16:24 via Website

Weil ich als Vorgabe einen Service nehmen soll. Ich habe mit auch schon hundert bessere Möglichkeiten angesehen =)

Antworten