Surface View - RenderThread Problem

Railwanderer

Railwanderer

Ambitioniertes Mitglied
11
Hi,
ich habe ein Beispiel aus dem Buch "Beginning Android for Games Development" übernommen,
und habe das Problem das sich mein Handy bei dem Test aufhängt.

Der Code ist hier aus Seite 190 :http://it-ebooks.info/book/506/
Ich teste auf einem SG S3 mit Android Vers. 4.3, der Code ist unten und aus dem Buch kopiert:

Code:
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;

public class SurfaceViewTest extends Activity {
FastRenderView renderView;

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);

renderView = new FastRenderView(this);
setContentView(renderView);
}

protected void onResume() {
super.onResume();
renderView.resume();
}

protected void onPause() {
super.onPause();
renderView.pause();
}

class FastRenderView extends SurfaceView implements Runnable {
Thread renderThread = null;
SurfaceHolder holder;
volatile boolean running = false;

public FastRenderView(Context context) {
super(context);
holder = getHolder();
}

public void resume() {
running = true;
renderThread = new Thread(this);
renderThread.start();
}

public void run() {
while(running) {
if(!holder.getSurface().isValid())
continue;

Canvas canvas = holder.lockCanvas();
canvas.drawRGB(255, 0, 0);
holder.unlockCanvasAndPost(canvas);

}
}

public void pause() {
running = false;
while(true) {
try {
renderThread.join();
}
catch (InterruptedException e) {
// retry
}
}
}
}
}
 
was heißt den "aufhängt". Stürzt die App ab oder hängt das ganze Gerät? Gibt es eine Fehlermeldung? Was steht im Logcat?
 
hallo
1. die methode public void run()
sollte eine @Override dadrüber spendiert bekommen von dir

2. in der pause methode solltest du im try catch block nachdem du erfolgreich den thread gejoint hast und dieser tot ist einen break einfügen damit keine probleme auftauchen

3. empfehle ich dir deine operationen wo du zeichnest zu erstens zu synchronisieren und zweitens in einem try catch block zu legen so ungefähr:

PHP:
                try
		{
			canvas = sHolder.lockCanvas();
			synchronized(sHolder)
			{
				//drawing operations here
			}
		}
		catch(Throwable t)
		{
			canvas=null;
		}
		finally
		{
			if(canvas!= null)
			{
				sHolder.unlockCanvasAndPost(canvas);
			}
		}


4. warum empfehlt dir das Buch nciht das InterFace SurfaceHolder.Callback zu implementieren?komisch!!!!

Der ursprüngliche Beitrag von 22:08 Uhr wurde um 22:19 Uhr ergänzt:

Also ich mache das immer so mit Surfaceviews:


Die Activity bekommt mit folgenden Content:
PHP:
setContentView(new MainView(this));

Die MainView klasse sieht dann so aus:

PHP:
public class MainView extends SurfaceView implements SurfaceHolder.Callback
{	
	
	
	//__________________________________________//
	MainThread mThread;
	SurfaceHolder sHolder;
	Canvas canvas;
	Paint paint=new Paint();
	
	public boolean surfaceReady,startedForFirstTime=true;

	
	/*
	 * Constructor
	 */
	public MainView(Context context) {
		super(context);
		sHolder = getHolder();
		sHolder.addCallback(this);
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) 
	{}

	@Override
	public void surfaceCreated(SurfaceHolder sHolder) {
		surfaceReady=true;
		if(startedForFirstTime)
		{
			mThread=new MainThread(this);
			scaleBitmaps();
			startMainThread();
			startedForFirstTime=false;
		}
		mThread.setPause(false);
		
		
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		surfaceReady=false;
		mThread.setPause(true);
	}
	
	
	@Override
	public boolean onTouchEvent(MotionEvent e) 
	{
		super.onTouchEvent(e);
		float X=e.getX();
		float Y=e.getY();
		  switch(e.getAction())
		  {
		  	case MotionEvent.ACTION_DOWN:
		  		//was tun bei touch down?
		  		break;
		  		
		  	case MotionEvent.ACTION_MOVE:
                                //was tun bei bewegung?
		  		break;
		  	
		  	case MotionEvent.ACTION_UP:
		  		//was tun bei touch up?
		  		performClick();
		  		break;
		  }
		   
		  return true;
	}
	@Override
	 public boolean performClick() {
	        super.performClick();
	        return true;
	 }
	/*
	 * Method for the drawing operations.
	 */
	public void doDraw()
	{
		try
		{
			canvas = sHolder.lockCanvas();
			synchronized(sHolder)
			{
				//drawing operations here
			}
		}
		catch(Throwable t)
		{
			canvas=null;
		}
		finally
		{
			if(canvas!= null)
			{
				sHolder.unlockCanvasAndPost(canvas);
			}
		}
	}
	
	
	
	
	
	
	public int startMainThread()
	{
		if(surfaceReady)
		{
			mThread.setRunning(true);
			try
			{
				mThread.start();
				return 0;
			}
			catch(Throwable t)
			{
				return -1;
			}
		}
		else return -1;
		
	}
	
	
	public void killMainThread()
	{
		mThread.setRunning(false);
		mThread.setPause(true);

		boolean retry = true;
		while(retry)
		{
			try
			{
				mThread.join();
				retry = false;
			}
			catch(Exception e)
			{}
		}
		
	}
	
	

}


und der Main Thread sieht folgendermaßen aus:
PHP:
public class MainThread extends Thread{
	
	MainView mView;
        boolean isRunning,isPaused;
	
	public MainThread(MainView mView)
	{
		this.mView=mView;
		
	}
	
	public void setRunning(boolean run)
	{
		this.isRunning=run;
	}
	
	public void setPause(boolean pause)
	{
		this.isPaused=pause;
	}
	
