我在线程上有一个surfaceview更新/在画布上绘制。我认为由于surfaceHolder.lockCanvas(null),UI线程(用户交互)和surfaceview线程没有交互。我认为这些线程的逻辑是同步的。但是,当我使用用户触摸事件和surfaceview线程更新逻辑时,它们似乎在某些情况下同时运行。 请参阅以下日志,更新后应该ACTION_MOVE结束。
由于这个问题,无法从UI线程安全地更新游戏变量(基于用户难事),然后在表面视图上更新画布。如何解决这个问题?是否可以锁定/同步游戏变量,以便我们可以在两个线程之间安全地访问它们?
D/myApp﹕ ACTION_MOVE start
D/myApp﹕ ACTION_MOVE
D/myApp﹕ ACTION_MOVE end
D/myApp﹕ update
**D/myApp﹕ ACTION_MOVE start
D/myApp﹕ ACTION_MOVE
D/myApp﹕ update
D/myApp﹕ ACTION_MOVE end**
D/myApp﹕ ACTION_MOVE start
D/myApp﹕ ACTION_MOVE
D/myApp﹕ ACTION_MOVE end
D/myApp﹕ update
D/myApp﹕ ACTION_MOVE start
D/myApp﹕ ACTION_MOVE
D/myApp﹕ ACTION_MOVE end
D/myApp﹕ update
D/myApp﹕ ACTION_MOVE start
D/myApp﹕ ACTION_MOVE
D/myApp﹕ ACTION_MOVE end
D/myApp﹕ update
D/myApp﹕ update
D/myApp﹕ ACTION_MOVE start
D/myApp﹕ ACTION_MOVE
D/myApp﹕ ACTION_MOVE end
D/myApp﹕ ACTION_MOVE start
D/myApp﹕ ACTION_MOVE
D/myApp﹕ ACTION_MOVE end
D/myApp﹕ update
public class GameThread extends Thread {
private SurfaceHolder surfaceHolder;
private GameViewSurface surface;
private boolean running = false;
private Context GameContext;
private int gameState;
public static final int STATE_PAUSE = 1;
public static final int STATE_RUNNING = 2;
private int GameSurfaceWidth = 1;
private int GameSurfaceHeight = 1;
public GameThread(SurfaceHolder holder, Context context, GameViewSurface GameSurface) {
surfaceHolder = holder;
surface = GameSurface;
GameContext = context;
}
public void setRunning(boolean run) {
running = run;
}
public void startGame() {
synchronized (surfaceHolder) {
setState(STATE_RUNNING);
}
}
public void pause() {
synchronized (surfaceHolder) {
if (gameState == STATE_RUNNING) setState(STATE_PAUSE);
}
}
public synchronized void restoreState(Bundle savedState) {
synchronized (surfaceHolder) {
setState(STATE_PAUSE);
}
}
public void setState(int stateToSet) {
synchronized (surfaceHolder) {
// TODO Message Handling
}
}
public Bundle saveState(Bundle map) {
synchronized (surfaceHolder) {
if (map != null) {
}
}
return map;
}
public void setSurfaceSize(int width, int height) {
// synchronized to make sure these all change atomically
synchronized (surfaceHolder) {
GameSurfaceWidth = width;
GameSurfaceHeight = height;
}
}
public void unpause() {
setState(STATE_RUNNING);
}
@Override
public void run() {
while (running) {
Canvas canvas=null;
try {
if(!surfaceHolder.getSurface().isValid())
continue;
surface.update();
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
surface.doDraw(canvas);
}
} catch(Exception p){
Log.d("myApp", p.getMessage());
}finally {
if (canvas != null) {
try {
surfaceHolder.unlockCanvasAndPost(canvas);
}catch (Exception e){}
}
}
}
}
}
public class GameViewSurface extends SurfaceView implements SurfaceHolder.Callback {
public GameViewSurface(Context context) {
super(context);
getHolder().addCallback(this);
gameThread = new GameThread(getHolder(), context, this);
setFocusable(true);
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
if (!hasWindowFocus) gameThread.pause();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// gameThread.setSurfaceSize(width, height);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
gameThread.setRunning(true);
gameThread.start();
createGameHandler();
}
public void createGameHandler()
{
gameData = new Game(backColor);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameThread.setRunning(false);
while (retry) {
try {
gameThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
//@Override
public void doDraw(Canvas canvas) {
canvas.drawBitmap(gameDataGen.board, mPosX, mPosY, null);
}
public void playSound() {
if (enableSound && !sp.isPlaying())
sp.start();
}
public void update()
{
try {
if (waitTillLoad || isExit || finished)
return;
if (gameData != null) {
gameData.update();
}
}catch (Exception ex)
{
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
viewHeight = h;
viewWidth = w;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(waitTillLoad ||finished || !isTouchable || bypassParent)
return super.onTouchEvent(event);
scaleDetector.onTouchEvent(event);
boolean handled = false;
int xTouch;
int yTouch;
int actionIndex = event.getActionIndex();
// get touch event coordinates and make transparent circle from it
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
Log.d("myApp", "ACTION_DOWN start");
xTouch = (int) event.getX(0);
yTouch = (int) event.getY(0);
if (scaleDetector.isInProgress()) {
break;
}
mLastTouchX = xTouch;
mLastTouchY = yTouch;
this.selectedIndex = findClusterFromXY(xTouch, yTouch);
if (selectedIndex >= 0) {
Log.d("myApp", "ACTION_DOWN selectedIndex >");
gameData.setTopIndex(this.selectedIndex);
gameData.clearMotionData();
gameData.addMotionEvent(new Point(xTouch, yTouch));
}
handled = true;
Log.d("myApp", "ACTION_DOWN end");
break;
case MotionEvent.ACTION_MOVE:
xTouch = (int) event.getX();
yTouch = (int) event.getY();
gameData.moveStart = true;
gameData.moveEnd = false;
gameData.setMovingClusterData(gameData.currentCluster);
gameData.addMotionEvent(new Point(xTouch, yTouch));
_previousMouseX = xTouch;
_previousMouseY = yTouch;
Log.d("myApp", "ACTION_MOVE end");
gameData.moveEnd = true;
break;
case MotionEvent.ACTION_UP:
Log.d("myApp", "ACTION_UP start");
handled = processActionUp(event, true);
handled = true;
Log.d("myApp", "ACTION_UP end");
break;
}
return super.onTouchEvent(event);
}
protected void onComplete() {
if(solveFragment != null)
{
finished= true;
solveFragment.onComplete();
}
}
public GameThread getThread() {
return gameThread;
}
}
答案 0 :(得分:0)
以下页面中列出的方法为我工作。
http://www.rbgrn.net/content/342-using-input-pipelines-your-android-game
使用正确锁定的管道(队列)来存储当前用户事件。然后在Surfaceview线程执行时逐个处理此列表中的项目。