2010-08-20 9 views
14

devam ama bunu devam ettirmek zaman ben parçacığı zaten başlatılmış bir hata olsun. Uygulama arka plana gidip ön plana geri döndüğünde ne yapmanın doğru yolu nedir? Ben etrafında tinkered ve çökmesini olmadan geri gelmek için uygulamayı almayı başardı ... ama SurfaceView artık anthing çizmiyor. Benim kodum:nasıl durdurabilir ve ben SurfaceView kurulum ve çalıştırıyorum bir SurfaceView parçacığı

@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; 
    } 

cevap

11

kolay çözüm sadece öldürmek ve iplik yeniden oluşturmaktır. Yöntemleri yeniden oluştur() - iş parçacığı nesnesini oluşturur ve başlatır - ve duraklatır() - SurfaceView sınıfınızdaki parçacığı öldürür ve iş parçacığını başlatmak ve durdurmak için bunları SurfaceCreated ve surfaceDestroyed öğelerinden çağırır.

Artık SurfaceView'ı çalıştıran Etkinlikte, SurfaceView'daki Activity() ve pause() yöntemlerini Activity() ve onPause() üzerindeki Activity (veya parçaların) öğelerinden de çağırmanız gerekecektir. Zarif bir çözüm değil, ama işe yarayacak.

+0

Ur fikrini seviyorum, kolay bir şeyler bulmak için uğraşıyorum. "SurfaceDestroyed" her zaman çağrılmadığı için "onPause" dır. Sadece "güç" düğmesine basmak gibi geri dönün. Bu yüzden seçiminizin gerçekten iyi olduğunu düşünüyorum. –

0

Activity onPause() ve onResume() yöntemlerini kullanmalısınız.

Birincisi, surfaceCreated içinde(), başlık açın. Ayrıca, onResume() öğesinde, iş parçacığının önceden başlatılmamış olduğundan emin olun (iş parçacığının içinde bir değişken bulundurun). Ardından çalışmıyorsa, tekrar çalışacak şekilde ayarlayın. onPause() 'da, iş parçacığını duraklatın. SurfaceDestroyed'da, ipliği tekrar duraklatın.

+0

Ben düzgün uygun yerlere setRunning kullanacak şekilde ayarlanmış, ancak thread.setRunning (doğru) olmasına rağmen benim onResume benim SurfaceView boş olduğunda onun geri ön planda. Konu hala var, ana ekrana gittiğimde uygulamayı kilitlemiyor ve eğer başka bir thread.start() deneyinse, iş parçacığı başlatıldığını söyleyen hatayı alıyorum. Herhangi bir fikir? – jfisk

+0

Sadece bir kez bir iş parçacığı başlatabilirsiniz. SetRunning yönteminizin içte ne yaptığını bilmiyorum, ancak Android'de bir iş parçacığını düzgün şekilde durdurmanın tek yolu, onun run() yönteminden dönmesine izin vermektir. Bu noktadan sonra, yeni bir iplik nesnesi oluşturmanız gerekir. Bu iş parçacığını bir daha kullanamazsın. İpliği 'duraklatmak' istiyorsanız, wait() ve notifyAll() 'i kullanmalısınız. Google, örneğin, bu konuda doğru bir anlayış. – Moncader

2
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... 
      } 
     } 
    } 
+0

Yüzey görünümünü öğreniyorum, ancak while döngüsünde neden bir break stamenet kullanmadığınızı anlayamıyorsunuz. Programım onResume() sırasında dondu, ama mola verdikten sonra agin başladı, açıkça yeni bir thread oluşturuldu. Bu benim kodum ve soru: http://stackoverflow.com/questions/19200972/android-surfaceview-cant-resume-activity-from-pause – Luther

+0

Thread.join() hakkında daha fazla bilgi edinin. –

+0

Aktivite 'onPause' ** her zaman ** kendi başına" SurfaceDestroyed "işlevini çağırmayacaktır. Yani bu tek başına sorunu çözmeyecek. Bu soruya bakın http://stackoverflow.com/q/11495842/1180117 – Kiran

0

