2012-04-28 10 views
8

Ho un'estensione SurfaceView in cui sono implementate le sue strutture come nell'esempio Lunar Lander. Cioè, il metodo del disegno Threadrun() è essenzialmente:null Canvas in SurfaceView Thread, nonostante l'arresto Thread in surfaceDestroyed() - solo su Android 4/ICS

public void run() { 
    while (mRun) { 
     Canvas c; 
      try { 
       c = mSurfaceHolder.lockCanvas(); 
       synchronized (mSurfaceHolder) { 
        doDraw(c); // Main drawing method - not included in this code snippet 
       }         
      } 

      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) { 
        mSurfaceHolder.unlockCanvasAndPost(c); 
       } 
      } 
     } 
    } 
} 

E la Thread venga chiusa correttamente quando la superficie è distrutto:

public void surfaceDestroyed(SurfaceHolder holder) { 
    // we have to tell thread to shut down & wait for it to finish, or else 
    // it might touch the Surface after we return and explode 
    boolean retry = true; 
    thread.setRunning(false); 
    while (retry) { 
     try { 
      thread.join(); 
      retry = false; 
     } 
     catch (InterruptedException e) { 
     } 
    } 
} 

Sui dispositivi solito ho testato a data (HTC Desiderio, Desiderio HD e Archos 101 che tra loro hanno OS 2.2 e 2.3.3 se ricordo bene) non c'è mai stato un problema con quanto sopra. Cioè, quando la superficie viene distrutta perché l'utente esce dal Activity o un altro Activity viene invocato in cima, il codice all'interno di surfaceDestroyed() assicura sempre che mSurfaceHolder.lockCanvas() non venga mai chiamato perché restituisca null.

La differenza che ho trovato sul mio nuovo HTC One X su cui è in esecuzione Android 4/ICS, tuttavia, è che durante la chiamata al metodo surfaceDestroyed() (ovvero il codice all'interno di tale metodo è ancora in esecuzione) verrà fornito il mio disegno Thread una tela null da mSurfaceHolder.lockCanvas(). Ciò ovviamente causerebbe un arresto anomalo dell'applicazione. Sul mio One X, questo accadrà ogni singola volta la superficie è distrutta - sia che si tratti a causa ruotando il telefono, retromarcia dal Activity, ecc

Sono confuso su questo perché ero sotto la impressione che mSurfaceHolder.lockCanvas() dovrebbe restituire un numero non nullCanvas fino al surfaceDestroyed() ha effettivamente terminato. In effetti, questo è quello che dice il Javadoc:

This is called immediately before a surface is being destroyed. After returning from this call, you should no longer try to access this surface. If you have a rendering thread that directly accesses the surface, you must ensure that thread is no longer touching the Surface before returning from this function.

La mia soluzione per ora è quello di verificare solo per null. Funziona bene:

if(c != null){ 
    doDraw(c); // Main drawing method - not included in this code snippet 
} 

Ma, qualche idea per cui dovrei improvvisamente farlo per Android 4/ICS?

+0

Restituire "null" è un modo per evitare di toccare anche la superficie, quindi quello che si dovrebbe fare va bene. Idk perché o cosa è cambiato nell'implementazione sottostante. – zapl

+0

Grazie, zapl. Sì, è davvero bizzarro. A meno che non ci sia qualcosa di strano che sto facendo per causare questo apparente cambiamento nell'implementazione, sicuramente molte applicazioni basate su SurfaceView moriranno su ICS. – Trevor

+0

Qualcuno trova un "ufficiale" perché perché ho ricevuto questo errore anche su dispositivi o emulatori ICS. – Lenciel

risposta

5

Giusto per elaborare il mio commento, sembra che ci sia un ticket di bug aperto per questo cambiamento di comportamento, che potete trovare qui: http://code.google.com/p/android/issues/detail?id=38658. Se ti colpisce, potrebbe valerne la pena, solo così aumenta la sua importanza un po '!

Personalmente, l'ho visto io stesso, solo utilizzando l'esempio del lander lunare che viene fornito con l'ultimo SDK Android. Lo metto sul mio HTC Sensation XE (4.0.3) e sul cambio di orientamento, ottengo alcune tele nulle che vengono restituite prima che la superficie venga distrutta.

Quindi la soluzione che utilizzo è solo per ricontrollare che il canvas non è null prima di passarlo ai miei metodi di aggiornamento e rendering.

Andy

+0

Interessante! Anch'io ho avuto questo problema, e mi sono aggirato in un modo simile, l'unica cosa diversa che ho messo il mio controllo per la tela nulla in realtà all'interno del mio metodo di disegno :-) – Zippy

+0

Ho appena trovato questo dopo aver lottato con lo stesso problema per giorni! Molte grazie! Ho pensato di verificare la nullità, ma ho continuato a cercare un'altra soluzione, dal momento che sembra un po 'disordinata controllare null, ma suppongo che dobbiamo farlo in questo modo. –

0

Ho fatto un piccolo esperimento. Ho messo alcune bandiere di notifica in surfaceDestroyed() metodo come questo:

public void surfaceDestroyed(SurfaceHolder holder) { 
Log.i("i","i have started"); 
...[the code]... 
Log.i("i","i have finished"); 
} 

Quello che ho scoperto, è il fatto che il nullPionterExceptionOccurs si verifica dopo la prima bandiera, ma prima che la seconda.Il più possibile per la tua domanda è che blocchi la tela quando la vecchia vista è raggiungibile, la disegni su di essa ma nel frattempo lo schermo cambia. Quindi, quando sblocchi la tela non c'è posto per mostrare i risultati, l'errore è causato.

P.S. Gioca un po 'con Lunar Lander da codici Android di esempio e prova a ruotare lo schermo mentre il gioco funziona. Quando si verifica l'errore, guarda come stava per disegnare la bitmap di sfondo. Scoprirai che l'orientamento dello schermo è cambiato, ma il programma ha tentato di disegnare una bitmap come se nulla fosse accaduto.