	@Override
	public void run()
	{
		while(isRunning)
		{
			while(!isPaused)
			{
				
				if(mView.surfaceReady)
					mView.doDraw();

                                //kurze pause
			        try
			        {
				         sleep(40);
			        }
			        catch (InterruptedException e)
			        {
				        return;
			        }
			}
			
		}
	}
	
Frag mcih ruhig falls du etwas über Surfaceviews wissen willst ich hab mich die letzten 2 Monaten nur mit denen beschäftigt weil ich spiele damit programmiere
}




mit meinem ansatz hast du sogar die möglichkeit den thread zu pausieren wenn sich eine andere activity in den vordergrund schiebt oder der homebutton gedrückt wurde und ihn weiter laufen zu lassen falls du wieder zurücknavigierst in die app
benutz also lieber den surfaceholder.callback um mehr kontrolle zu erlangen
und ich hab dir schon die methode reingeschrieben womit du auf touch ereignisse vom user reagieren kannst(siehe onTouch methode) natürlich lege ich dir auch den gesturedetector ans herz falls du noch mehr sachen abarbeiten wilslt vom user obwohl man auch alles in ontouch implementieren kann


Aber ansonsten musst du nur punkt 1 und 2 beachten dann sollte es soweit laufen


lg jaiel
 
Zuletzt bearbeitet:
Vielen Dank für die schnelle und ausführliche Antwort!!
Ich gehe es mal direkt alles durch und melde mich danach gleich zurück! :)

@kosmus
mit aufhängen meine ich das display wird zwar rot gefüllt,
allerdings reagiert die app danach auf nichts mehr also weder button back noch
auf taskmanager aufrufen etc. Erst durch lockscreen an / aus und dann taskmanager
lässt sich die app killen.

Der ursprüngliche Beitrag von 22:48 Uhr wurde um 23:15 Uhr ergänzt:

Vielen Dank für die schnelle und ausführliche Antwort!!
Ich gehe es mal direkt alles durch und melde mich danach gleich zurück! :)

@kosmus
mit aufhängen meine ich das display wird zwar rot gefüllt,
allerdings reagiert die app danach auf nichts mehr also weder button back noch
auf taskmanager aufrufen etc. Erst durch lockscreen an / aus und dann taskmanager
lässt sich die app killen.

Edit:
Okay also zu
1) das kann ich noch nachvollziehen, habe ich vor Android unter Java auch so gemacht,
und ist mir hier einfach nicht aufgefallen ;)

2)Das Buch sagt hier:
The FastRenderView.pause() method looks a little strange. First we set the running flag to false.
If you look up a little, you will see that the while loop in the FastRenderView.run() method will eventually terminate due to this,
and hence stop the rendering thread.

In the next couple of lines, we simply wait for the thread to die completely, by invoking Thread.join().
This method will wait for the thread to die, but might throw an InterruptedException before the thread actually dies.
Since we have to make absolutely sure that the thread is dead before we return from that method,
we perform the join in an endless loop until it is successful.

Das sagt doch folgendes:
-running wird auf false gesetzt, dadurch wird die running schleife beendet, und danach verlässt der Thread die Methode und
wird quasi dadurch gekillt. Das allerdings kann dauern wodurch die FastRenderView.pause() Methode die dauerhaft versucht
einen Thread zu joinen evtl daran gehindert wird dies zu tun da der thread noch am Beenden ist.

Hierzu erstmal zwei fragen:
-Ist der Thread danach nicht gelöscht? Wieso versucht man diesem zu joinen? Laufen die join versuche im
Hintergrund, also gerade dann wenn die app pausiert ist und verbraucht damit Speicher?

-in der onResume() Methode wird doch ein neuer thread erstellt und gestartet. Wenn der alte aber noch aufgegriffen (join?)
wurde dann würde ja jedes mal noch dazu ein neuer thread erstellt werden oder? Ist das vll der Grund für das Aufhängen? Bzw
wo steckt der Aufhäng-Fehler überhaupt, er tritt direkt auf wenn ich die Activity öffne/starte.

Dein Lösungsvorschlag mit dem break sähe dann so aus und würde verhindern das wenn dem thread gejoint wurde er noch weiter sucht?
Code:
public void pause() {

            running = false;
            while (true) {
                try {
                    renderThread.join();
                    break;
                } catch (InterruptedException e) {
                    // retry
                }
            }
        }

3)wie funktioniert der synchonized thread?
Ich schau mir hierzu gleich nochmal an was die Java Insel sagt.
Ich bin leider erst seid 4-5 Monaten am Java lernen und seid einem Monat auf Android dabei ;)
Aber ich versuche auch mit Hilfe von dem Buch und der Insel langsam voran zu kommen und möchte im Idealfall
im September eine Ausbildung zum Fachinformatiker im Anwendungsbereich anfangen :)

Vielen Dank soweit schonmal!
 
Zuletzt bearbeitet:
also wenn du ein thread jouinst geht das programm in den thread und führt dort die anweisungen aus bis der thread zu ende geht.
es geht erst weiter im ablauf wenn der thread tot ist.
also alles was anch thread.join(); kommt wird nur ausgeführt wenn der Thread sich beendet hat solange wird der ablauf eben geblockt
deswegen macht es sinn danach ein break einzufügen sonst versucht er einen toten thread unendlich oft zu joinen!!!da ist dem author wohl ein maleur unterlaufen



also es ist nciht so wie mit start(); dass das programm weiterläuft falls du das gearde verwechselt hast

Der ursprüngliche Beitrag von 23:29 Uhr wurde um 23:32 Uhr ergänzt:

zu 3. du synchrinsiert das damit du ein lock darauf hast und es nciht von irgendwo anders manipuliert werden kann solange du damit arbeitest
 
okay :) also da muss ich echt nochmal tiefer ins Thread-Kapitel schauen :D

