Android: Destroy the surface in EmulationFragment onStop.

Emulation needs to be running when the surface is destroyed, but we want
to pause in onStop. So call the surfaceDestroyed callback, as this
accomplished both.
This commit is contained in:
Mike 2017-10-25 12:45:03 -07:00
parent 987d24fe87
commit 18cb68eb3c
2 changed files with 145 additions and 110 deletions

View File

@ -275,8 +275,6 @@ public final class EmulationActivity extends AppCompatActivity
mImageView.setVisibility(View.GONE);
}
});
mEmulationFragment.startEmulation();
}
else
{

View File

@ -25,14 +25,9 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
private SharedPreferences mPreferences;
private Surface mSurface;
private InputOverlay mInputOverlay;
private Thread mEmulationThread;
private String mGamePath;
private final EmulationState mEmulationState = new EmulationState();
private EmulationState mEmulationState;
public static EmulationFragment newInstance(String gamePath)
{
@ -73,7 +68,8 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
mGamePath = getArguments().getString(KEY_GAMEPATH);
String gamePath = getArguments().getString(KEY_GAMEPATH);
mEmulationState = new EmulationState(gamePath);
}
/**
@ -116,10 +112,17 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
}
@Override
public void onStop()
public void onResume()
{
pauseEmulation();
super.onStop();
super.onResume();
mEmulationState.run();
}
@Override
public void onPause()
{
mEmulationState.pause();
super.onPause();
}
@Override
@ -158,100 +161,27 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
@Override
public void surfaceCreated(SurfaceHolder holder)
{
Log.debug("[EmulationFragment] Surface created.");
// We purposely don't do anything here.
// All work is done in surfaceChanged, which we are guaranteed to get even for surface creation.
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height);
if (mEmulationState.isPaused())
{
NativeLibrary.UnPauseEmulation();
}
mSurface = holder.getSurface();
NativeLibrary.SurfaceChanged(mSurface);
mEmulationState.newSurface(holder.getSurface());
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
Log.debug("[EmulationFragment] Surface destroyed.");
NativeLibrary.SurfaceDestroyed();
if (mEmulationState.isRunning())
{
pauseEmulation();
}
mEmulationState.clearSurface();
}
public void startEmulation()
public void stopEmulation()
{
synchronized (mEmulationState)
{
if (mEmulationState.isStopped())
{
Log.debug("[EmulationFragment] Starting emulation thread.");
mEmulationThread = new Thread(mEmulationRunner, "NativeEmulation");
mEmulationThread.start();
// The thread will call mEmulationState.run()
}
else if (mEmulationState.isPaused())
{
Log.debug("[EmulationFragment] Resuming emulation.");
NativeLibrary.UnPauseEmulation();
mEmulationState.run();
}
else
{
Log.debug("[EmulationFragment] Bug, startEmulation called while running.");
}
}
}
public void stopEmulation() {
synchronized (mEmulationState)
{
if (!mEmulationState.isStopped())
{
NativeLibrary.StopEmulation();
mEmulationState.stop();
}
}
}
private void pauseEmulation()
{
synchronized (mEmulationState)
{
Log.debug("[EmulationFragment] Pausing emulation.");
NativeLibrary.PauseEmulation();
mEmulationState.pause();
}
}
private Runnable mEmulationRunner = new Runnable()
{
@Override
public void run()
{
// Busy-wait for surface to be set
while (mSurface == null) {}
synchronized (mEmulationState)
{
Log.info("[EmulationFragment] Starting emulation: " + mSurface);
mEmulationState.run();
}
// Start emulation using the provided Surface.
NativeLibrary.Run(mGamePath);
}
};
public void startConfiguringControls()
{
@ -277,42 +207,149 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
STOPPED, RUNNING, PAUSED
}
private final String mGamePath;
private Thread mEmulationThread;
private State state;
private Surface mSurface;
private boolean mRunWhenSurfaceIsValid;
EmulationState()
EmulationState(String gamePath)
{
mGamePath = gamePath;
// Starting state is stopped.
state = State.STOPPED;
}
public boolean isStopped()
// Getters for the current state
public synchronized boolean isStopped()
{
return state == State.STOPPED;
}
public boolean isRunning()
{
return state == State.RUNNING;
}
public boolean isPaused()
public synchronized boolean isPaused()
{
return state == State.PAUSED;
}
public void run()
public synchronized boolean isRunning()
{
state = State.RUNNING;
return state == State.RUNNING;
}
public void pause()
{
state = State.PAUSED;
}
// State changing methods
public void stop()
public synchronized void stop()
{
if (state != State.STOPPED)
{
state = State.STOPPED;
NativeLibrary.StopEmulation();
}
else
{
Log.warning("[EmulationFragment] Stop called while already stopped.");
}
}
public synchronized void pause()
{
if (state != State.PAUSED)
{
state = State.PAUSED;
Log.debug("[EmulationFragment] Pausing emulation.");
// Release the surface before pausing, since emulation has to be running for that.
mSurface = null;
NativeLibrary.SurfaceDestroyed();
NativeLibrary.PauseEmulation();
}
else
{
Log.warning("[EmulationFragment] Pause called while already paused.");
}
}
public synchronized void run()
{
// If the surface is set, run now. Otherwise, wait for it to get set.
if (mSurface != null)
{
runWithValidSurface();
}
else
{
mRunWhenSurfaceIsValid = true;
}
}
// Surface callbacks
public synchronized void newSurface(Surface surface)
{
mSurface = surface;
if (mRunWhenSurfaceIsValid)
{
runWithValidSurface();
}
}
public synchronized void clearSurface()
{
if (mSurface == null)
{
Log.warning("[EmulationFragment] clearSurface called, but surface already null.");
}
else
{
mSurface = null;
Log.debug("[EmulationFragment] Surface destroyed.");
if (state == State.RUNNING)
{
NativeLibrary.SurfaceDestroyed();
state = State.PAUSED;
}
else if (state == State.PAUSED)
{
Log.warning("[EmulationFragment] Surface cleared while emulation paused.");
}
else
{
Log.warning("[EmulationFragment] Surface cleared while emulation stopped.");
}
}
}
private void runWithValidSurface()
{
mRunWhenSurfaceIsValid = false;
if (state == State.STOPPED)
{
Log.debug("[EmulationFragment] Starting emulation thread.");
mEmulationThread = new Thread(new Runnable()
{
@Override
public void run()
{
NativeLibrary.SurfaceChanged(mSurface);
NativeLibrary.Run(mGamePath);
}},
"NativeEmulation");
mEmulationThread.start();
}
else if (state == State.PAUSED)
{
Log.debug("[EmulationFragment] Resuming emulation.");
NativeLibrary.UnPauseEmulation();
NativeLibrary.SurfaceChanged(mSurface);
}
else
{
Log.debug("[EmulationFragment] Bug, run called while already running.");
}
state = State.RUNNING;
}
}
}