0

I am developing an Android game engine using a SurfaceView to draw Bitmaps on, with a similar framework to LunarLander. My main game engine activity sets up the layout such that it should display a SurfaceView extension class, as shown below:

super.onCreate(savedInstanceState);

    setContentView(R.layout.ss_layout);

    SSSurfaceView ssSurfaceView = (SSSurfaceView)findViewById(R.id.surface_view);
    SSSurfaceView.SSViewThread renderThread =
        (SSSurfaceView.SSViewThread) ssSurfaceView.getThread();    

The findViewById method successfully returns an instance of my SurfaceView extension class, and I am able to run the thread I define within the class, which is created in this method, the class constructor:

public SSSurfaceView(Context context, AttributeSet attributes)
{
    super(context, attributes);

    SurfaceHolder holder = getHolder();
    holder.addCallback(this);

    thread = new SSViewThread(holder, context);
}

The constructor should be obtaining a reference to the associated SurfaceHolder, which it then passes on to the thread constructor. I began having an issue inside the thread run method where holder.lockCanvas(null) was returning null. I read somewhere that this could mean that the surface has not been created, so I added an if statement using a boolean that I set to true in the surfaceCreated callback.

    public void run()
    {
        Canvas canvas = null;
        try
        {
            canvas = holder.lockCanvas(null);
            //I added the condition below
            if(surfaceCreated)
            {
                drawAll(canvas);
            }
        }
        catch(Exception e)
        {
            Log.wtf("SurfaceView", "exception");
        }
        finally
        {
            if(canvas != null)
            {
                holder.unlockCanvasAndPost(canvas);
            }
        }
    }

And here is the callback:

public void surfaceCreated(SurfaceHolder holder)
{
    surfaceCreated = true;
}

And as it turns out, surfaceCreated is never called. My application displays a gray screen and does nothing else, as the drawAll method is never iterated. Here is the layout.xml file to boot:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <com.bostonwalker.sseng.SSSurfaceView
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

EDIT: This isn't my entire code, I tried to limit my examples to the relevant sections. The thread is called in a main loop, but given time, the callback never fires.

EDIT: Here is the loop iterated in the UI thread:

while(!halt)
    {
        //EDIT: Changed from .run() to .start()
        renderThread.start();
        gameThread.start();
        //Wait for both threads to proceed before continuing
        try
        {
            renderThread.join();
            gameThread.join();
        }
        catch (InterruptedException e)
        {
            //Do nothing
        }

        Thread.currentThread();
        try
        {
            Thread.sleep(2);
        }
        catch (Exception e)
        {
            //Do nothing
        }
    }
Boston Walker
  • 534
  • 1
  • 6
  • 19
  • sorry, but your `while(!halt)` loop and content looks really really evil... sure you need to do it that way? – WarrenFaith May 21 '13 at 14:05
  • While that is not the problem at hand, I appreciate your concern. At this point I'm not concerned with optimization or even flexibility, just with getting my SurfaceView to display and the callback to fire. – Boston Walker May 21 '13 at 17:36

1 Answers1

1

Your drawing thread doesn't loop - it just runs once and exits. And it's likely that the surface isn't created yet on that first run.

You'll need to put the code that you currently have in some sort of loop, such as (in pseudo-code) :

while(!done) {
   lockTheCanvas();
   drawAFrame();
   unlockTheCanvas();
   sleep(FRAME_INTERVAL);
}

You then need to terminate the thread at the appropriate time by setting "done" to true; Note that you need the sleep(), otherwise, your thread will hog the cpu, and will never terminate.

GreyBeardedGeek
  • 29,460
  • 2
  • 47
  • 67
  • The thread is activated 10 times a second by a primary loop, and it at least does that part flawlessly. My problem is that the surface is NEVER created, or at least, that the callback never fires. – Boston Walker May 21 '13 at 02:39
  • I'd be suspicious about how you 'activate the thread 10 times a second'. Is the loop that you're using to do that yielding appropriately (by sleeping), to allow the callback to be called? Also, why have a thread that just runs once, and then exits, and then call it multiple times? It would probably be better to just let the thread run continuously until interrupted, as in my pseudo-code example. – GreyBeardedGeek May 21 '13 at 02:43
  • It's a bit simplified, but I posted the loop iterated in the UI thread (the main activity .java). – Boston Walker May 21 '13 at 13:59
  • I doubt that this will actually fix your problem, but you shouldn't be calling the run() method of Thread, because it won't run it on a new thread :-) Call the start() method instead. – GreyBeardedGeek May 21 '13 at 14:31