bu iyi bilinen problem için başka bir çözüm. Ne yazık ki, neden işe yaradığını anlamıyorum - yanlışlıkla çıktı. Ama benim için iyi çalışır ve uygulamak kolaydır: özel diş yöntemlerinin Activity 'ın onPause(), onResume(), onStart(), onStop(), ne de yazı hiçbir ağır basan (gibi resume(), pause()) gereklidir.

Özel gereksinimi iplik sınıfını render dışında bir bütün değişen değişkenleri koymaktır.

Ana noktaları eklemek hale iplik sınıfı:

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 
     } 
    } 
} 

Şimdi, önemli şeyler YourAppViewClass hakkında:

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) { 
     }    
    } 
} 

İki kod blokları yukarıda değil tam yazılı sınıfları, ancak salt kavramı vardır Hangi metotlara ihtiyaç duyulduğunu belirler. Ayrıca, uygulamaya her dönüşün surfaceChanged()'u çağırdığını unutmayın.

böyle uzay tüketen cevap için üzgünüm. Umarım düzgün çalışır ve yardımcı olur.

+0

Etkinlik 'onPause '** her zaman ** kendi yüzeyinde" SurfaceDestroyed "komutunu kullanmayacaktır. Yani bu problemi çözmeyecek. Bu soruya bir bakın http://stackoverflow.com/q/11495842/1180117 – Kiran

1

yukarıda kabul edilen cevap üzerine yorum çalıştı ama bu yeni, yapamadım. SurfaceView ve Activity öğelerinizden start/stop ipliği yöntemlerini çağırmanız gerektiğini düşünmüyorum. Bu, ipliği iki kez başlatmaya/durdurmaya neden olur ve bir iş parçacığını bir kereden fazla başlatamazsınız. Yöntemlerinizi yalnızca Etkinliğin onPause ve onResume'den arayın. Uygulamadan çıkarken ve tekrar girerken çağrılırlar, böylece bu, durumlarınızın gerektiği gibi ele alındığından emin olur. SurfaceDestroyed her zaman çağrılmıyor, bu da beni bir süredir karıştırdı.

Bu yöntemi kullanırsanız, tuvalinizle çalışmadan önce çalışma kodunuzda geçerli bir yüzey olup olmadığını kontrol edin; çünkü Etkinlik, yüzey mevcut olmadan önce OnResume'daki ipliği başlatır.

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

bir geri çekilme yorumu – tjb

2

bulduk en iyi yolu Bu yöntemle SurfaceView yeniden başlatır, böylece yüzey görünümü kontrol aktivitesinin onResume yöntemini geçersiz kılmak için ve daha sonra setContentView ile ayarlar. Bu yaklaşımdaki problem, SurfaceView'inizin ilgilendiği herhangi bir durumu yeniden yüklemeniz gerektiğidir.

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

     setContentView(new MyCustomSurfaceView(this)); 
    } 

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

Bu benim için hile yaptı. Sadece onResume'de denediğimde, işe yaramadı. – sivi

1

Kullandığım budur. Uygulama şimdi çöker.

Görünüm Sınıfı: GameLoopThread yılında

holder.addCallback(new Callback() { 

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

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

     } 

:

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){} 
} 

} 

Ben yardımcı olacağını umuyoruz.

+0

Aktivite 'onPause' ** her zaman ** kendi başına“ SurfaceDestroyed ”ifadesini kullanmayacaktır. Yani bu problemi çözmeyecek. Bu soruya bakın http://stackoverflow.com/q/11495842/1180117 – Kiran

4

Bu hata, oldukça ünlü olan ay lander böceği ile ilgili gibi görünüyor (üzerinde bir Google araması yapın). Tüm bu süreden sonra ve birkaç android sürümü yayınlandıktan sonra, hata hala var ve hiç kimse onu güncellemekten rahatsız oldu. ben bu az kod dağınıklığı ile çalışmak bulduk:

public void surfaceCreated(SurfaceHolder holder) {  
      if (thread.getState==Thread.State.TERMINATED) { 
       thread = new MainThread(getHolder(),this); 
      } 
      thread.setRunning(true); 
      thread.start(); 
    } 
+0

Etkinlik 'onPause '** her zaman ** kendi yüzeyinde" SurfaceDestroyed "işlevini kullanmayacaktır. Yani bu problemi çözmeyecek. Bu soruya bakın http://stackoverflow.com/q/11495842/1180117 – Kiran