Java Android - Handler Message Queue bei jeder neuen Nachricht ausgeben

palaber

Captain
Registriert
Juni 2006
Beiträge
3.856
Hey ho,

folgendes Ding. Ich experementiere momentan mit Bluetooth und Android und PC :freak:
Momentan bekomme ich es hin, das ich von nem PC Nachrichten an mein Handy schicke. Das Problem aber ist, dass diese Nachrichten erst nach dem beenden des Servers (verbindungsabbau) dargestellt werden. In der LogCat wird mir gezeigt, dass wenn mein Thread der auf Nachrichten lauscht und diese bekommt dann auch diese weiter an meinen Handler leitet. Im Handler wird aber die Nachricht(en) erst nach beenden des Servers ausgegeben. Wie kann ich denn jede ankommende Nachricht im Handler sofort ausgeben?

Hier mal mein Thread Code, der auf Nachrichten lauscht:
Code:
 public void run() {
	            Log.i(TAG, "BEGIN ConnectedThread");
		        byte[] buffer;  // buffer store for the stream
		        int bytes; // bytes returned from read()
		        Log.d(TAG, mmSocket.toString() + "Run in ConnectedThread");
		        // Keep listening to the InputStream until an exception occurs
		       
		        while (true) {
		            try {
		                // Read from the InputStream
		            	buffer  = new byte[1024];
		                bytes = mmInStream.read(buffer);
		                // Send the obtained bytes to the UI activity
		                String sOut = new String(buffer);
		                
		                Log.d(TAG, "ConnectedThread - while schleife - Nachricht geschickt an Handler");
		                mHandler.obtainMessage(1, bytes, -1, buffer).sendToTarget();  //Nachrichten werden erst angezeigt wenn Verbindung mit Server abgebaut wurde!
    
		                
		            } catch (IOException e) {
		                break;
		            }
		        }


Und hier mein Handler Code:
Code:
public void handleMessage(Message msg) {
	
			super.handleMessage(msg);
						
			switch (msg.what){
			
			case 0:
				............ //Code für nen anderen Thread - zur besseren Übersicht raus genommen
				
				break;
				
			case 1:
				byte[] readBuffer = (byte[])msg.obj;	//Empfangene Daten (Byte[]) aus connectedThread werden in neuen byte[] geschrieben
				String sOut = new String(readBuffer);	//Byte[] umwandeln in String
				Log.d(TAG, "Empfangene Daten: " + sOut);
				Toast.makeText(getApplicationContext(), sOut, Toast.LENGTH_LONG).show();	//Ausgabe der Msg.
				break;
			
			default:
				Log.d(TAG, "Bla Bla");
			
			}
		}
 
Kannst du den Server-Code hier noch posten? Erste Vermutung: Fehlendes flush() oder auto-flush.
 
Der Server ist momentan eine Windows Forms Application in C#.
Da wird per Button Click einfach ein Textfeld ausgelsen und diese Informationen in den Stream gespeißt.

Hier der Code zum Daten versenden / empfangen

Code:
public void ServerConnectThread()        
        {
            serverStarted = true;       //Bool zum verhindern des mehrfachstarts dieses Threads
            BluetoothListener btListener = new BluetoothListener(mUUID);

            btListener.Start();
            updateUI("Warte auf Verbindung...");

            BluetoothClient connect = new BluetoothClient();
            connect = btListener.AcceptBluetoothClient();
            updateUI("Client verbunden");
              
            Stream mStream = connect.GetStream();

            while (true)            
            {                
                byte[] received = new byte[1024];
                Array.Clear(received, 0, received.Length);

                //ließt aus Bytestream
                mStream.ReadTimeout = 1000;
                try                
                {
                    mStream.Read(received, 0, received.Length);                    
                    updateUI("\r\nEmpfangen: " + Encoding.ASCII.GetString(received));                     
                }                
                catch (IOException e) { } 
                    
                //Schreibt Textbox in Bytestream
                if (ready==true)                    
                {                                              
                    mStream.Write(message, 0, message.Length); 
                    updateUI(Encoding.ASCII.GetString(message));   
                    ready = false;                     
                }  
            }        
        }

Reicht dir das so? flush() hab ich wirklich nicht drin. Aber auch noch k.p. was das is :eek:
 
Bin mir gerade nicht sicher, dass es diese Methode gibt und obs bei C# nötig ist, aber probier mal, nach

Code:
mStream.Write(message, 0, message.Length);

folgende Zeile einzufügen:

Code:
mStream.Flush();

Die Flush() Methode bewirkt bei Streams, dass Daten, die auf den Stream geschrieben wurden, dann auch tatsächlich gesendet werden. Wenn der Server beendet und damit auch der Stream geschlossen wird, wird das automatisch aufgerufen. Daher hast du die Nachrichten dann erst am Client empfangen (wäre jetzt meine Vermutung).
 
Hab das gerade auch selbst mal getestet. Bringt leider nichts.
Erst wenn ich den Server beendet wird mir der Toast und der Log angezeigt.

Edit:
Ich denk, das Problem liegt im Handler, dieser gibt erst die Nachrichten aus wenn keine mehr nachkommen können. Aber eigentlich will ich ja jede Nachricht sofort ausgeben^^
Auf Android Developers hab ich folgendes gefunden:
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
Aber so wirklich weiter gebracht hat mich das nicht. In meinem Fall müsste ja (1) und (2) eintreten.
Also ich sage über den Handler starte den ConnectedThread und dann will ich ja messages empfangen denen ein Objekt angehängt ist (der Buffer des Streams) und dieser soll ausgegeben werden. Aber das "at some point in the future" is doch völlig arrgghhh. Ich müsste doch irgendwie "manuell" angeben können wann dieser Punkt erreicht ist?

Edit 2:
So, weitergelesen und dann dies gefunden:
The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.
Das trifft wohl bei mir zu. Wenn passend - aber das kann doch nicht sein, das man nicht selbst bestimmen kann wann eine Message ausgegeben wird, oder?!
 
Zuletzt bearbeitet:
(1): damit ist postDelayed oder postAtTime gemeint
(2): wenn nicht anders definiert, läuft der Handler auf dem UI-Thread (was er in deinem Fall auch muss, da er sonst keine Toasts ausgeben dürfte). Jetzt musst du noch dafür sorgen, dass das Lesen des InputSteams wirklich auf einem anderen Thread läuft. Ansonsten kommt dein Handler nur dann dazu die eigene MessageQueue abzuarbeiten, wenn eben nicht mehr gelesen wird(Server beendet).

Was zum Lesen:
https://developer.android.com/training/multiple-threads/index.html
 
Ich hab das lesen des Streams in einem anderen Thread! Hier mal der komplette Code des Threads (hab ich so von Android übernommen mit ein paar kleinen Änderungen):

Code:
private class ConnectedThread extends Thread {
		    private final BluetoothSocket mmSocket;
		    private final InputStream mmInStream;
		    private final OutputStream mmOutStream;
		 
		    public ConnectedThread(BluetoothSocket socket) {
		        mmSocket = socket;
		        InputStream tmpIn = null;
		        OutputStream tmpOut = null;
		 
		        // Get the input and output streams, using temp objects because
		        // member streams are final
		        try {
		            tmpIn = socket.getInputStream();
		            tmpOut = socket.getOutputStream();
		            Log.d(TAG, "Sockets für I/O Stream");
		        } 
		        
		        catch (IOException e) {
		        	Log.e(TAG, "temp sockets not created", e);
		        }
		 
		        mmInStream = tmpIn;
	            mmOutStream = tmpOut;
		        
		        Log.d(TAG, "Connected Thread Socket: " + mmSocket.toString());
		    }
		    //public void start(){
		    public void run() {
	            Log.i(TAG, "BEGIN ConnectedThread");
		        byte[] buffer;  // buffer store for the stream
		        int bytes; // bytes returned from read()
		        Log.d(TAG, mmSocket.toString() + "Run in ConnectedThread");
		        // Keep listening to the InputStream until an exception occurs
		       
		        while (true) {
		            try {
		                // Read from the InputStream
		            	buffer  = new byte[1024];
		                bytes = mmInStream.read(buffer);
		                // Send the obtained bytes to the UI activity
		                String sOut = new String(buffer);	// zu Log zwecken angelegt
		                
		                Log.d(TAG, "ConnectedThread - while schleife - Nachricht geschickt an Handler");
		                mHandler.obtainMessage(1, bytes, -1, buffer).sendToTarget();  //Nachrichten werden erst angezeigt wenn Verbindung mit Server abgebaut wurde!

		                
		            } catch (IOException e) {
		            	Log.d(TAG, "ConnectedThread - while schleife - catch");
		                break;
		            }
		        }
		    }

Damit sollte doch das lesen in einem extra Thread laufen?!
Und den Thread ruf ich im Handler auf (case 0):
Code:
Handler mHandler = new Handler(){
		@Override
		public void handleMessage(Message msg) {
	
			super.handleMessage(msg);
									
			switch (msg.what){
			
			// Wenn verbunden, dann String an Server schicken, dass Verbindung steht und den ConnectedThread Run() aufrufen (Schleife zum Daten empfangen)
			case 0:
				ConnectedThread connectedThread = new ConnectedThread((BluetoothSocket)msg.obj);		//startet connectedThread und übergibt den Socket
				Log.d(TAG, "Geräte verbunden");
				Toast.makeText(getApplicationContext(), "Geräte verbunden", Toast.LENGTH_LONG).show();	//Info auf Smartphone
				
				String s = "Mit Android Phone verbunden";
				
				connectedThread.write(s.getBytes());		//kickt nachricht an Server
				connectedThread.run();		//blockiert evtl. dann doch start()?!?
				
				break;
				
			case 1 ....
			
			}
		}
	};

Edit:
Hab den Fehler gefunden! Ich hab die Run() Methode des Threads aufgerufen, anstatt den Thread mit start() auszuführen :freaky:
 
Zuletzt bearbeitet:
Zurück
Oben