Message Handler erzeugen

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Message Handler erzeugen

    Hallo,
    ich hätte da mal wieder ein Problem:

    Ich versuche immer noch, Messages vom UI-Thread an einen anderen Thread zu senden.

    Momentan habe ich den neuen Thread mit einem Looper und einem Handler versehen.
    Dazu gibt es eine Menge Beispiele im Netz, aber:
    1. Die wenigsten funktionieren wirklich (Absturz)
    2. Der Handler wird immer "innerhalb des Threads erzeugt. Das finde ich erstens unschön und unübersichtlich, zweitens blickt man nicht mehr durch, wenn der Handler bzw. der Thread größer wird ( bei mir der Fall):

    Also wollte ich den Handler als "separate Methode" definieren, also etwa so:

    Quellcode

    1. public class ClassComThread extends Thread
    2. {
    3. public Handler mHandler;
    4. @Override
    5. public void run()
    6. {
    7. Looper.prepare();
    8. mHandler = new Handler(Looper.getMainLooper());
    9. Looper.loop();
    10. }
    11. public void handleMessage(Message msg)
    12. {
    13. Log.i("ComThread","Message from Application");
    14. }
    15. }
    Alles anzeigen
    Leider bekomme ich das so nicht kompiliert. Kann mir jemand sagen, wie es richtig gemacht wird????
  • Nochmal die Frage etwas genauer beschrieben:

    Ich möchte einen TCP/IP Client schreiben.

    Alle Beispiele, die ich im Netz finde, funktionieren nicht, sind extrem kompliziert und nicht nachvollziehbar,

    Wenn es ein einfaches Beispiel gibt, dann funktioniert es IMMER nach folgendem Schema:

    1. Main-Activity erzeugt einen Thread und übergibt das zu sendende Kommando im Konstruktor des Threads.
    2. Der Thread wird gestartet,
    3. der Thread baut eine Verbindung zum Server auf, sendet das im Konstruktor übergebene Kommando, schließt die Verbindung und beendet sich sofort wieder.

    Mit anderen Worten :

    1. Ich kann immer nur EIN EINZIGES KOMMANDO an den Server senden, nämlich das, was im Konstruktor des Threads übergeben wurde.
    2. Für jedes Kommando, was ich senden möchte, wird eine TCP/IP Verbindung auf-und abgebaut .

    Was ich jetzt möchte ist folgendes:
    Ich möchte die Verbindung halten.
    Ich möchte im Thread Konstruktor NUR die Verbindungsdaten übergeben ( Ip-Adresse des Servers, Port)
    Ich möchte, das die Run-Methode des Threads NICHT als Sequenz läuft ( Verbinden, Senden; Verbindung trennen, Thread-Ende !)
    Ich möchte, das die Run Methode des Threads in einer Schleife läuft, die erst dann beendet wird, wenn ein "Disconnect" Befehl vom UI-Thread gesendet wird.
    Und ich möchte vom UI-Thread aus jederzeit und nacheinander Kommandos an den Thread senden, die dieser dann an den Server weitersendet und die Antwort an den UI-Thread zurückgibt.

    Und das bekomme ich nicht hin.
    Es ist aber kein Kompilierproblem, kein Absturz oder so, sondern eher ein Verständnisproblem, wie man das im Code formuliert.

    Meine erste Idee war:
    Ich verpasse dem TCP-Client Thread einfach eine neue Methode:
    thread.SendData( String Data )
    Das habe ich nicht hinbekommen, ich glaube, das es daran liegt, das man einem eigenen Thread, der von einer Android-Thread Klasse abgeleitet wird, keine Extra Funktionen verpassen kann. Man kann nur die vorhandenen überschreiben ( Was man z.B. mit der Run-Methode macht )

    Meine zweite Idee war:
    Man verpasst dem Thread eine Message-Queue und sendet einfach Messages vom UI-Thread an den TCP-Thread.
    Der Empfänger einer Message ist dann ein Handler.

    In den Beispielen im Netz steht immer folgendes:
    1. Looper hinzufügen
    2. Handler definieren/erzeugen
    3. Handler mit dem Loopper verbinden.

    Also so:

    Quellcode

    1. class MyThread extends Thread
    2. {
    3. public Handler mHandler;
    4. @Override
    5. public void run()
    6. {
    7. Looper.prepare();
    8. mHandler = new Handler()
    9. {
    10. public void handleMessage(Message msg)
    11. {
    12. // Nachricht verarbeiten
    13. }
    14. };
    15. Looper.loop();
    16. }
    17. }
    Alles anzeigen

    Was mir daran nicht gefällt ist die "eingeschobene" Definition des Handlers.
    Wenn man so programmiert, hat man hinterher den kompletten Code einer Applikation in einer Funktion stehen und das finde ich Mist.

    Wie bekomme ich den Handler "aus der Run-Methode raus"?

    Ich möchte es so haben :

    Quellcode

    1. class MyThread extends Thread
    2. {
    3. public Handler mHandler;
    4. @Override
    5. public void run()
    6. {
    7. Looper.prepare();
    8. mHandler = new Handler();
    9. Looper.loop();
    10. }
    11. public void handleMessage(Message msg)
    12. {
    13. // Nachricht verarbeiten
    14. }
    15. }
    Alles anzeigen

    So geht es aber nicht.
    Der Code lässt sich kompilieren.
    An den Imports kann es also nicht liegen.
    Nur : Wenn ich im UI-Thread etwas sende, kommt es im Handler des TCP-Threads nicht an.

    Mir ist auch nicht klar, wie ich beim Erzeugen des Handlers mit new Handler die Verbindung mit meinem Handler herstellen muss.
    Und genau das ist die eigentliche Frage
  • Hallo das mit dem Import habe ich gesagt, weil du von Compiler Problemen gesprochen hast.


    Irgendwie verstehe ich dein Problem nicht.
    Du hast schon einen funktionierenden Code im letzten Thread von mir bekommen.


    In der Run Methode nur einen neuen Handler erstellen mit mHandler = new Handler();
    Reicht nicht, du hast jetzt einen neuern Handler in den du nichts überschreiben kannst.
    Du kannst da nichts Oberrieden.

    Das mit den geschweiften Klammern ist eine Kurzform von einer abgeleiteten Kasse. Alles was in den Klammern steht ist in der abgeleiteten Klasse.
    Du kannst dir auch eine Klasse richtig mit Namen und extends erstellen und die benutzen. Ausführlicher weg.

    Wenn du nicht alles in der Run Methode haben willst warum erstellt du dir nicht einzelne Methoden und rufst die in der Run auf. Alles was du im Run aufrufst läuft im Thread.

    Dein zweites Beispiel wird und kann nie gehen. Deine Bearbeitung Methode ist jetzt eine neue Methode in der Kasse von Thread
    nicht vom Handler.
    Die wird nie aufgerufen. Ist mit Sicherheit auch ausgegraut.


    Ich glaube du solltest dir die Grundlagen von OOP nochmal ansehen.


    Quellcode

    1. import android.os.Handler;
    2. import android.os.Looper;
    3. import android.os.Message;
    4. class LooperThread extends Thread {
    5. public Handler mHandler ;
    6. class MyHandler extends Handler{
    7. @Override
    8. public void handleMessage(Message msg) {
    9. switch (msg.what){
    10. case 0:
    11. doLongRunningOperation();
    12. break;
    13. case 1:
    14. doLong2();
    15. break;
    16. }
    17. }
    18. }
    19. public void run() {
    20. Looper.prepare();
    21. mHandler = new MyHandler();
    22. Looper.loop();
    23. }
    24. void doLongRunningOperation() {
    25. // Add long running operation here.
    26. }
    27. void doLong2() {
    28. // Add long running operation here.
    29. }
    30. }
    Alles anzeigen

    Activity

    Quellcode

    1. public class LooperActivity extends AppCompatActivity {
    2. LooperThread mLooperThread;
    3. public void onCreate(Bundle savedInstanceState) {
    4. super.onCreate(savedInstanceState);
    5. setContentView(R.layout.activity_main);
    6. mLooperThread = new LooperThread();
    7. mLooperThread.start();
    8. }
    9. public void onClick(View v) {
    10. if (mLooperThread.mHandler != null) {
    11. Message msg = mLooperThread.mHandler.obtainMessage(0);
    12. mLooperThread.mHandler.sendMessage(msg);
    13. }
    14. }
    15. protected void onDestroy() {
    16. super.onDestroy();
    17. mLooperThread.mHandler.getLooper().quit();
    18. }
    19. }
    Alles anzeigen
    Ein Feedback auf Tipps ist auch schön. :P
  • Ich glaube, mein Problem ist eher Java.
    Ich tue mich damit echt schwer, ich finde diese Sprache sehr verworren und umständlich.

    Ich bin in der Zwischenzeit schon weiter gekommen, und bin im Prinzip auf die gleiche Idee gekommen, wie in deinem Vorschlag:

    Ich hab's jetzt so:

    Quellcode

    1. public class ClassComHandler extends Handler
    2. {
    3. @Override
    4. public void handleMessage(Message msg)
    5. {
    6. // Nachricht verarbeiten
    7. Log.i("Test","Message from UI Thread" );
    8. }
    9. }

    Also der UI-Thread erzeugt einen TCP-IP Thread, dieser erzeugt den Handler. UI-Thread,
    TCP-Thread und Handler stehen jetzt in einem separaten File.
    Die Aufrufe vom UI-Thread kommen jetzt auch im Handler an.

    Nur habe ich jetzt wieder das gleiche Problem wie vorher nur in Grün:

    Der Handler kann nicht auf Variablen im TCP-Thread zugreifen.

    Wenn ich also im TCP-Thread die Verbindung aufbaue, habe ich ja einen Socket.
    Und wenn der Sendebefehl vom UI-Thread im Handler ankommt, will ich den Befehl über diesen Socket versenden.
    Nur komme da wieder nicht dran.

    Irgendwie drehe ich mich ständig im Kreis.

    Es ist einfach nicht möglich, Daten von einem Thread in den anderen zu bekommen.
    Immer geht irgendwas nicht.

    Hier nochmal alles zusammen:

    Quellcode

    1. // Auszug aus onCreate in der MainApplication
    2. comThread = new ClassComThread( serverHost, serverPort, msgHandlerApp );
    3. comThread.start();
    4. // in einem onClick-Handler in der MainApplication:
    5. msgToComThread = comThread.msgHandlerCom.obtainMessage(0);
    6. comThread.msgHandlerCom.sendMessage(msgToComThread);
    7. // Die run methode des TCP/IP threads:
    8. @Override
    9. public void run()
    10. {
    11. Looper.prepare();
    12. msgHandlerCom = new ClassComHandler();
    13. try
    14. {
    15. socket = new Socket(strHostName, intPortNumber);
    16. writer = new BufferedWriter( new OutputStreamWriter( socket.getOutputStream() ) );
    17. reader = new BufferedReader( new InputStreamReader( socket.getInputStream() ) );
    18. }
    19. catch(Exception ex)
    20. {
    21. //:TODO Handle exceptions
    22. }
    23. Looper.loop();
    24. }
    25. }
    26. // Und im Com-Handler:
    27. public class ClassComHandler extends Handler
    28. {
    29. @Override
    30. public void handleMessage(Message msg)
    31. {
    32. // Nachricht verarbeiten
    33. // Wie sendet man jetzt hier etwas über den writer des TCP-Treads?????
    34. // Auf die Variablen des TCP-Threads habe ich hier keinen Zugriff.
    35. // der Handler bringt mir also gar nichts, ich kann trotzdem nichts senden.
    36. Log.i("Test","Message from UI Thread" );
    37. }
    38. }
    Alles anzeigen
  • Ich bin in der Zwischenzeit schon weiter gekommen, und bin im Prinzip auf die gleiche Idee gekommen, wie in deinem Vorschlag:
    Das ist keine Idee, sondern die ausführliche Schreibweise.




    Und wenn der Sendebefehl vom UI-Thread im Handler ankommt, will ich den Befehl über diesen Socket versenden.

    Nur komme da wieder nicht dran.
    Wie so wenn du den Handler als Innere Klasse von Thread macht kommst du an die Kassen Variablen auch aus dem Handler ran.
    Ein Feedback auf Tipps ist auch schön. :P