Threads absichern

S

Skolleus

Neues Mitglied
0
Hi,
ich bin recht neu in der Android-Programmierung (wie auch hier im Forum) und stehe vor einem kleinen Problem was Threads angeht.
Ich habe einen Thread, der eigentlich die ganze Zeit Daten (von Socket) lesen soll. Diese werden von ihm ausgewertet und je nach Inhalt wird etwas vom UI-Thread ausgegeben.
Soll heißen: Es kommen mehrere (sehr viele pro Sekunde) Nachrichten in einem bestimmten Format an, werden ausgewertet und bei Empfang einer oder mehrerer bestimmten Nachrichten (alle paar ms) wird deren enthaltener Wert im Display angezeigt. der Rest wird ignoriert.

Das Problem dabei: der Lese-Thread arbeitet und lässt nicht zu, dass das Display aktualisiert wird (klar, es kann immer nur ein Thread arbeiten).
Von Java aus kenne ich das Problem bei mehreren Threads und hätte es z.B mit Semaphoren lösen können. Dummerweise gibt es in Android 2 Einschränkungen.
1. Nur der UI-Thread kann auf das Display schreibend zugreifen.
2. Der UI -Thread darf nicht geblockt werden.
Ich kann ihn also nicht gezielt anhalten, den Lese-Thread ausführen, bei Bedarf diesen wieder anhalten und den UI-Thread etwas anzeigen zu lassen.

Mein 2. Versuch war AsyncTask.
Da wird allerdings das Display nur alle paar Sekunden aktualisiert. Es sollte aber innerhalb von ms passieren.
Hat jemand eine Idee was ich tun kann, um dem Lese-Thread die CPU Zeit zusichern zu können und trotzdem alle paar ms (möglichst zeitnah nach Erhalt der entsprechenden Nachricht) das Display aktualisiert zu kriegen?
 
Wenn du auf die Widgets zugreifen willst, geht es nur von dem UI-Thread
( Also - async task ore runOnUiThread() ) - allerdings hast du da kein Einfluss auf
Laufzeitverhalten ( es wird irgendwann ausgeführt, wenn Update mal fällig ist )

Wenn du Zeitnahe Updates willst, must due mit der Surface Arbeiten und selber drauf zeichnen.

PS: 2-3 ms sind eindeutig zu schnell. Kein Benutzer wird deinen EInsatz würdigen können. 20 ms
reichen vollkommen aus
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: Skolleus
Ich hab keine Widgets sondern zeichne selbst (ist ein Bitmap und ein paar Standardformen). Wie man das kombiniert wäre eine Frage für die nächsten Wochen *g.
Die Zeitangabe bezieht sich auf die einkommenden Nachrichten. Nicht auf die Anzeige Darauf habe ich auch keinen Einfluss. Momentan liegt die dadurch entstehende Updaterate allerdings bei >5 Sekunden. Absolut unbrauchbar.
20+ Updates pro Sekunde müssten es schon sein. Also 50ms oder besser.
 
Dann ist die SurfaceView deine Wahl - schau einfach den LunarLander Sample an
 
OK werd ich machen. Danke schonmal.
 
Also nachdem ich heute mal dazu kam mir das anzuschauen, weiß ich nicht wie mir das bei meinem Problem helfen soll.
Das Ding ruft auch nur invalidate auf und wartet dann darauf, dass irgendwann mal das Display aktualisiert wird.

Es muss doch irgendwie möglich sein kontinuierlich Daten von einem Socket zu lesen, zu verarbeiten und das Ergebnis flüssig (also mindestens 20Bilder pro Sekunde) auf dem Bildschirm darzustellen.

Ich habe eine Klasse die einen Socket öffnet und Daten (es kommt alle 50ms ein Set mit vielen Nachrichten) liest: das passiert in einem eigenen Thread
Die gibt das Ganze an eine andere Klasse (Parser) die die Daten bearbeitet und alles wichtige in ein Datenobjekt schreibt: das passiert im UI Thread (momentan noch über runOnUiThread)
Jetzt soll anhand dieser Daten etwas flüssig auf dem Bildschirm ausgegeben werden.

Als Beispiel kann man sich ein analoges Messgerät vorstellen, bei dem die Werte über eine Socketverbindung reinkommen.
 
Zuletzt bearbeitet:
hm, mein Socket z.B. verarbeitet auch Daten schnell, den "Fortschritt"
gebe ich über einen EventHandler aus dem WorkerThread an die UI weiter
und bis jetzt konnte ich in meinem Anwendungsfall keine merklichen
Verzögerungen feststellen. Der "Fortschritt" wird aber auch nur in einer
Textbox ausgegeben, also nix mit Grafik zeichen - da habe ich keine Erfahrungen mit.