Aber gerade bin ich verwirrt also der Ablauf ist doch wie folgt:
-App wird created - onResume erstellt einen Thread und startet ihn.

-Im run Teil des Threads wird gezeichnet.

- Sobald der activity lifecycle sich ändert -> onPause aufgerufen wird,wird
1) die while(running) schleife beendet
2) dadurch die run-Methode verlassen
3) dadurch der Thread beendet

parallel dazu versucht die SurfaceView.pause() Methode den Thread zu joinen Oo

ich dachte aber doch dass ich ihn gerade verlassen habe oder und dieser nicht mehr existiert. oder versucht man das löschen abzufangen indem man wieder joint dieser aber nicht gestartet ist?

und der Fehler ist nun darin dass die while schleife in der pause() Methode immer true ist
und dadurch das Programm im activity lifecycle gar nicht weiter kommt?
 
2) und 3), wenn du viel Glück hast. ;)

Das läuft alles schön hintereinander im Hauptprozess ab. Das System schaltet relativ willkürlich zwischen den Threads, und ruft immer nur ein Thread auf. Starte ruhig mal ein paar Threads, und lass dir ausgeben, wann sie aufgerufen wurde.

Wenn du ein Thread beenden musst, würde ich es mit interrupt() versuchen. Die Methode wurde genau für den Fall implementiert. Alles andere kann ins Auge gehen.

Rheinwerk Computing :: Java ist auch eine Insel - 12 Einfhrung in die nebenlufige Programmierung
 
Okay -.- also in der Insel findet sich leider nichts, im Internet heisst es zu join();

-Die noch laufende Thread Instance wird gelockt bis sie fertig ausgeführt wurde. Danach
wird die Ausführmethode (run?) erneut aufgerufen.

in meinem Beispiel hiesse das : zeichne dein canvas fertig danach mach nochmal die
run method mit running = false sprich mach nichts, thread danach endgueltig beendet.

Somit ist es nur eine Absicherung um wirklich zu warten bis der Thread tot ist um dann
im Activity lifecycle weiter zu gehen? Damit quasi nicht während des Zeichnens abgebrochen und der Canvas möglicherweise gelockt wird?
 
der fehöer das die app sioch aufhängt ist nicht das was in onPause passiert da deine Activity ja nciht diesen status hat(???)

er versucht auch nciht parallel dazu etwas zu tun sondern tut es in 1.2. und 3. genau der Reihenfolge die du beschrben hast.

running wird auf false gesetzt der Ablauf geht in die run-methode an welcher stelle er auch immer gerade ist sobald die while schleife dein running testet sieht es es erfüllt nciht mehr die bedinugn für einen erneuten schleifen durchlauf skippt die schleife kommt zum ende seines lebens/der run methode und übergibt die steuerung wieder an onPause. dort gehts ja nciht mehr weiter und schluss ist!

if(!holder.getSurface().isValid())
continue;

vllt ist die surface ja nciht valid und deswegen hängt sich das auf keine ahnung...

also wie gesagt bei der arbeit mit surfaceview implementiert man eig stets auch gleich das Interface SurfaceHolder.Callback und managed so den LifeCycle des Views!
 
Okay top :)
Habe jetzt verstanden, mit deinen Tipps vom Anfang läuft es, war wohl wirklich das fehlende break in der while(true) schleife der SurfaceView.pause() Methode.
onPause() wurde damit nie beendet und zack es hing.

jetzt muss ich nur noch verstehen warum deine version besser ist und was die SurfaceHolder.Callback() macht.

Danke euch für die guten Tipps! :)
Ich sollte nicht alles aus den Büchern blind übernehmen ;)
 
geh wirklich am besten mein Beispiel durch von wieter oben wie ich mit Surfaceviews umgehe das klappt schon einigermaßen!

Der ursprüngliche Beitrag von 00:12 Uhr wurde um 00:14 Uhr ergänzt:

eine Frage von mir: warum hat deine activity die onPause methode sos chnell angeworfen ich habe gedacht es passierte während onResume bzw. während die Activity noch im Vordergrund war!?

Der ursprüngliche Beitrag von 00:14 Uhr wurde um 00:19 Uhr ergänzt:

Ich hab grad erst gesehen dass das buch von Mario Zechner ist...der Typ hat libGDX geschrieben lol dann müsste das ein gutes Buch sein ich bin ein großer Fan von ihm!

Der ursprüngliche Beitrag von 00:19 Uhr wurde um 00:20 Uhr ergänzt:

hey cool werde mri gleich mal die bücher von ihm downloaden danke für den link
 
hey ich hab zu danken :D du hilfst mir hier echt gut weiter,
ja bisher war es auch gut nachvollziehbar, das nächste Kapitel fasst jetzt dann
alles zusammen und da wirds kniffelig den überblick zu halten.

wie meinst du sie wirft die onPause Methode so schnell an? :S

das aufhängen ist vor der Änderung mit dem break aufgetreten sobald ich diese gestartet habe,
ich habe sie dazu aus einer ListView gestartet, als separate activity. In dem Buch wird erstmal so
eine Test App gemacht mit der zugriffe auf Speicher, touch etc durchgegangen werden. Der surface view test
ist dabei der abschliessende. Ich code in Android Studio. Allerdings war mir Eclipse lieber, nur ging dann iwann der
Marketplace gar nicht mehr -.- deswegen musste ich umsteigen
 
okay also ich hab mal noch ein bisschen weiter experimentiert.
Mein Versuch ist die FPS zu vergleichen:

-einmal habe ich die Activity RenderViewTest, diese nutzt den UI Thread
und sollte damit langsamer sein als die

-SurfaceViewTest Activity, welche den Thread auslagert und separat zeichnet.

