UDP DatagrammSocket.receive() im AsyncTask

  • 3 Antworten
  • Letztes Antwortdatum
F

Flocke123

Ambitioniertes Mitglied
4
Hallo,

stehe vor einem weiteren Problem und ich reis mir bald sämtliche Haare raus.
Ein Service startet einen AsyncTask, der in seiner doInBackground() Methode auf ein UDP Paket lauscht.
Wer die Eigenschaften der socket.receive() Methode kennt, sollte nun schon wissen, welche Problematik es mit sich zieht.
Diese Methode verhindert die Weiterverarbeitung des Codes.

Teil meines Services
Code:
@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		
		if (intent.getAction().equals("action.START")) {
			Toast.makeText(getApplicationContext(), "OnStartCommand Equals Start", Toast.LENGTH_LONG).show();
			release = true;
			MyUdpListener listener = new MyUdpListener();
			listener.execute();
			
		} else if (intent.getAction().equals("action.STOP")) {
			Toast.makeText(getApplicationContext(), "OnStartCommand Equals Stop", Toast.LENGTH_LONG).show();
			release = false;
			
		}
		
		return super.onStartCommand(intent, flags, startId);
	}

Ziemlich simple. Drücke ich auf einen Button "Start", wird ein Neuer AsyncTask instanziert und das boolean "release" gesetzt. Drücke ich hingegen auf Stop, wird das boolean zurückgesetzt und soll somit den Task beendet. (was es auch tut, aber erst sobald ein weiteres Paket ankommt.

doInBackground
Code:
@Override
		protected String doInBackground(String... arg0) {
			try {
				if (socket == null) {
					socket = new DatagramSocket(null);
					SocketAddress socketAddr = new InetSocketAddress(2050);
					socket.setReuseAddress(true);
					socket.setBroadcast(true);
					socket.bind(socketAddr);		
				}										
			} catch (SocketException e) {
				e.printStackTrace();
			}
			
			byte[] buf = new byte[1024];
			DatagramPacket packet = new DatagramPacket(buf, buf.length);
			
			while(release) {
				
				try {
					Log.i("MyUdpListener", "listening...");
					socket.receive(packet);
					if (release == false) {
						break;
					}
					String message = new String(packet.getData(), 0, packet.getLength());
					publishProgress(message);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return null;
		}

Das "if release == false dann break" ist ein Verzweiflungsakt, da ich ja weis, das diese Zeile nie bearbeitet wird, bis ein neues Paket eintrifft.

Ich gehe davon aus, dass schon viele Leute vor einer ähnlichen Problematik standen, und mir hierzu vllt einen Tipp geben können. Würde mich freuen.

Gruß Flo
 
Eine Möglichkeit ist, ein Timeout zu setzen. Dann blockiert die Methode maximal so lange. Da du aber Buttons zum starten und stoppen hast, denke ich, dass du keinen Timeout haben willst.

Die andere Möglichkeit ist, eifach das Socket zu schliessen, wenn jemand auf Stop klickt.

AsyncTask
Code:
public class MyAsyncTask extends AsyncTask<...> {

  public void closeSocket() {
    //vielleicht erst noch isClosed() prüfen, falls nötig
    socket.close();
  }

  //...
}

Service
Code:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
  MyUdpListener listener = new MyUdpListener();
  listener.execute();
  //...
  listener.closeSocket();
}

accept() schmeisst dann eine Exception (SocketException?), die musst du dann halt noch behandeln.

Je nachdem, was dein Service genau bezwecken soll, könntest du vielleicht statt einem normalen Service einen IntentService verwenden. Der startet beim Aufruf automatisch einen Thread.
 
Danke Zoopa,
werd ich heute mal ein wenig durchspielen.

Der Service startet automatisch bei Device Boot.
Auf dem HomeScreen habe ich ein Widget mit zwei Button, die die Bearbeitung des AsyncTasks Starten bzw Stoppen (Später sollen daraus nur noch 1 Button werden).

Code:
public int onStartCommand(Intent intent, int flags, int startId) {
		
		if (intent.getAction().equals("action.START")) {
			Toast.makeText(getApplicationContext(), "OnStartCommand Equals Start", Toast.LENGTH_LONG).show();
			release = true;
			MyUdpListener listener = new MyUdpListener();
			listener.execute();
			
		} else if (intent.getAction().equals("action.STOP")) {
			Toast.makeText(getApplicationContext(), "OnStartCommand Equals Stop", Toast.LENGTH_LONG).show();
			release = false;
			
		}
		
		return super.onStartCommand(intent, flags, startId);
	}

das Problem ist ja, das ich bei action.STOP keinen Zugriff auf die zuvor in action.START erstellte Async-Instanz habe (as far as I know).
Oder gibt es eine Möglichkeit, die aktuelle Instanz des ATs zu erreichen?

Gruß Flo
 
Ich finde, wenn man mit "Server-Side"-Sockets (okay hab bisher nur mit TCP gearbeitet) arbeitet, dann verlässt man den Bereich, für den ASyncTask gedacht ist. Da nehme ich dann lieber echte Threads - und man kann die Kommunikation via volatile Variablen und/oder Locks so machen, wie man es in Desktop Java auch tut.
 
Zurück
Oben Unten