Bekommst Du denn die 20 Bilder pro Sekunde animiert *ohne* Abhängigkeit
von deinem WorkerThread ?
 
Ich hab jetzt 2 verschiedene Modis.
Es wird alle 50ms ein Paket mit sehr vielen Nachrichten gesendet und zeilenweise ausgelesen.
1. Jede Nachricht kommt in eigener Zeile.
2. Das komplette Paket besteht aus einer einzigen Zeile.

zu 1.
Wenn ich einen TimerTask mit 1ms Delay benutze, ist die Bewegung zwar flüssig, aber ich kann nur 50 Nachrichten pro Paket abarbeiten.
Wenn ich kein delay eintrage, wird die Menge der Nachrichten zwar nur von der Prozessgeschwindigkeit begrenzt, aber das Display wird nur alle 5+ Sekunden aktualisiert.

zu 2. Das muss ich am Montag noch erarbeiten.. noch nicht getestet.
 
folgendes sollst du von deiner Update-Thread machen:

Code:
    /**
     * draw back buffer on the screen.
     */
    public void updateFrontBuffer() {
        //  draw on screen out back buffer
        Canvas c = null;
        try {
            // TODO: investigate whether to guard here,  or prevent this globally
            // as race confition
            c = holder.lockCanvas(null);
            // synchronized (mSurfaceHolder) {
            c.drawBitmap(fieldBitmap, 0, 0, backgroundPaint);
            // }
        } finally {
            // do this in a finally so that if an exception is thrown
            // during the above, we don't leave the Surface in an
            // inconsistent state
            if (c != null) {
                holder.unlockCanvasAndPost(c);
            }
        }// end finally block
    }
Das updated das Bild in deiner SurfaceView sofort (dein Front-Buffer ) Und deine Bilder/Texte/Linien/was auch immer zeichnest du in einen BItmap ( BackBuffer)

Es ist aktuelles code von meinen Spiel:
https://market.android.com/details?id=de.pribluda.games.android.colors&feature=search_result
 
Irgendwie funktioniert das nicht. Das Lunarlander Beispiel hilft mir nicht weil schon der grundsätzliche Aufbau des Programms ein völlig anderer ist und ich auch nicht zu großen Einfluss darauf nehmen kann.

z.B. gibt es hier kein XML File, es gibt nur einen UI-Thread und einen NetworkThread....
Hier mal eine grobe Strukturübersicht:

Code:
public class Main extends Activity{
         //Variablendelerationen...
         final runnable mUpdateresults = new Runnable(){
               public void run(){
                     //Parseraufruf speichert in Datenobjekt
                     actionOnData();  
               }
         }
         public void onCreate (Bundle savedInstanceState){
              NetworkReader reader = new NetworkReader(this, IP, PORT);
              super.onCreate(savedInstanceState);
              setContentView(mGraphicsView);
              reader.reading();
         }

        public void actionOnData(){}//Berechnungen mit dem Datenobjekt
}
Code:
public class NetworkReader{
      public void reading(){
            public void run(){
                  //in=bufferedreader...
                  while ((str = in.readLine()) !=null){
                        ...
                        Main.runOnUiThread(Main.mUpdateresults);
                  }
                  ...
            }
            ...
      }
}
Code:
public class GraphicsView extends View(){
      //Variablendeklarationen
      public GraphicsView(Context context){
            super(context);
      }
      
      protected onDraw(Canvas canvas){
             //Variablendeklarationen
             //berechnungen mit Datenobjekt
             canvas.drawBitmap();//Hintergrundbild
             canvas.drawPath();//Standardformen
      }
}
Dazu noch eine Klasse Parser und ein Datenobjekt zur Speicherung und weiterverarbeitung.

Wenn ich jetzt statt on View von SurfaceView erben lasse, muss schon der Aufruf in der Main ein ganz anderer sein, es kommt ein weiterer Thread dazu, es muss das SurfaceHolder Interface implementiert werden.. inklusive 3 neuer Methoden. Ich müsste das halbe Programm umschreiben.
 
Zuletzt bearbeitet:
Vergiss runOnUiThread - es ist nicht realtime. Stattdessen machst du folgendes:
- definierst einen Surface-View wo du zeichnen willst
- übergibst diesen an deinen NetworkReader
- zeichnest das was du willst so wie im Beispiel
 
  • Danke
Reaktionen: Skolleus
Jetzt sieht es so aus:

