Android 暂停/恢复后无法绘制
我正在写我的第一个android游戏。它几乎在各个方面都工作得很好,但是当屏幕超时开始时,我按下按钮返回游戏,我发现游戏状态的图像冻结了。主线程似乎仍然处于活动状态-我可以告诉你这一点,因为当我在屏幕上拖动某些对象时,会出现一组声音,这些声音仍然表现得好像一切正常(除了除了冻结的屏幕之外,我什么也看不到)。如果我选择一个菜单来显示“high scores”活动,然后退出游戏,我发现一切都很好,可以继续所有图形正常工作 我不太确定诊断此问题需要包含代码的哪些部分,但我猜,如果需要,我将添加更多内容: 编辑:根据Brigham的建议,我对代码进行了一些修改,但症状相同 编辑:我注意到,如果我单击一个按钮发起“查看高分”活动,则会调用surfaceDestroyed()。但是当屏幕超时开始时,不会调用surfaceDestroyed() 编辑:对于这个问题,有很多观点都没有找到解决方案——我想这个问题一定很棘手——或者可能没有在我发布的代码片段中透露出来。关于如何诊断此问题的一些建议将非常有用 编辑:我现在已将整个(简化的)项目放在simple.zip中,并将其放到web上:-关键代码都在simple.java文件中Android 暂停/恢复后无法绘制,android,Android,我正在写我的第一个android游戏。它几乎在各个方面都工作得很好,但是当屏幕超时开始时,我按下按钮返回游戏,我发现游戏状态的图像冻结了。主线程似乎仍然处于活动状态-我可以告诉你这一点,因为当我在屏幕上拖动某些对象时,会出现一组声音,这些声音仍然表现得好像一切正常(除了除了冻结的屏幕之外,我什么也看不到)。如果我选择一个菜单来显示“high scores”活动,然后退出游戏,我发现一切都很好,可以继续所有图形正常工作 我不太确定诊断此问题需要包含代码的哪些部分,但我猜,如果需要,我将添加更多内容
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;
}
}
}
在
onPause
中调用setRunning(false)
后,您的MicksThreadThing
实例正在死亡,因为while
循环条件变为false,并且run
方法退出
您应该在
onResume
中创建一个新的MicksThreadThing
,或者使用某种同步来暂停run方法,直到用户恢复游戏。我建议使用CountDownLatch
类来完成此任务。它将允许您的run
方法调用countDownLatch.await()
,此时线程将暂停,直到您在onResume
方法中调用countDownLatch.countDown()
此外,this\u thread\u当前处于活动状态
变量应设置为volatile
:
private volatile boolean this_thread_is_currently_active = false;
在
onPause
中调用setRunning(false)
后,您的MicksThreadThing
实例正在死亡,因为while
循环条件变为false,并且run
方法退出
您应该在
onResume
中创建一个新的MicksThreadThing
,或者使用某种同步来暂停run方法,直到用户恢复游戏。我建议使用CountDownLatch
类来完成此任务。它将允许您的run
方法调用countDownLatch.await()
,此时线程将暂停,直到您在onResume
方法中调用countDownLatch.countDown()
此外,this\u thread\u当前处于活动状态
变量应设置为volatile
:
private volatile boolean this_thread_is_currently_active = false;
你的问题是你的
surf_持有者
,因为你的线程成员是错误的。当您的活动进入睡眠状态,然后返回时,surface 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
}
}
你的问题是你的
surf_持有者
,因为你的线程成员是错误的。当您的活动进入睡眠状态,然后返回时,surface 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
}
}
试试这个:
在曲面销毁方法中添加以下内容:
支架.解锁CanvasandPost(主线.画布)
编辑:
试试这个:
在曲面销毁方法中添加以下内容:
支架.解锁CanvasandPost(主线.画布)
编辑:
“您应该在onResume中创建一个新的MicksThreadThing”-但我认为这是我已经在做的:if(micks_thread_thing==null){mpt.get_a_new_thread_all_over_()}只有在某个点设置为null时才会发生这种情况。在哪里设置为空?我现在看到了。确保调用了
surfaceDestroyed
,并在onResume中添加一条log语句,以确保micks\u thread\u thing
为空。执行setRunning(false)后,micks\u thread\u thing对象是否仍然存在(尽管未运行)-即,我需要重新运行它还是需要创建一个新的?它仍然存在,但一旦运行方法退出,线程对象就不能再次启动。您必须创建一个新的线程。“您应该在onResume中创建一个新的MicksThreadThing”-但我认为这是我已经在做的:if(micks_thread_thing==null){mpt.get_a_new_thread_all_over_()}只有在某个点设置为null时才会发生这种情况。在哪里设置为空?我现在看到了。确保调用了surfaceDestroyed
,并在onResume中添加一条log语句,以确保micks\u thread\u thing
为null。setRun之后