Nun zwei Probleme:
warum muss ich bei der Ersten Activity 1000000 / (totalTime/fpscount) rechnen
und bei der zweiten 100000000 / (totaTime/fpscount) rechnen um vernünftige
Zahlen zu bekommen? Beide nutzen den exakt gleichen code, dazu lasse ich mir ein
Rechteck in ZufallFarben füllen und verkleinern mit der Variable warpRect - der Wert 256 ist hier nur gewählt
damit ich diese Variable auch für die Farben nutzen kann und so nicht nur zufällig sondern durch schnelles umschreiben
auch den farbverlauf von 0-255 abdecken kann, nicht wundern - in der grösse bewirkt es nur dass das Rechteck
bis zur hälfte der Breite verkleinert wird ;)

einmal die erste TestActivity:

Code:
/*
 .........................................................||||||||||||| Render View Test |||||||||||
*/
public class RenderViewTest extends Activity {

    RenderView renderView;

    class RenderView extends View {

        Random rand = new Random();
        Paint paint = new Paint();
        double fps;
        double fpsCount=0;
        double lastTime;
        double firstTime;
        double totalTime = 0;
        double deltaTime;

        String FPS = "FPS: 0";

        int rectWarp = 0;

        public RenderView(Context context) {
            super(context);

        }

        protected void onDraw(Canvas canvas) {

                firstTime = System.nanoTime();

                paint.setStyle(Paint.Style.FILL);
                paint.setColor(Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
                canvas.drawRect(canvas.getWidth()*rectWarp/510, canvas.getHeight()/3,canvas.getWidth()-(canvas.getWidth()*rectWarp/510),canvas.getHeight()-(canvas.getHeight()*rectWarp/510),paint);

                rectWarp++;

                if (rectWarp >= 256){
                    rectWarp = 0;
                }
                paint.setTypeface(Typeface.DEFAULT_BOLD);
                paint.setTextSize(canvas.getHeight()/25);
                paint.setTextAlign(Paint.Align.LEFT);
                paint.setColor(Color.BLACK);

                fpsCount++;
                lastTime = System.nanoTime();
                deltaTime = lastTime - firstTime;
                totalTime = totalTime + deltaTime;

                if(fpsCount >= 20) {
                    fps = ((1000000)/(totalTime/fpsCount));
                    fps = (int) fps;
                    FPS = "FPS: " + fps;

                    totalTime = 0;
                    fpsCount = 0;
                }

                canvas.drawText(FPS, canvas.getWidth() / 3, canvas.getHeight()/5, paint);

                invalidate();
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        renderView = new RenderView(this);
        setContentView(renderView);
    }

}

und die zweite, mit separaten render thread:
Code:
/*
 ............................................................||||||||||||| Surface View Test |||||||
*/
public class SurfaceViewTest extends Activity {

    FastRenderView renderView;
    Paint paint;
    Random rand;

    // Variables
    public double fps;
    public double fpsCount=0;
    public double lastTime;
    public double firstTime;
    public double totalTime = 0;
    public double deltaTime;

    String FPS = "FPS: 0";

    int rectWarp = 0;


    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);


        renderView = new FastRenderView(this);
        paint = new Paint();
        rand = new Random(256);
        setContentView(renderView);
    }

    protected void onResume() {
        super.onResume();
        renderView.resume();
    }

    protected void onPause() {
        super.onPause();
        renderView.pause();
    }

    class FastRenderView extends SurfaceView implements Runnable {
        Thread renderThread = null;
        SurfaceHolder holder;
        volatile boolean running = false;

        public FastRenderView(Context context) {
            super(context);
            holder = getHolder();
        }

        public void resume() {
            running = true;
            renderThread = new Thread(this);
            renderThread.start();
        }

        @Override
        public void run() {

            doDraw();
        }

        public void pause() {

            running = false;
            while (true) {
                try {
                    renderThread.join();
                    break;
                } catch (InterruptedException e) {
                    // retry
                }
            }
        }

