1

У меня есть фоновая анимация, нарисованная на SurfaceView другим потоком в моем приложении. Кажется, что анимация работает хорошо, за исключением того, что экран вращается. Тогда основной поток будет иногда висел в течение нескольких секунд. Используя DDMS, я вижу, что основной поток вызывает Object.wait(), я не понимаю, где и почему он это делает.MainThread зависает, когда анимация SurfaceView повернута

Ниже приведен некоторый сокращенный код, при необходимости полный источник может быть найден на github по адресу https://github.com/GavinDBrown/Amazing.

Основная деятельность:

public class StartMenu extends Activity { 

    /** A handle to the thread that's running the Game Of Life animation. */ 
    private GOLThread mGOLThread; 

    /** A handle to the View in which the background is running. */ 
    private GOLView mGOLView; 

public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
       setContentView(R.layout.game_of_life_background); 
     startGOLBackground(); 
    } 
    private void startGOLBackground() { 
     // get handles to the GOLView and it's GOLThread 
     mGOLView = (GOLView) findViewById(R.id.game_of_life_background); 
     mGOLThread = new GOLThread(mGOLView.getHolder()); 
     mGOLView.setThread(mGOLThread); 
     mGOLThread.start(); 
    } 

    private void stopGOLBackground() { 
     if (mGOLThread != null) { 
      mGOLThread.halt(); // stop the animation if it's valid 
      boolean retry = true; 
      while (retry) { 
       try { 
        mGOLThread.join(); 
        retry = false; 
       } catch (InterruptedException e) { 
       } 
      } 
      mGOLThread = null; 
      mGOLView = null; 
     } 
    } 

    @Override 
    public void onResume() { 
     super.onResume(); 
     mGOLThread = mGOLView.getThread(); 
      mGOLThread.unpause(); 
    } 

    @Override 
    public void onPause() { 
     super.onPause(); 
     mGOLThread.pause(); 
    } 

    @Override 
    protected void onDestroy() { 
     super.onDestroy(); 
     stopGOLBackground(); 
    } 

} 

SurfaceView:

public class GOLView extends SurfaceView implements SurfaceHolder.Callback { 
    /** The thread that actually draws the animation */ 
    private GOLThread thread; 
    SurfaceHolder surfaceHolder; 

    public GOLView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     SurfaceHolder holder = getHolder(); 
     holder.addCallback(this); 

    } 
    @Override 
    public void onWindowFocusChanged(boolean hasWindowFocus) { 
     if (hasWindowFocus){ 
      thread.unpause(); 
     } else { 
      thread.pause(); 
     } 

    } 
     public void surfaceChanged(SurfaceHolder holder, int format, int width, 
      int height) { 
     thread.setSurfaceSize(width, height); 
    } 
     @Override 
    public void surfaceDestroyed(SurfaceHolder holder) { 
     thread.pause(); 
    } 

    @Override 
    public void surfaceCreated(SurfaceHolder holder) { 
     thread.unpause(); 
    } 
} 

И, наконец, тема:

public class GOLThread extends Thread { 

    private GameOfLife gameOfLife; 
    private final Object GOLLock = new Object(); 
     private int mCanvasHeight; 
     private int mCanvasWidth; 
     private SurfaceHolder mSurfaceHolder; 

     public GOLThread(SurfaceHolder surfaceHolder) { 
     mSurfaceHolder = surfaceHolder; 
    } 

     @Override 
    public void start() { 
     synchronized (mSurfaceHolder) { 
      stopped = false; 
      mSurfaceHolder.notify(); 
     } 
     super.start(); 
    } 

     public void halt() { 
     synchronized (mSurfaceHolder) { 
      paused = true; 
      stopped = true; 
      mSurfaceHolder.notify(); 
     } 
    } 

     public void pause() { 
     synchronized (mSurfaceHolder) { 
      paused = true; 
     } 
    } 

     public void unpause() { 
     synchronized (mSurfaceHolder) { 
      paused = false; 
      mSurfaceHolder.notify(); 
     } 
    } 

    public Bundle saveState(Bundle outState) { 
     synchronized (GOLLock) { 
      if (outState != null) { 
       outState.putParcelable(GAME_OF_LIFE_ID, gameOfLife); 
      } 
     } 
     return outState; 
    } 

    public synchronized void restoreState(Bundle savedState) { 
     synchronized (GOLLock) { 
      gameOfLife = (GameOfLife) savedState.getParcelable(GAME_OF_LIFE_ID); 
     } 
    } 
    @Override 
    public void run() { 
     while (!stopped) { 
      while (paused && !stopped) { 
       try { 
        synchronized (mSurfaceHolder) { 
         mSurfaceHolder.wait(100L); 
        } 

       } catch (InterruptedException ignore) { 
       } 
      } 
      // Check if thread was stopped while it was paused. 
      if (stopped) 
       break; 

      beforeTime = System.nanoTime(); 
      Canvas c = null; 
      try { 
       c = mSurfaceHolder.lockCanvas(); 
       synchronized (GOLLock) { 
        if (gameOfLife != null) { 
         gameOfLife.drawAndUpdate(c); 
        } else { 
         pause(); 
        } 

       } 
      } finally { 
       if (c != null) { 
        mSurfaceHolder.unlockCanvasAndPost(c); 
       } 
      } 

      sleepTime = FRAME_DELAY 
        - ((System.nanoTime() - beforeTime)/1000000L); 

      try { 
       // actual sleep code 
       if (sleepTime > 0 && !stopped && !paused) { 
        synchronized (mSurfaceHolder) { 
         mSurfaceHolder.wait(sleepTime); 
        } 
       } 
      } catch (InterruptedException ex) { 

      } 
     } 
    } 

    public void setSurfaceSize(int width, int height) { 
     synchronized (GOLLock) { 
      if (mCanvasWidth != width || mCanvasHeight != height) { 
       mCanvasWidth = width; 
       mCanvasHeight = height; 
       // reset the GOL 
       if (mCanvasWidth > 0 && mCanvasHeight > 0) { 
        gameOfLife = new GameOfLife(); 
        gameOfLife.init(mCanvasWidth, mCanvasHeight); 
       } 
      } 
     } 
    } 
} 

ответ

0

Проблема GOLThread звонит SurfaceHolder.lockCanvas() и получить нуль в результате слишком довольно часто.

Из документов на SurfaceHolder.lockCanvas()

Если вы вызываете этот раз, когда поверхность не готова (до Callback.surfaceCreated или после Callback.surfaceDestroyed), ваши звонков будет задушил на медленную скорость в чтобы избежать потребления CPU.

Итак, ОС была дросселированием вызовов, поставив мои потоки спать.

Я установил его обновления кода в GOLThread.run() иметь

Canvas c = null; 
    try { 
     c = mSurfaceHolder.lockCanvas(); 
     if (c == null) { 
      // Pause here so that our calls do not get throttled by the 
      // OS for calling lockCanvas too often. 
      pause(); 
     } else { 
      synchronized (GOLLock) { 
       if (gameOfLife != null) { 
        gameOfLife.drawAndUpdate(c); 
       } else { 
        pause(); 
       } 
      } 
     } 
    } 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); 
     } 
    } 
Смежные вопросы