在onPause / onResume之后无法绘制

时间:2012-01-11 15:01:22

标签: android

我正在写我的第一个安卓游戏。它在几乎所有方面都运行良好,但是当屏幕超时开始并按下按钮返回游戏时,我发现游戏状态的图像已冻结。看起来主线程仍然存在 - 我可以说这是因为我在屏幕上拖动某些物体时会发出一组声音,声音仍然表现得好像一切正​​常(除了我看到的除了冷冻的屏幕)。如果我然后选择一个菜单来显示“高分”活动,然后退回到游戏,我发现一切都很好,并且可以继续正常工作的所有图形。

我不太确定我的代码的哪些部分需要包含以便诊断此问题,但这是我的猜测 - 如果请求我会添加更多:

编辑:根据布里格姆的建议,我已经为代码做了几个修改,但症状是一样的。

编辑:我注意到,如果我点击按钮发起“查看高分”活动,则会调用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;
        }
    }
}

3 个答案:

答案 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();
        }

    }
}