Domanda

Ho una messa a punto SurfaceView e funzionante, ma quando riprendo io ottenere un errore che il thread è già stato avviato. Qual è il modo corretto di gestire quando l'applicazione passa in secondo piano e poi di nuovo in primo piano? Ho armeggiato intorno e riuscito ad ottenere l'applicazione di tornare senza schiantarsi ... ma la SurfaceView non disegna più niente. Il mio codice:

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
           Log.e("sys","surfaceCreated was called.");
           if(systemState==BACKGROUND){
                  thread.setRunning(true);

           }
           else {
        thread.setRunning(true);
               thread.start();
               Log.e("sys","started thread");
               systemState=READY;
           }



    }
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
           Log.e("sys","surfaceDestroyed was called.");
           thread.setRunning(false);
           systemState=BACKGROUND;
    }
È stato utile?

Soluzione

La soluzione più semplice è quello di uccidere e riavviare il filo semplicemente. Creare metodi resume () - crea oggetto thread e lo avvia - e pause () - uccide filo (vedi Lunarlander esempio) -. Nella classe SurfaceView e chiamare questi da surfaceCreated e surfaceDestroyed per avviare e fermare il filo

Ora, nella attività che gestisce il SurfaceView, avrete anche bisogno di chiamare il resume () e pause () metodi nella SurfaceView dalla propria attività (o del frammento) onResume () e onPause (). Non è una soluzione elegante, ma funzionerà.

Altri suggerimenti

Questo bug sembra riguardare il bug lander lunare, che è abbastanza famoso (fare una ricerca su Google su di esso). Dopo tutto questo tempo, e dopo varie versioni release Android, il bug esiste ancora e nessuno ha preoccupato di aggiornarlo. Ho trovato questo al lavoro con il disordine codice di almeno:

  public void surfaceCreated(SurfaceHolder holder) {     
          if (thread.getState==Thread.State.TERMINATED) { 
               thread = new MainThread(getHolder(),this);
          }
          thread.setRunning(true);
          thread.start();
  }
public void surfaceCreated(SurfaceHolder holder) {
        if (!_thread.isAlive()) {
            _thread = new MyThread(this, contxt);
        }

public void surfaceDestroyed(SurfaceHolder holder) {            
        boolean retry = true;
        _thread.setRunning(false);
        while (retry) {
            try {
                _thread.join();
                retry = false;
            } catch (InterruptedException e) {
                // we will try it again and again...
            }
        }
    }

Il modo migliore che ho trovato è quello di sovrascrivere il metodo onResume dell'attività controllo della superficie vista in modo che con il metodo ri-istanzia il SurfaceView e quindi imposta con setContentView. Il problema di questo approccio è che è necessario ricaricare qualsiasi stato che il vostro SurfaceView stava prendendo cura di.

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

        setContentView(new MyCustomSurfaceView(this));
    }

    @Override
    protected void onResume() {
        super.onResume();
        setContentView(new MyCustomSurfaceView(this));
    }

Ho cercato di commentare sul risposta accettata sopra, ma non ci riuscì, nuovo a questo. Non credo che si dovrebbe chiamare la propria start / metodi di filo di arresto sia dal SurfaceView e Activity. Questo si tradurrà in avvio / arresto il filo doppiamente, e non è possibile avviare un thread più di una volta. Basta chiamare i vostri metodi da onPause della attività e onResume. Si chiamano quando si esce e ri-entrare l'applicazione in modo da questo farà in modo che i vostri stati vengono gestiti correttamente. surfaceDestroyed non è sempre chiamato, che mi ha incasinato per un po '.

Se si utilizza questo metodo assicurarsi di verificare la presenza di una superficie valida nel codice corsa prima di lavorare con la tela, perché l'attività inizierà il filo nel onResume prima che la superficie è disponibile.

        while (_run) {
            if (_surfaceHolder.getSurface().isValid()) {
                ...
            }
        } //end _run

Questo è quello che ho usato. L'applicazione non si blocca ora.

Visualizza Classe:

holder.addCallback(new Callback() {

        public void surfaceDestroyed(SurfaceHolder holder) {
            gameLoopThread.setRunning(false);
            gameLoopThread.stop();
        }

        public void surfaceCreated(SurfaceHolder holder) {
            gameLoopThread.setRunning(true);
            gameLoopThread.start();

        }

Nel GameLoopThread:

private boolean running = false;

public void setRunning(boolean run) {
    running = run;
}
@Override
public void run() {
    long ticksPs=1000/FPS;
    long startTime;
    long sleepTime;

while(running){
        Canvas c = null;
        startTime=System.currentTimeMillis();
        try {
            c = view.getHolder().lockCanvas();
            synchronized (view.getHolder()) {

                view.onDraw(c);

            }

        } finally {

            if (c != null) {
                view.getHolder().unlockCanvasAndPost(c);
            }

        }
        sleepTime=ticksPs-(System.currentTimeMillis()-startTime);
        try{

            if(sleepTime>0){
                sleep(sleepTime);
            }
            else
                sleep(10);
        } catch(Exception e){}
}

}

Spero che sarà di aiuto.

Si dovrebbe usare l'Attività onPause () e onResume () metodi.

In primo luogo, in surfaceCreated (), avviare il thread. Inoltre, in onResume (), assicurarsi che il filo non è già avviato (mantenere una variabile all'interno il filo o qualcosa del genere). Poi, se non è in esecuzione, impostarlo come correre. in onPause (), mettere in pausa il filo. In surfaceDestroyed, sospendere nuovamente il filo.

Un'altra soluzione per questo problema una buona nota. Purtroppo, non capisco perché funziona - è venuto fuori per caso. Ma funziona bene per me ed è facile da implementare: non ignorando di di Activity onPause(), onResume(), onStart(), onStop(), né la scrittura di metodi filo speciali (come resume(), pause()) sono obbligatori.

Il requisito speciale è quello di mettere tutte le variabili che cambiano in qualcosa di diverso di rendering classe thread.

I principali punti da aggiungere per rendere-thread classe:

class RefresherThread extends Thread {
    static SurfaceHolder threadSurfaceHolder;
    static YourAppViewClass threadView;
    static boolean running;

    public void run (){
        while(running){
            //your amazing draw/logic cycle goes here
        }
    }
}

Ora, le cose importanti su YourAppViewClass:

class YourAppViewClass extends SurfaceView implements SurfaceHolder.Callback  {
    static RefresherThread surfaceThread;

    public YourAppViewClass(Activity inpParentActivity) {
        getHolder().addCallback(this);
        RefresherThread.threadSurfaceHolder = getHolder();
        RefresherThread.threadView = this;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        surfaceThread = new RefresherThread();
        surfaceThread.running=true;
        surfaceThread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        surfaceThread.running=false;
        try {
            surfaceThread.join();
        } catch (InterruptedException e) {  
        }               
    }
}

Due blocchi di codice di cui sopra non sono classi full-scritto, ma mera nozione di quali comandi in cui sono necessari metodi. Si noti inoltre che ogni ritorno a invoca app surfaceChanged().

Ci scusiamo per tale risposta di consumo dello spazio. Spero che funzionerà correttamente e sarà di aiuto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top