Code:
public class Main extends Activity{
        //Variablendelerationen...
        public void onCreate (Bundle savedInstanceState){             
             super.onCreate(savedInstanceState);
             mGraphicsView = new GraphicsView(this);
             setContentView(mGraphicsView);
             NetworkReader reader = new NetworkReader(IP, PORT, mGraphicsView);
             reader.reading();
        }


}
Code:
public class GraphicsView extends SurfaceView implements SurfaceHolder.Callback{
        //Variablendeklarationen
        public GraphicsView(Context context){
            super(context);
        }
      
        protected onDraw(Canvas canvas){
            //Variablendeklarationen
            Canvas c=null;
            try {
                 c=mHolder.lockCanvas(null);
                 c.drawBitmap();//Hintergrundbild
                 c.drawPath();//Standardformen
            }finally 
                if(c!=null) mHolder.unlockCanvasAndPost(c);
       }

       public void surfaceCreated(SurfaceHolder holder){
            mHolder=holder;
       }    

       public void surfaceChanged(SurfaceHolder holder, int format, int width);
       public void surfaceDestroyed(SurfaceHolder holder);
}
Code:
public class NetworkReader{
       public void reading(){
            public void run(){
                  //in=bufferedreader...
                  while ((str = in.readLine()) !=null){
                        if (mParser.parse(str)){
                             actionOnData();
                             mGraphicsView.doDraw();
                        }
                  }
                  ...
            }
            ...
       }

       public void actionOnData() //Berechnungen mit Daten aus dem Datenobjekt        
}
Kein Bild. Stattdessen eine NullpointerException beim Versuch das Bitmap zu zeichnen und eine Meldung:
Layout repeat skipped after too many iterations
 
Zuletzt bearbeitet:
1. Check deinen Layout ( im XML ? )
2. Ich würde nicht von dem SurfaceView ableiten, sondern diese einfach
benutzen ( dein Netzwerk-Code hat dann weniger Abhängigkeiten zum android Zeugs )
3. Ich würde wirklich empfehlen den LunarLander noch mal durchzugehen.
 
1. Check deinen Layout ( im XML ? )
Wie gesagt.. es gibt kein XML File.
Ich würde nicht von dem SurfaceView ableiten, sondern diese einfach
benutzen
Wo meinst du?
3. Ich würde wirklich empfehlen den LunarLander noch mal durchzugehen
Das Beispiel hat mit meinem Problem einfach nichts zu tun.
LunarLander hat einen Thread (neben dem Mainthread) in dem alles gemacht wird. Eingaben, Ausgaben, Berechnungen. Ich habe zusätzlich einen Netzwerkthread für die Eingabe. Und genau DA ist das Problem weil ich die Eingabe nicht mit der Ausgabe in Einklang bringen kann.
 
Poste mal den Stacktrace. LunarLAnder demonstriert, wie man einen SurfaceView in Echtzeit von einen separaten (nich UI) Thread updated - genau das willst du auch haben

SurfaceView muss du von deiner Netzwerk-Thread aus benutzen
 
Ich glaub ich habs jetzt. Musste es doch komplett umschreiben.
Nur noch ein paar kleine Probleme..
Was ich zeichne wird beim nächsten Durchlauf nicht gelöscht. Gibts da eine "schöne" Methode um gezeichnete Linien beim nächsten Durchlauf nicht mehr zu zeigen?
Außerdem funktioniert plötzlich der Orientierungswechsel (Landscape, Portrait) nicht mehr. Daran hab ich gar nichts geändert.
 
Zuletzt bearbeitet:
OK das Problem mit dem Orientation-wechsel ist auch vom Tisch.. hatte vergessen das Manifest wieder anzupassen.

Danke für deine Hilfe und Geduld mit mir :)

Nur noch die Sache mit dem neu Zeichnen bei jedem Durchgang. Ich hab schon versucht gleich am Anfang der onDraw() Methode ein
Code:
canvas.drawColor(Color.Black);
oder ein
Code:
paint.setColor(Color.Black);
canvas drawRect(new Rect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
zu setzen. Leider hat das nichts gebracht. Ich fänds auch sehr unschön..
Die gezeichneten Linien werden beim nächsten Durchlauf nicht entfernt.

edit: Das ist ja ein neues Problem.. also neuer ForenThread..
 
Zuletzt bearbeitet:

Ähnliche Themen

W
  • waltsoft
Antworten
3
Aufrufe
722
waltsoft
W
W
  • waltsoft
Antworten
4
Aufrufe
936
waltsoft
W
znieh99
  • znieh99
Antworten
1
Aufrufe
1.017
znieh99
znieh99
Zurück
Oben Unten