我的应用程序中的另一个线程将一个背景动画绘制到SurfaceView上。除了旋转屏幕外,动画似乎运行良好。然后主线程有时会挂起几秒钟。使用DDMS我看到主线程正在调用Object.wait(),我不明白它在哪里或为什么这样做。
下面是一些缩写代码,如果需要,可以在https://github.com/GavinDBrown/Amazing的github上找到完整的源代码。
主要活动:
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 :(得分:0)
问题是GOLThread正在调用SurfaceHolder.lockCanvas()
并且过于频繁地获取null。
来自SurfaceHolder.lockCanvas()
如果在Surface未准备好时(之前)重复调用此方法 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);
}
}