        public void doDraw(){
            while (running) {
                firstTime = System.nanoTime();

                if (!holder.getSurface().isValid())
                    continue;
                Canvas canvas = holder.lockCanvas();    // LOCK

                paint.setStyle(Paint.Style.FILL);
                paint.setColor(Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
                canvas.drawRect(canvas.getWidth()*rectWarp/510, canvas.getHeight()/3,canvas.getWidth()-(canvas.getWidth()*rectWarp/510),canvas.getHeight()-(canvas.getHeight()*rectWarp/510),paint);

                rectWarp++;

                if (rectWarp >= 256){
                    rectWarp = 0;
                }

                paint.setTypeface(Typeface.DEFAULT_BOLD);
                paint.setTextSize(canvas.getHeight()/25);
                paint.setTextAlign(Paint.Align.LEFT);
                paint.setColor(Color.BLACK);

                fpsCount++;
                lastTime = System.nanoTime();
                deltaTime = lastTime - firstTime;
                totalTime = totalTime + deltaTime;

                if(fpsCount >= 20) {
                    fps = ((1000000000) / (totalTime / fpsCount));
                    fps = (int) fps;
                    FPS = "FPS: " + fps;

                    totalTime = 0;
                    fpsCount = 0;
                }
                canvas.drawText(FPS, canvas.getWidth() / 3, canvas.getHeight()/5, paint);
                holder.unlockCanvasAndPost(canvas);     // UNLOCK
            }
        }
    }
}

-merkwürdigerweise wird bei der zweiten auch die Grafik komplett verbuggt...
-ist die Berechnung überhaupt gut so? nachdem die Fps berechnet wurden werden ja noch
ein paar Zeilen Code ausgeführt:
Code:
 if(fpsCount >= 20) {
                    fps = ((1000000000) / (totalTime / fpsCount));
                    fps = (int) fps;
                    FPS = "FPS: " + fps;

                    totalTime = 0;
                    fpsCount = 0;
                }
                canvas.drawText(FPS, canvas.getWidth() / 3, canvas.getHeight()/5, paint);
                holder.unlockCanvasAndPost(canvas);     // UNLOCK
 
Zuletzt bearbeitet:
also zum FPS bestimmen machst du wohl so dass du anch 20 frames die zeit bestimmst wieviel diese 20 frames gebraucht haben...eine merkwürdige vorgehensweise wenn auch nciht falsch...

da du die nanozeit nimmst muss der Faktor auf jeden Fall 1 000 000 000 sein und nciht nur eine million

ansonsten schlage ich vor die FPS so auszurechnen:

PHP:
lastTime=System.nanoTime();

//hier alle operationen

totaltime+=System.nanoTime()-lastTime;
if(totalTime>=1 000 000 000l)
{
    FPS="FPS: " + fpsCount;
    fpsCount=0;
    totaltime-=1 000 000 000;
}

dann würden deltaTime und firstTime wegfallen

so kriegst du jede sekunde ein update...das kannst du natürlich auch runterschrauben auf mmmh 0.1 s dann müsstest du fpsCount mal 0.1 oder durch 10 rechnen oder auf 0.01 dann eben mal 0.01 oder durhc 100 du verstehst schon!
Natürlich musst du dann in der Bedingugn die zahl auf 100 000 000 ns bzw. 10 000 000 ns runterschrauben und so...

Der ursprüngliche Beitrag von 19:36 Uhr wurde um 19:47 Uhr ergänzt:

und ja die rechnung die sie da anstellen aus dem buch ist richtig auch wenn mir die Form nciht gefällt aus (.../.../...) könnte man eine besser lesbare berechnung machen und zwar : fpsCount*1 000 000 000 / totalTime

dann würde man bnesser sehen dass der Faktor 1 000 000 000/totalTime bestimmt wie der fpscount gewertet wird weil wenn diese 20 frames in 0.1 sekunden generiert wurden würde die rechnung 20*1 000 000 000 / 100 000 000 und würde eine FPS von
200 ergeben:)O) du solltest noch ein sleep einfügen um der CPU etwas zeit zum ausruhen zu geben

Der ursprüngliche Beitrag von 19:47 Uhr wurde um 19:51 Uhr ergänzt:

und ja du ahst recht deren berechnung für firstTime sollte sofort nachdem deltatime ausgerechnet wurd kommen damit alles was danach kommt bis zur neuen Berechnung von deltatime zeitlich erfasst wird!
Also kurz: zeitpunkt setzen;operationen;deltatime berechnen;zeitpunkt setzen;operationen;...
 
hm also die Rechnung war nicht im Buch die habe ich selbst überlegt ;)
ich wollte überprüfen ob es stimmt dass die fps höher sind wenn ich das Rendern
in einem separatem Thread mache:

also zähle ich jeden durchlauf fpscount +1
nach 20 updates teile ich die insgesamt vergange zeit durch die gesamten fps (20+)
und müsste somit rausbekommen wie lange ich für ein fps brauche. danach rechne ich
1 sekunde also 1000000000 / die zeit pro (durchschnittlichen) durchlauf.

die 20 sind nur da um einen durchschnitt über 20 fps zu erhalten und um das Ergebnis nicht
zu schnell flimmern zu lassen.

lustigerweise wenn ich 1000000000/ (zeitfür20updates / 20+ updates) rechne bekomme ich
bei der ersten view die keinen separaten thread nutzt zwischen 10000-15000 fps....

beim zweiten stimmts eher da bekomme ich 50-70 fps.
die erste Rechnung ist also um 3 stellen zu hoch...ich denke 10-15 fps wären da realistischer
komischerweise muss ich eben dort 100000 für eine Sekunde veranschlagen sonst bekomme
ich eben die zu hohen zahlen. Aber beide Varianten messen doch in nano Sekunden.
Das verstehe ich nicht :S

Der ursprüngliche Beitrag von 22:41 Uhr wurde um 22:48 Uhr ergänzt:

Jaiel schrieb:
dann würde man bnesser sehen dass der Faktor 1 000 000 000/totalTime bestimmt wie der fpscount gewertet wird weil wenn diese 20 frames in 0.1 sekunden generiert wurden würde die rechnung 20*1 000 000 000 / 100 000 000 und würde eine FPS von
200 ergeben:)O) du solltest noch ein sleep einfügen um der CPU etwas zeit zum ausruhen zu geben


und ja du ahst recht deren berechnung für firstTime sollte sofort nachdem deltatime ausgerechnet wurd kommen damit alles was danach kommt bis zur neuen Berechnung von deltatime zeitlich erfasst wird!
Also kurz: zeitpunkt setzen;operationen;deltatime berechnen;zeitpunkt setzen;operationen;...


ich wollte noch kein sleep reinnehmen um zu sehen was das handy kann ;)
kommt aber noch :D nur wusste ich nicht wie ich das im UI thread einbaue,
deswegen wollte ich es auch noch nicht im extra thread einbauen ;)


wenn du magst schick mir deine email als pm dann sende ich dir das Komplette Projekt (wenn sich das auf eclipse importieren lässt)
ich habe ja android studio benutzt. Bin vom Stand her nun bei ende von Kapitel 4 und im 5ten gehts daran daraus ein spiel zu
basteln. habe lediglich ein paar tests abgeändert ( Schrift mittig etc...bei der renderview die fps sind die grösste veränderung zum buch ;) )
 
Zuletzt bearbeitet:
naja die 10000 Frames könnten hinhauen da du keine allzu aufwenidgen operationen machst könnte es schon 10k bilder pro sekunde haben

kommt vllt drauf an ob du es auf dem handy ausführst oder am pc aber egal eig. mein note 4 zum beispiel hat eine CPU mit 2,7 GHZ und davon 4 bei so hoher taktzahl und so wenigen operationen könnten also die 10 k schon hinkommen!
 
