SurfaceView und Activity mit Zurück-Button beenden

Hallo,

ich habe ein Problem mit dem SurfaceView und dem Back Button (Hardware) meines Handys
und zwar reagiert der leider nicht. Es scheint wohl generell ein Problem mit dem SurfaceView und den Hardware Buttons (und auch dem Drehen) zu geben, aber eine Lösung hab ich leider nicht gefunden.

Gibt es denn eine Lösung, wie ich es schaffe einen Thread zu beenden, wenn ich den Back Button drücke?
Gibt es da eine Methode, die dann aufgerufen wird?
Es funktioniert leider nicht, wenn ich die onBackPressed() Methode überschreibe

Kann mir da jemand helfen?
Vielen Dank,
Chris

Java Code:

[ol]
[li]package de.fuckthesystem.thenewboston;[/li]
[li] [/li]
[li]import …[/li]
[li] [/li]
[li]public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {[/li]
[li] [/li]
[li] private static final String TAG = MainGamePanel.class.getSimpleName();[/li]
[li] public MainThread thread;[/li]
[li] private GreenBall greenBall;[/li]
[li] private SpaceBackground spaceBackground; [/li]
[li] [/li]
[li] private int counter;[/li]
[li] [/li]
[li] [/li]
[li] public MainGamePanel(Context context) {[/li]
[li] super(context);[/li]
[li] [/li]
[li] //Loging Stuff[/li]
[li] Log.d(TAG, “MainGamePanel Construkter”);[/li]
[li] [/li]
[li] [/li]
[li] getHolder().addCallback(this);[/li]
[li] [/li]
[li] //create greenBall and Load[/li]
[li] greenBall = new GreenBall(BitmapFactory.decodeResource(getResources(), R.drawable.greenball), 50, 50);[/li]
[li] //create BG and Load[/li]
[li] spaceBackground = new SpaceBackground(BitmapFactory.decodeResource(getResources(), R.drawable.verticalspace), 50);[/li]
[li] [/li]
[li] [/li]
[li] thread = new MainThread(getHolder(), this);[/li]
[li] [/li]
[li] setFocusable(true);[/li]
[li] }[/li]
[li] [/li]

[li] @Override[/li]
[li] public void surfaceChanged(SurfaceHolder holder, int format, int width,[/li]
[li] int height) {[/li]
[li] // TODO Auto-generated method stub[/li]
[li] [/li]
[li] //Loging Stuff[/li]
[li] Log.d(TAG, “MainGamePanel Surface Changed”);[/li]
[li] [/li]
[li] }[/li]
[li] [/li]
[li] @Override[/li]
[li] public void surfaceCreated(SurfaceHolder holder) {[/li]
[li] [/li]
[li] //Loging Stuff[/li]
[li] Log.d(TAG, “MainGamePanel surfaceCreated”);[/li]
[li] [/li]
[li] [/li]
[li] thread.setRunning(true);[/li]
[li] thread.start();[/li]
[li] [/li]
[li] }[/li]
[li] [/li]
[li] @Override[/li]
[li] public void surfaceDestroyed(SurfaceHolder holder) {[/li]
[li] [/li]
[li] //Loging Stuff[/li]
[li] Log.d(TAG, “MainGamePanel surfaceDestroyed”);[/li]
[li] [/li]
[li] [/li]
[li] boolean retry = true;[/li]
[li] while(retry) {[/li]
[li] try {[/li]
[li] Log.d(TAG, “trying to destroy thread”);[/li]
[li] thread.join();[/li]
[li] retry = false;[/li]
[li] } catch (InterruptedException e) {[/li]
[li] // try again shutting down the thread[/li]
[li] }[/li]
[li] }[/li]
[li] Log.d(TAG, “did destroy thread”);[/li]
[li] [/li]
[li] [/li]
[li] }[/li]
[li] [/li]
[li] @Override[/li]
[li] public boolean onTouchEvent(MotionEvent event) {[/li]
[li] [/li]
[li] //Loging Stuff[/li]
[li] Log.d(TAG, “MainGamePanel onTouchEvent”);[/li]
[li] [/li]
[li] if (event.getAction() == MotionEvent.ACTION_DOWN) {[/li]
[li] // delegating event handling to the droid[/li]
[li] greenBall.handleActionDown((int)event.getX(), (int)event.getY());[/li]
[li] [/li]
[li] // check if in the lower part of the screen we exit[/li]
[li] if (event.getY() > getHeight() - 50) {[/li]
[li] thread.setRunning(false);[/li]
[li] ((Activity)getContext()).finish();[/li]
[li] } else {[/li]
[li] Log.d(TAG, “Coords: x=” + event.getX() + “,y=” + event.getY());[/li]
[li] }[/li]
[li] } if (event.getAction() == MotionEvent.ACTION_MOVE) {[/li]
[li] // the gestures[/li]
[li] if (greenBall.isTouched()) {[/li]
[li] // the droid was picked up and is being dragged[/li]
[li] greenBall.setX((int)event.getX());[/li]
[li] greenBall.setY((int)event.getY());[/li]
[li] }[/li]
[li] } if (event.getAction() == MotionEvent.ACTION_UP) {[/li]
[li] // touch was released[/li]
[li] if (greenBall.isTouched()) {[/li]
[li] greenBall.setTouched(false);[/li]
[li] }[/li]
[li] }[/li]
[li] return true;[/li]
[li] }[/li]
[li] [/li]
[li] @Override[/li]
[li] public void draw(Canvas canvas) {[/li]
[li] [/li]
[li] //Loging Stuff[/li]
[li] //Log.d(TAG, “MainGamePanel draw”);[/li]
[li] [/li]
[li] [/li]
[li] // TODO Auto-generated method stub[/li]
[li] //super.draw(canvas);[/li]
[li] //canvas.drawColor(Color.RED);[/li]
[li] canvas.drawBitmap(spaceBackground.getLook(), 0, (spaceBackground.getY()-spaceBackground.getLook().getHeight())+canvas.getHeight(), null);[/li]
[li] [/li]
[li] Paint textPaint = new Paint();[/li]
[li] textPaint.setARGB(50, 254, 10, 50);[/li]
[li] textPaint.setTextAlign(Align.CENTER);[/li]
[li] textPaint.setTextSize(50);[/li]
[li] canvas.drawText(“counter :”+ counter, canvas.getWidth()/2, 200, textPaint);[/li]
[li] counter++;[/li]
[li] [/li]
[li] greenBall.draw(canvas);[/li]
[li] [/li]
[li] }[/li]
[li] [/li]
[li] [/li]
[li] [/li]
[li] [/li]
[li] public void destroyThread() {[/li]

[li] [/li]
[li] //Loging Stuff[/li]
[li] Log.d(TAG, “MainGamePanel destroyThread”);[/li]
[li] [/li]
[li] [/li]
[li] // TODO Auto-generated method stub[/li]
[li] /*boolean retry = true;[/li]
[li]** while(retry) {[/li]
[li]
try {[/li]
[li]
thread.join();[/li]
[li]
retry = false;[/li]
[li]
} catch (InterruptedException e) {[/li]
[li]
// try again shutting down the thread**[/li]
[li]** }[/li]
[li]
}*/**[/li]
[li] }[/li]
[li]}[/li]

