我正在写我的第一个安卓游戏。它在几乎所有方面都运行良好,但是当屏幕超时开始并按下按钮返回游戏时,我发现游戏状态的图像已冻结。看起来主线程仍然存在 - 我可以说这是因为我在屏幕上拖动某些物体时会发出一组声音,声音仍然表现得好像一切正常(除了我看到的除了冷冻的屏幕)。如果我然后选择一个菜单来显示“高分”活动,然后退回到游戏,我发现一切都很好,并且可以继续正常工作的所有图形。
我不太确定我的代码的哪些部分需要包含以便诊断此问题,但这是我的猜测 - 如果请求我会添加更多:
编辑:根据布里格姆的建议,我已经为代码做了几个修改,但症状是一样的。
编辑:我注意到,如果我点击按钮发起“查看高分”活动,则会调用surfaceDestroyed()。但是当屏幕超时启动时,surfaceDestroyed()不被调用。
编辑:在没有解决方案的情况下,已经有很多关于这个问题的观点了 - 我认为这个问题必须非常棘手 - 或者在我发布的代码片段中没有透露。关于如何诊断这个问题的一些建议非常有用。
编辑:我现在将整个(简化的)项目放在simple.zip中并将其放在网上:http://dl.dropbox.com/u/2969211/Simple.zip - 关键代码全部在文件中。的java
MicksPanelThing mpt = null;
public MicksThreadThing micks_thread_thing = null;
@Override
protected void onPause() // pair with onResume
{
super.onPause();
allow_draw.set(false);
micks_thread_thing.setRunning(false);
save_state();
}
@Override
protected void onResume() // pair with onPause
{
super.onResume();
get_state();
mpt.get_a_new_thread_all_over_again();
micks_thread_thing.setRunning(true);
allow_draw.set(true);
}
////////////////////////////////////////////////////////////////////////////
public class MicksPanelThing extends SurfaceView implements SurfaceHolder.Callback
{
public MicksPanelThing(Context context)
{
super(context);
}
public void get_a_new_thread_all_over_again()
{
getHolder().addCallback(this);
micks_thread_thing = new MicksThreadThing(getHolder(), this);
setFocusable(true);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
synchronized (micks_thread_thing.getSurfaceHolder())
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
// blah
}
if (event.getAction() == MotionEvent.ACTION_MOVE && selected_node != -1)
{
// blah
}
if (event.getAction() == MotionEvent.ACTION_UP)
{
// blah
}
return true;
}
}
@Override
public void onDraw(Canvas canvas)
{
canvas_width = canvas.getWidth();
canvas_height = canvas.getHeight();
if (allow_draw.get())
{
canvas.drawBitmap(sandy_bitmap, 0, 0, null);
// blah
}
}
public void surfaceCreated(SurfaceHolder holder)
{
micks_thread_thing.setRunning(true);
micks_thread_thing.start();
we_have_a_surface = true;
}
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
micks_thread_thing.setRunning(false);
while (retry)
{
try
{
micks_thread_thing.join();
retry = false;
micks_thread_thing = null;
}
catch (InterruptedException e)
{
// we will try it again and again...
}
}
we_have_a_surface = false;
}
}
class MicksThreadThing extends Thread
{
private SurfaceHolder surf_holder = null;
private MicksPanelThing micks_panel_thing;
private boolean this_thread_is_currently_active = false;
public MicksThreadThing(SurfaceHolder surfaceHolder, MicksPanelThing mpt)
{
surf_holder = surfaceHolder;
micks_panel_thing = mpt;
}
public void setRunning(boolean set_run)
{
this_thread_is_currently_active = set_run;
}
public SurfaceHolder getSurfaceHolder()
{
return surf_holder;
}
@Override
public void run()
{
Canvas c;
while (this_thread_is_currently_active)
{
c = null;
try
{
c = surf_holder.lockCanvas(null);
synchronized (surf_holder)
{
micks_panel_thing.onDraw(c);
}
}
finally
{
if (c != null)
{
surf_holder.unlockCanvasAndPost(c);
}
}
}
}
}
public class Allow_draw
{
private boolean draw;
Allow_draw()
{
draw = false;
}
public void set(boolean x)
{
synchronized (this)
{
draw = x;
}
}
boolean get()
{
synchronized (this)
{
return draw;
}
}
}
答案 0 :(得分:3)
你的问题是你保留你的surf_holder
作为你的线程成员女巫是错误的。当您的活动进入休眠状态然后返回时,表面支架会发生变化,并且它也可能发生在屏幕旋转或其他SurfaceView事件上。因此,最好的做法是每次抽奖时从视图中询问。
@Override
public void run()
{
Canvas c;
while (this_thread_is_currently_active)
{
c = null;
try
{
Final SurfaceHolder holder = getHolder();
c = holder.lockCanvas(null);
if(c != null)// may happen when SurfaceHolder is changing
synchronized (surf_holder)
{
micks_panel_thing.onDraw(c);
}
}
}
finally
{
if (c != null)
{
surf_holder.unlockCanvasAndPost(c);
}
}
}
}
不不不不!你做错了!
删除SurfaceHolder的所有依据,不要将它保存在任何地方,删除你用来获取持有者的功能,也将它从你的线程中删除!只有当您要锁定画布时才执行以下操作:
//inside surfaceView
public void updateView(){
final SurfaceHolder holder = getHolder();
try{
Canvas canvas = holder.lockCanvas();
if(canvas != null){
onDraw(canvas);
}
holder.unlockCanvasAndPost(canvas);
}
catch (Exception e) {
// TODO: handle exception
}
}
答案 1 :(得分:2)
您在MicksThreadThing
中致电setRunning(false)
后,onPause
实例正在消亡,因为while
循环条件为false,并且run
方法退出。< / p>
您应该在MicksThreadThing
中创建新的onResume
,或者使用某种同步来暂停运行方法,直到用户恢复游戏为止。我建议使用CountDownLatch
类来完成此任务。它允许您的run
方法调用countDownLatch.await()
,此时线程会暂停,直到您在countDownLatch.countDown()
方法中调用onResume
。
此外,this_thread_is_currently_active
变量应设为volatile
:
private volatile boolean this_thread_is_currently_active = false;
答案 2 :(得分:0)
尝试一下: 在“ Surface Destroyed”方法中添加以下内容:
holder.unlockCanvasAndPost(MainThread.canvas);
编辑:
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d("SURFACEDESTROYED","TRUE");
boolean retry = true;
while (retry) {
try {
holder.unlockCanvasAndPost(MainThread.canvas);
thread.setRunning(false);
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}