Jaiel schrieb:
naja die 10000 Frames könnten hinhauen da du keine allzu aufwenidgen operationen machst könnte es schon 10k bilder pro sekunde haben

kommt vllt drauf an ob du es auf dem handy ausführst oder am pc aber egal eig. mein note 4 zum beispiel hat eine CPU mit 2,7 GHZ und davon 4 bei so hoher taktzahl und so wenigen operationen könnten also die 10 k schon hinkommen!

oh okay :D kein wunder das es so komisch flattert, jetzt check ich woher das kommt,
mein handy ist nicht zu lahm sondern geht mein muster wohl 500 mal durch, bzw
überspringt für mein auge 500 Durchgänge, das ja eh nur bis 30 fps kann oder? ;)

VIELEN DANK!! :)


ahhh moment.. .dann müsste es in der SurfaceViewTest aber noch höher sein :( oder nicht?

okay und noch eins :D die verbuggte Grafik der renderview kommt daher da ich mein Bild nicht lösche,
also er übermalt ständig, die Zahlen übereinander. Wie lösche ich das? Double Buffer ne? :D
also zwei mal holder - lock / unlock und post? einmal mit übermalter fläche? man man sorry dass ich es so
kompliziert mache. aber lerne mit euch echt 10000 mal besser ;)

--das wars ;) einfach nochmal übermalen und nochmal posten hat die Optik gelöst.
FPS bleiben bei 69-72 :/ und bei der Activity ohne render Thread mit selbiger zeitberechnung ums 1000x höher :S

ums deutlich zu machen:


10000-14000fps:
Code:
protected void onDraw(Canvas canvas) {

                firstTime = System.nanoTime();

                paint.setStyle(Paint.Style.FILL);
                paint.setColor(Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
                canvas.drawRect(canvas.getWidth()*rectWarp/510, canvas.getHeight()/3,canvas.getWidth()-(canvas.getWidth()*rectWarp/510),canvas.getHeight()-(canvas.getHeight()*rectWarp/510),paint);

                rectWarp++;

                if (rectWarp >= 256){
                    rectWarp = 0;
                }
                paint.setTypeface(Typeface.DEFAULT_BOLD);
                paint.setTextSize(canvas.getHeight()/25);
                paint.setTextAlign(Paint.Align.LEFT);
                paint.setColor(Color.BLACK);

                fpsCount++;
                lastTime = System.nanoTime();
                deltaTime = lastTime - firstTime;
                totalTime = totalTime + deltaTime;

                if(fpsCount >= 20) {
                    fps = ((1000000000)/(totalTime/fpsCount));
                    fps = (int) fps;
                    FPS = "FPS: " + fps;

                    totalTime = 0;
                    fpsCount = 0;
                }

                canvas.drawText(FPS, canvas.getWidth() / 3, canvas.getHeight()/5, paint);

                invalidate();
        }


69-75 FPS:
Code:
public void doDraw(){
            while (running) {
                firstTime = System.nanoTime();

                if (!holder.getSurface().isValid())
                    continue;
                Canvas canvas = holder.lockCanvas();    // LOCK

                paint.setStyle(Paint.Style.FILL);
                paint.setColor(Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
                canvas.drawRect(canvas.getWidth()*rectWarp/510, canvas.getHeight()/3,canvas.getWidth()-(canvas.getWidth()*rectWarp/510),canvas.getHeight()-(canvas.getHeight()*rectWarp/510),paint);

                rectWarp++;

                if (rectWarp >= 256){
                    rectWarp = 0;
                }

                paint.setTypeface(Typeface.DEFAULT_BOLD);
                paint.setTextSize(canvas.getHeight()/25);
                paint.setTextAlign(Paint.Align.LEFT);
                paint.setColor(Color.WHITE);

                fpsCount++;
                lastTime = System.nanoTime();
                deltaTime = lastTime - firstTime;
                totalTime = totalTime + deltaTime;

                if(fpsCount >= 20) {
                    fps = ((1000000000) / (totalTime / fpsCount));
                    fps = (int) fps;
                    FPS = "FPS: " + fps;

                    totalTime = 0;
                    fpsCount = 0;
                }
                canvas.drawText(FPS, canvas.getWidth() / 3, canvas.getHeight()/5, paint);
                holder.unlockCanvasAndPost(canvas);     // UNLOCK

                if (!holder.getSurface().isValid())
                    continue;
                    canvas = holder.lockCanvas();
                    canvas.drawRGB(0,0,0);
                holder.unlockCanvasAndPost(canvas);
            }
        }

schaut die zeitberechnung an beide gleich.
beim unteren wird nur pro fps doppelt gezeichnet (double buffer) dafür aber auch in einem eigenen thread gemacht
 
Zuletzt bearbeitet:
Hi also ich verspreche dir morgen dein Code mal anders zu schreiben
und als kleine wichtige info: du machst schon automatisch eine Doppel pufferung da du ja auf ein canvas zuerst lockst (lockcanvas) diesen manipulierst (canvas.draw...)und dann erst malst (unlockcanvasandpost ())
Da brauchst du nicht löschen oder schwarz drüber malen

Bis dann jaiel
 
PHP:
public void doDraw(){

            paint.setTypeface(Typeface.DEFAULT_BOLD);
            paint.setTextSize(canvas.getHeight()/25);
            paint.setTextAlign(Paint.Align.LEFT);
            paint.setStyle(Paint.Style.FILL);
            Canvas canvas;
            while (running) {

                if(firstTime!=0)
                totalTime += System.nanoTime() - firstTime;

                firstTime = System.nanoTime();

                if(fpsCount >= 20/*&&totalTime!=0*/) {
                    FPS = "FPS: " + (int)((1000000000) / (totalTime / fpsCount));

                    totalTime = 0;
                    fpsCount = 0;
                }

                if (!holder.getSurface().isValid())
                    continue;
                canvas = holder.lockCanvas();    // LOCK

                paint.setColor(Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
                canvas.drawRect(canvas.getWidth()*rectWarp, canvas.getHeight()/3,canvas.getWidth()-(canvas.getWidth()*rectWarp),canvas.getHeight()-(canvas.getHeight()*rectWarp),paint);

                rectWarp++;
                rectWarp%=256;
                rectWarp/=510;


                paint.setColor(Color.WHITE);
                canvas.drawText(FPS, canvas.getWidth() / 3, canvas.getHeight()/5, paint);
                holder.unlockCanvasAndPost(canvas);
                fpsCount++;        // UNLOCK
        }

habe es kaum verändert nur ein bisschen "kleiner" geschrieben dadurch sind lastTime und fps rausgeflogen
eine if anfrage(die anch ob warp größer 256 ist hab ich umgangen in dem ich den modulo operator benutzt habe der bei warp =256 eine 0 ausgibt bei 257 eine 1 und so weiter) und den warp auch gleich durch 510 geteilt damit du bei drawrect nicht 3 mal eine / operatione ausführst(spart an rechenzeit)

ausserdem habe ich die paint .set methoden die du nur einmal zu setzen braucht aus der schelife genommen da das nochmal rechenzeit spart


ansonsten ist das so lauffähig wie es da steht
 
Zuletzt bearbeitet:
Jawoll ganz herzlichsten Dank! Werde es gleich ausprobieren sobald ich zuhause bin :)
Klasse das du dir die Muehe gemacht hast! Ich hoffe ich kann mich
auch mal schlau revangieren ;)

wie bedanke ich mich fuer einen beitrag? ;)

Der ursprüngliche Beitrag von 19:13 Uhr wurde um 20:30 Uhr ergänzt:

okay so ganz laufen wills noch nicht ;) habe das Canvas canvas an den Anfang kopiert
und in der onCreate() dann canvas = new Canvas(); gemacht, da er sonst die Referenz
canvas.getWidth() nicht finden wollte.