[/ol]

hier noch die anderen Klassen:


import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;

public class DroidzActivity extends Activity {
    
    MainGamePanel myMainGamePanel;
    
    private static final String TAG = DroidzActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        
        myMainGamePanel = new MainGamePanel(this);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        
        setContentView(myMainGamePanel);
        
        Log.d(TAG, "view added");
        
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        Log.d(TAG, "destryoing...");
        super.onDestroy();
        finish();
    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        
    }

    @Override
    public void onBackPressed() {
        // TODO Auto-generated method stub
        Toast.makeText(this, "Back", Toast.LENGTH_SHORT).show();
        myMainGamePanel.destroyThread();
        super.onBackPressed();
    }

    @Override
    protected void onStop() {
        // TODO Auto-generated method stub
        Log.d(TAG, "stopping...");
        super.onStop();
    }
    
    

}


import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;

public class MainThread extends Thread {

    private static final String TAG = MainThread.class.getSimpleName();

    private boolean running;

    private SurfaceHolder surfaceHolder;
    private MainGamePanel gamePanel;

    // desired fps
    private final static int MAX_FPS = 50;
    // maximam numbers of frames to be skipped
    private static final int MAX_FRAME_SKIPS = 5;
    // the frame periode
    private final static int FRAME_PERIOD = 1000 / MAX_FPS;

