0

This is my first app, and a basic question. I want to know how to deal with interruptions such as locking the screen, or minimising the app. Both of these things currently cause the app to go black, and stop responding.

The app is structured with just one activity: Main Activity. Here,

GameSurface extends SurfaceView implements SurfaceHolder.Callback { ... }

is a class that has a related "GameThread.java" Thread , and together they run the simple app and draw to a Canvas.

Here's MainActivity.java:

public class MainActivity extends AppCompatActivity {

final int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

    getWindow().getDecorView().setSystemUiVisibility(flags);

    this.setContentView(new GameSurface(this));
}


@Override
protected void onPause() {
    //finish();
    super.onPause();
}

}

Without finish() in onPause(), the app just can't cope with interruptions. Presumably this is to do with the savedInstanceState Bundle not saving the "GameSurface", or not being able to recover in the onResume() function, but I don't understand the problem.

Adding finish() to onPause() causes the app to close, but the image of the app lingers behind on my home screen background, behind my app icons. I don't want to kill the app when I interrupt it, but I thought it should be easy, so I'm curious as to why this doesn't cleanly close the app. I can't seem to find another way to end the main activity, is there a different function for terminating the app cleanly?

My immediate aim is to have the app resume when the screen is unlocked, after having been locked, and I thought this would be a simple procedure, but I can't find out how to do it. Perhaps my app is poorly structured. If you have a better way that I can structure the app, please tell me. The whole UI is a single canvas that I draw cartoon trees etc. onto.

EDIT: I have read in another question: Android Activity lifecycle and locking/unlocking device

That since I have specified the screen orientation as landscape in Main Activity, the Main activity has to be destroyed in order to display the portrait lock screen. Then, I guess, onCreate() doesn't work because the ,savedInstanceState is bad, but I don't see why it's bad. I also don't know what happens when I just minimise the app, without screen orientation changing.

EDIT: Here is the log of me opening the app, locking the screen, unlocking, then closing:

(I have added Log instructions to some methods on Main Activity)

[The app starts, lots of normal logging]

06-09 08:06:47.384 29697-29697/com.example.christian.androidgame2d W/System: ClassLoader referenced unknown path: /data/app/com.example.christian.androidgame2d-2/lib/arm
06-09 08:06:47.388 29697-29697/com.example.christian.androidgame2d I/InstantRun: starting instant run server: is main process
06-09 08:06:47.533 29697-29697/com.example.christian.androidgame2d D/log: onCreate
06-09 08:06:47.587 29697-29697/com.example.christian.androidgame2d W/art: 
Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
06-09 08:06:47.837 29697-29697/com.example.christian.androidgame2d D/log: onstart
06-09 08:06:47.837 29697-29697/com.example.christian.androidgame2d D/log: onresume
06-09 08:06:47.859 29697-29735/com.example.christian.androidgame2d D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
06-09 08:06:47.957 29697-29735/com.example.christian.androidgame2d I/Adreno-EGL: <qeglDrvAPI_eglInitialize:379>: QUALCOMM Build: 10/09/15, 6cbbf7d, I3193f6e94a
06-09 08:06:47.968 29697-29735/com.example.christian.androidgame2d I/OpenGLRenderer: Initialized EGL, version 1.4
06-09 08:06:48.206 29697-29697/com.example.christian.androidgame2d W/art: Before Android 4.1, method int android.support.v7.widget.ListViewCompat.lookForSelectablePosition(int, boolean) would have incorrectly overridden the package-private method in android.widget.ListView

The app is now running ok. [I lock the screen (power button)]

06-09 08:07:22.852 29697-29697/com.example.christian.androidgame2d D/log: onPause

                                                                      [ 06-09 08:07:22.868  1735: 1835 D/         ]
                                                                      activate, handle: 1598182242, enabled: 0, index 2

                                                                      [ 06-09 08:07:22.868  1735: 1835 D/         ]
                                                                      BstSensorExt: id=1598182242, en=0

                                                                      [ 06-09 08:07:22.868  1735: 1835 D/         ]
                                                                      enable ID_SORI, path /sys/class/srot_sensor/g_sensor/en_disp_rotation, fd 248
06-09 08:07:23.344 29697-29697/com.example.christian.androidgame2d D/log: ondestroy

[I unlock the screen]

No more logging. But the app displays as a frozen image. I minimise the app and it glitches as follows: Buggy minimisation

So the app hasn't closed, but main activity has called onDestroy(). I don't know what stage the app is in.

  • are you familiar with the activity lifecycle? it is an important android concept https://developer.android.com/guide/components/activities/activity-lifecycle.html – nandsito Jun 08 '17 at 21:32
  • Yes, I have read that article. I found (i'll link in the question) another question that I think partially answers my question. The screen orientation is set by my activity, and so the activity, I think, has to be destroyed in order to display the lockscreen. Then on unlock, onCreate() is called; ie. MainActivity restarts. But it's not clear why it doesn't just cleanly restart. Perhaps I should do some Logging to see what's going on exactly. – Christian Fieldhouse Jun 08 '17 at 22:30
  • when the power button is pressed i believe your activity isn't destroyed, but just `pause`d and `stop`ped. Then, when the screen is unlocked, it is `start`ed and `resume`d. Yes, some logging in these callbacks will help you – nandsito Jun 08 '17 at 22:38
  • do you want your app to always be in landscape? – nandsito Jun 08 '17 at 22:38
  • @nandsito Yes, this app has no use for a portrait orientation. My current goal is just to be able to minimise the app, then maximise it, and for it to resume as it was. I have added a log to the question, which shows the main activity calling onDestroy() when I lock the screen. So for now I will try to stop onDestroy() being called. – Christian Fieldhouse Jun 09 '17 at 07:50

2 Answers2

1

After improving my understanding of the general concepts, and Logging things, I discovered that the problem was actually in "GameSurface.java"; there was a non-ending while loop in in the method surfaceDestroyed(SurfaceHolder holder). (Not important, just a bug)

The main things I missed:

Setting the orientation as landscape causes MainActivity to call onDestroy() when I lock the screen, because the orientation of the lockscreen is portrait, so it has to destroy the activity. This means my activity resets after locking and unlocking. What's particularly annoying is that that method was copied from a tutorial, oh well.

After fixing the bug, putting finish() in onPause() causes the app to close when I lock the screen, but causes it not to be able to reopen when I minimise it, so this is bad practice/ not useful.

Because the 'GameSurface' is being destroyed by interrupting the app, in order to resume the app I need some way to store what the GameSurface was doing (store the size and position of all the trees, in this case) This is my next goal.

My fix consisted of putting the getWindow().getDecorView().setSystemUiVisibility(flags); Instruction in onResume(), so that so far the app simply restarts when the user returns to it. At least it no longer crashes.

0

I suggest you to improve the binding of your application to Android Activity lifecycle.

When your app starts for the first time, Android calls onCreate, onStart, and onResume methods of your activity. Android expects you to set up your app in these methods. I believe your app is doing these fine.

Now, when the user leaves your app to do something else (e.g. answer a call, switch to another app, turn the phone off), Android expects your app to behave accordingly, that is, free resources, stops threads etc. Android offers onPause, onStop, and onDestroy methods for doing it. Note that onPause is mirrored to onResume, onStop to onStart, and onCreate to onDestroy.

So, for your app:

  • You say you start a thread. So you should stop it in one of the callbacks.
  • You can, but you need not call finish in onPause. onPause is called by the operating system when your app e.g. loses focus but remains visible. You don't need to entirely close your app every time it happens.
  • If you want your app to always be in landscape, you can do this in your manifest, so you don't have to create, then destroy, then create your activity again:
<activity android:name="..."
        android:screenOrientation="landscape">
nandsito
  • 3,782
  • 2
  • 19
  • 26