Hier mal wie meine neue Activity mit deiner onDraw() Methode aussieht:

Code:
package com.example.jesjjes.dummiesbuch.Tests;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;

import java.util.Random;

/*
 ............................................................||||||||||||| Surface View Test 2 |||||
*/
public class SurfaceViewTest2 extends Activity{

    FastRenderView renderView;
    Paint paint;
    Random rand;
    Canvas canvas;

    // Variables
    public double fps;
    public double fpsCount=0;
    public double firstTime;
    public double totalTime = 0;

    String FPS = "FPS: 0";

    int rectWarp = 0;


    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);


        renderView = new FastRenderView(this);
        paint = new Paint();
        rand = new Random(256);
        firstTime =  System.nanoTime();
        canvas = new Canvas();
        setContentView(renderView);
    }

    protected void onResume() {
        super.onResume();
        renderView.resume();
    }

    protected void onPause() {
        super.onPause();
        renderView.pause();
    }

    class FastRenderView extends SurfaceView implements Runnable {
        Thread renderThread = null;
        SurfaceHolder holder;
        volatile boolean running = false;

        public FastRenderView(Context context) {
            super(context);
            holder = getHolder();
        }

        public void resume() {
            running = true;
            renderThread = new Thread(this);
            renderThread.start();
        }

        @Override
        public void run() {

            doDraw();
        }

        public void pause() {

            running = false;
            while (true) {
                try {
                    renderThread.join();
                    break;
                } catch (InterruptedException e) {
                    // retry
                }
            }
        }

        public void doDraw() {
            firstTime = System.nanoTime();
            while (running) {
                if (!holder.getSurface().isValid())
                    continue;

                paint.setTypeface(Typeface.DEFAULT_BOLD);
                paint.setTextSize(canvas.getHeight() / 25);
                paint.setTextAlign(Paint.Align.LEFT);
                paint.setStyle(Paint.Style.FILL);

                while (running) {

                    if (firstTime != 0)
                        totalTime += System.nanoTime() - firstTime;

                    firstTime = System.nanoTime();

                    if (fpsCount >= 20/*&&totalTime!=0*/) {
                        FPS = "FPS: " + (int) ((1000000000) / (totalTime / fpsCount));

                        totalTime = 0;
                        fpsCount = 0;
                    }

                    if (!holder.getSurface().isValid())
                        continue;
                    canvas = holder.lockCanvas();    // LOCK

                    paint.setColor(Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
                    canvas.drawRect(canvas.getWidth() * rectWarp, canvas.getHeight() / 3, canvas.getWidth() - (canvas.getWidth() * rectWarp), canvas.getHeight() - (canvas.getHeight() * rectWarp), paint);

                    rectWarp++;
                    rectWarp %= 256;
                    rectWarp /= 510;


                    paint.setColor(Color.WHITE);
                    canvas.drawText(FPS, canvas.getWidth() / 3, canvas.getHeight() / 5, paint);
                    holder.unlockCanvasAndPost(canvas);
                    fpsCount++;        // UNLOCK
                }
            }
        }
    }
}




und hier nochmal meine alte, copy paste sollte klappen ;)

Code:
package com.example.jesjjes.dummiesbuch.Tests;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.*;

import java.util.Random;

/*
 ............................................................||||||||||||| Surface View Test |||||||
*/
public class SurfaceViewTest extends Activity {

    FastRenderView renderView;
    Paint paint;
    Random rand;

    // Variables
    public double fps;
    public double fpsCount=0;
    public double lastTime;
    public double firstTime;
    public double totalTime = 0;
    public double deltaTime;

    String FPS = "FPS: 0";