    public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
        
        super();
        this.surfaceHolder = surfaceHolder;
        this.gamePanel = gamePanel;
        
        //Loging Stuff
        Log.d(TAG, "MainThread Construkter");

    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    //@SuppressLint("WrongCall")
    @Override
    public void run() {
        //Loging Stuff
        Log.d(TAG, "MainThread run");
                
        Canvas canvas;
        Log.d(TAG, "Starting Game Loop");

        while (running) {
            canvas = null;
            
            try {
                canvas = this.surfaceHolder.lockCanvas();
                synchronized (surfaceHolder) {
                    this.gamePanel.draw(canvas);
                }
                
            } finally {
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            } // end finally
        
        }

    }
}

Also nicht einmal der Toast wird angezeigt? Der Code dafür sieht in Ordnung aus.

Die einzige sichere Möglichkeit ist im onPause den Thread zu beenden. Den weder bei onStop noch onDestroy wird garantiert, dass die Callbacks aufgerufen werden. Das sind allerdings Basics. Schau dir einmal den Activity-Lifecycle genauer an.

Hilft dir zwar nicht mit dem seltsamen Back-Button-Problem ist aber trotzdem die eigentliche Lösung.

Brauchst du das Drehen umbedingt? Das ist nämlich ein ziemlicher Mist da bei einer jeden Drehung deine Views zerstört werden und neu erstellt werden. Das heißt da wird jedes Mal onCreate aufgerufen was oft Troubles macht. Für ein Spiel ist’s sowieso öfter so, dass man das nur in einer Ausrichtung hat.

Hallo Schlingel,

doch, der Toast wird angezeigt, es wird auch die vorherige Activity angezeigt, von der ich komme (es ist ein Menu, von der ich die Activity DroidzActivity aufrufe)
Ich hab ja dazu gelernt und jede Methode für LogCat getaggt, u.a. auch die Methoden für den LifeCycle

In anderen Activitys funktioniert das auch alles tadellos, sie gehen in onPause und rufe ich eine andere Activity auf, gehen sie in onDestroy
und wenn ich in der o.g. Klasse die über den TouchEvent beende, funktioniert das auch alles

das Drehen ist mir nicht wichtig, ich hatte das nur so verstanden, dass (ich sag mal) „Hardware-Aktivitäten“ wie drehen oder Back-Button beim SurfaceView Probleme machen

Gruß,
Chris

*** Edit ***

Verdammt, ich hab mich mal regisitrieren um Beiträge zu editieren
Was ich meinte ist, die vorherige Activity wird zwar angezeigt, aber da friert dann die AVD ein und es geht nix mehr

Vielen Dank, damit hat es geklappt, obwohl ich nicht verstehe, warum das nicht in dem Tutorial auch so gemacht wird, da wird onPause gar nicht beachtet

Leider ergibt sich jetzt wieder ein neues Problem, ich kann die Activity nur maximal 3x aufrufen, dann stürzt sie wieder ab, weil es ein Problem mit der BitmapFactory gibt
Source not found
und LogCat:
10-09 17:57:14.704: E/dalvikvm-heap(514): 7200000-byte external allocation too large for this process.
10-09 17:57:14.823: E/GraphicsJNI(514): VM won’t let us allocate 7200000 bytes

Grüße,
C.

Weil viele Tutorials einen anderen Fokus haben oder einfach Mist sind :-/ Man kommt - wenn man seine Aufgabe ernst nimmt - nicht darum herum sich die Google Doku durch zu lesen. Bonus: Man hört/sieht sich die Google IO Vorträge an. Die sind spitze und geben viel Einblick in’s Innenleben.

Du musst den Speicher der Bitmaps freigeben wenn du sie nicht mehr brauchst. (Aufpassen, dass du nicht aus Versehen ein Bitmap recyclest das noch verwendet wird)

Generell ist das ein sehr, sehr leidiges Thema auf Android. Ich hatte auch schon mit OutOfMemoryErrors zu kämpfen und musste dann die ganze App profilen um die Fehler zu finden. (Hab ich hier dokumentiert.)

Vielen vielen Dank! :slight_smile: