Programming with SurfaceView and thread strategy f

2019-02-01 11:00发布

问题:

I am using a SurfaceView and a rendering thread to develop a game based on structure like LunarLander.

However, I faced many problems, and here I want to point them all out. I wish anyone who want to develop a game won't need to struggle with them anymore. And anyone who have a better idea of the structure can share their experiences, because I am still new to android and is eager to learn :)

[1] The function thread.start() should not be called more than once.

Many articles mentioned to create a thread when surface is created, in order to render again after activity paused using the method :

public void surfaceCreated(SurfaceHolder holder) {
    // start the thread here so that we don't busy-wait in run()
    // waiting for the surface to be created
    if (thread.getState() == Thread.State.TERMINATED)
    {
        thread = new LunarThread(getHolder(), getContext(), getHandler());
    }
    thread.setRunning(true);
    thread.start();
}

You can see that if the thread isn't terminated and the function is called, the activity crashes.

[2] If you pressed the "power" or the "red phone" button, or the phone is idle for a few minutes, the activity would be onPause() state, but the thread is still running. That is a really bad practice, so I need to find out a method to let the thread stop, and start again when onResume().

[3] If the screen lock is portrait / landscape, and your game is sticked to the other orientation, the screen lock forced you to "orient" once. That means to start the activity once more. I still can not find a solution to it. (as I mentioned in Android screen orientation bug)

Here are my code to fix those problems:

UIThread

public class UIThread extends Activity
{
    private gameView gameview;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        gameview = (gameView) findViewById(R.id.gameview);
    }
    protected void onPause()
    {
        super.onPause();
        Log.v("MY_ACTIVITY", "onPause");
        gameview.terminateThread();
        System.gc();
    }
    protected void onResume()
    {
        super.onResume();
        Log.v("MY_ACTIVITY", "onResume");
        if (gameview.surfaceCreated == true)
        {
            gameview.createThread(gameview.getHolder());
        }
    }
}

GameView

public class gameView extends SurfaceView implements SurfaceHolder.Callback
{
    boolean surfaceCreated;
    class gameThread extends Thread{}
    public gameView(Context context, AttributeSet attrs)
    {
        super(context, attrs);context.getResources();
            Log.v("MY_ACTIVITY", "gameView");
        surfaceCreated = false;
    }
    public void createThread (SurfaceHolder holder)
    {
            thread = new gameThread(holder);
        thread.run = true;
        thread.start();
    }
    public void terminateThread ()
    {
        thread.run = false;
        try
        {
            thread.join();
        }
        catch (InterruptedException e)
        {
            Log.e("FUNCTION", "terminateThread corrupts");
        }       
    }
    public void surfaceCreated(SurfaceHolder holder)
    {
        Log.v("MY_ACTIVITY", "surfaceCreated");
        if (surfaceCreated == false)
        {
            createThread(holder);
            surfaceCreated = true;
        }
    }
    public void surfaceDestroyed(SurfaceHolder holder) 
    {
        Log.v("MY_ACTIVITY", "surfaceDestroyed");
        surfaceCreated = false;
    }
}

Manifest

<activity android:name=".UIThread"
          android:screenOrientation="landscape"
          android:configChanges="orientation">

I use onResume() instead of surfaceCreated() to new thread. And I set a boolean surfaceCreated to know that onResume() comes from the first time application is created, or comes from the "screen off" situation.

So the thread die every time onPause() is called. Seems to be a good practice. Another way to let the thread stop then resume again is to synchronize an object and call wait / notify. But I don't know it is better or not.

Is there any better way to control the rendering thread?

回答1:

the solution is here

public void surfaceCreated(SurfaceHolder holder){

            if (plot_thread.getState() == Thread.State.TERMINATED) {
                plot_thread = new WaveformPlotThread(getHolder(), this);
                plot_thread.setRunning(true);
                plot_thread.start();
            }
            else {
                plot_thread.setRunning(true);
                plot_thread.start();
            }
        }


回答2:

I hope it can help

@Override
public void surfaceCreated(SurfaceHolder holder) {

   //if it is the first time the thread starts
   if(thread.getState() == Thread.State.NEW){
    thread.setRunning(true);//riga originale
    thread.start();//riga originale
   }

   //after a pause it starts the thread again
   else
   if (thread.getState() == Thread.State.TERMINATED){
       thread = new MainThread(getHolder(), this);
       thread.setRunning(true);
       thread.start();  // Start a new thread
       }
   }

and this

    @Override
    protected void onPause() {
    Log.d(TAG, "Pausing...");
    MainThread.running=false;//this is the value for stop the loop in the run()
    super.onPause();
    }


回答3:

I've been dealing with the same issue. I'm learning about game development on Android and also based my first project on the Lunar Lander example. I found that the SpriteMethodTest project created by Chris Pruett implements a much better approach in terms of thread management. It's all about using notify and wait methods, though. I couldn't tell whether it's better than your solution or not.