    int rectWarp = 0;


    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);


        renderView = new FastRenderView(this);
        paint = new Paint();
        rand = new Random(256);
        setContentView(renderView);
    }

    protected void onResume() {
        super.onResume();
        renderView.resume();
    }

    protected void onPause() {
        super.onPause();
        renderView.pause();
    }

    class FastRenderView extends SurfaceView implements Runnable {
        Thread renderThread = null;
        SurfaceHolder holder;
        volatile boolean running = false;

        public FastRenderView(Context context) {
            super(context);
            holder = getHolder();
        }

        public void resume() {
            running = true;
            renderThread = new Thread(this);
            renderThread.start();
        }

        @Override
        public void run() {

            doDraw();
        }

        public void pause() {

            running = false;
            while (true) {
                try {
                    renderThread.join();
                    break;
                } catch (InterruptedException e) {
                    // retry
                }
            }
        }

        public void doDraw(){
            while (running) {
                firstTime = System.nanoTime();

                if (!holder.getSurface().isValid())
                    continue;
                Canvas canvas = holder.lockCanvas();    // LOCK

                paint.setStyle(Paint.Style.FILL);
                paint.setColor(Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
                canvas.drawRect(canvas.getWidth()*rectWarp/510, canvas.getHeight()/3,canvas.getWidth()-(canvas.getWidth()*rectWarp/510),canvas.getHeight()-(canvas.getHeight()*rectWarp/510),paint);

                rectWarp++;

                if (rectWarp >= 256){
                    rectWarp = 0;
                }

                paint.setTypeface(Typeface.DEFAULT_BOLD);
                paint.setTextSize(canvas.getHeight()/25);
                paint.setTextAlign(Paint.Align.LEFT);
                paint.setColor(Color.WHITE);

                fpsCount++;
                lastTime = System.nanoTime();
                deltaTime = lastTime - firstTime;
                totalTime = totalTime + deltaTime;

                if(fpsCount >= 20) {
                    fps = ((1000000000) / (totalTime / fpsCount));
                    fps = (int) fps;
                    FPS = "FPS: " + fps;

                    totalTime = 0;
                    fpsCount = 0;
                }
                canvas.drawText(FPS, canvas.getWidth() / 3, canvas.getHeight()/5, paint);
                holder.unlockCanvasAndPost(canvas);     // UNLOCK

                if (!holder.getSurface().isValid())
                    continue;
                    canvas = holder.lockCanvas();
                    canvas.drawRGB(0,0,0);
                holder.unlockCanvasAndPost(canvas);
            }
        }
    }
}


Das wird noch :D :D

bei deiner Version, so wie ich sie oben implementiert habe, habe ich zwei Probleme:

Die Schrift (FPS : ) wird nicht angezeigt, bzw nur ein weisser Punkt,
und das farbenwechselnde Rechteck wird nicht kleiner.

trotzdem schonmal danke ;) der Hinweis das / auch Ressourcen verhaucht war mir
vorher nicht so einleuchtend ;)

Der ursprüngliche Beitrag von 20:30 Uhr wurde um 20:42 Uhr ergänzt:

ok erster Fehler ist gefunden ;)
- ich musste nochmal direkt vorm TextDraw:
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(canvas.getHeight() / 25);
paint.setTextAlign(Paint.Align.LEFT);

machen, sonst war der text so klein wie 1 pixel.

aber nun überschreibt es sich trotzdem noch, also muss ein double buffer doch rein? S:

Okay nun aber ;) so klappt deine onDraw auch!
Allerdings FPS 29-30
zwar Konstant für ein Spiel aber das Rechteck wird noch nicht kleiner und
30 Fps werden schnell kleiner sobald ich Bilder zeichne oder?
Wie steht es wenn ich nun hier noch thread.sleep(20) mache?
Werden die FPS dann noch geringer? :S
Code:
public void doDraw() {
            firstTime = System.nanoTime();
            while (running) {
                if (!holder.getSurface().isValid())
                    continue;


                paint.setStyle(Paint.Style.FILL);

                while (running) {

                    if (firstTime != 0) {
                        totalTime += System.nanoTime() - firstTime;
                    }else{
                        firstTime = System.nanoTime();
                    }

                    firstTime = System.nanoTime();

                    if (fpsCount >= 20/*&&totalTime!=0*/) {
                        FPS = "FPS: " + (int)  ((1000000000) / (totalTime / fpsCount));

                        totalTime = 0;
                        fpsCount = 0;
                    }

                    if (!holder.getSurface().isValid())
                        continue;
                    canvas = holder.lockCanvas();    // LOCK

                    paint.setColor(Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
                    canvas.drawRect(canvas.getWidth() * rectWarp, canvas.getHeight() / 3, canvas.getWidth() - (canvas.getWidth() * rectWarp), canvas.getHeight() - (canvas.getHeight() * rectWarp), paint);

                    rectWarp++;
                    rectWarp %= 256;
                    rectWarp /= 510;


                    paint.setColor(Color.WHITE);
                    paint.setTypeface(Typeface.DEFAULT_BOLD);
                    paint.setTextSize(canvas.getHeight() / 25);
                    paint.setTextAlign(Paint.Align.LEFT);
                    canvas.drawText(FPS, canvas.getWidth() / 3, canvas.getHeight() / 5, paint);
                    holder.unlockCanvasAndPost(canvas);
                    fpsCount++;        // UNLOCK


                    if (!holder.getSurface().isValid())
                        continue;
                    canvas = holder.lockCanvas();
                    canvas.drawRGB(0,0,0);
                    holder.unlockCanvasAndPost(canvas);
                }
            }
        }
 
Zuletzt bearbeitet:

Ähnliche Themen

W
  • WuDiDong
Antworten
3
Aufrufe
769
jogimuc
J
P
Antworten
0
Aufrufe
556
Pascppal
P
S
Antworten
4
Aufrufe
995
Sempervivum
S
Zurück
Oben Unten