简体   繁体   中英

Android: Activity lifecycle and Singleton

I'm experiencing strange behaviors and NullPointerException (sometimes) with a Singleton pattern using in an Activity .

The activity in manifest (declared the orientation landscape):

<activity
   android:name="com.lux.game.MainActivity"
   android:screenOrientation="landscape" />

The activity class:

private GameManager.OnEventListener mEventListener = new GameManager.OnEventListener {
   @Override
   public void onEvent(Event event) {
      if (event == Event.PLAYER_SELECTED_PUZZLE) {
         // Do something on the UI
      }
   }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

    // If the orientation is landscape, then initialize the Game Manager
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
       GameManager.getInstance().init();

       // Register an interface to deal with game events
       GameManager.getInstance().registerForEvent(mEventListener);
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();

    // The activity was destroyed, remove the interface and clear the references
    GameManager.getInstance().cleanUp();
}

The GameManager singleton:

public class GameManager {
   private static GameManager mInstance;

   private GameManager() {
   }

   public static synchronized getInstance() {
      if (mInstance == null) {
          mInstance = new GameManager();
      }

      return mInstance;
   }
}

As you can see, I'm using the GameManager singleton to deal with game events (player selects a card, game end, etc). The singleton is instantiated at onCreate() if the orientation is in landscape and an interface is passed to the singleton and it's stored as a member of the class. The interface is removed at activity's onDestroy() .

Activity lifecycle and logs:

#1 onCreate(), orientation: LANDSCAPE
#2 back button pressed
#3 onDestroy(), orientation: PORTRAIT

Everything was working as I expected, until I started to test the application by locking/unlocking and pressing the HOME button on the device (real device, not emulator).

Activity lifecycle and logs:

#1 onCreate(), orientation: LANDSCAPE
#2 lock phone
#3 onDestroy(), orientation: PORTRAIT
#4 onCreate(), orientation: PORTRAIT
#5 unlock phone
#6 onDestroy(), orientation: PORTRAIT
#7 onCreate(), orientation: LANDSCAPE

onCreate and onDestroy is called sever times (normal behavior because orientation change), however this does not creates an issue, because the activity's lifecycle methods are called in the excpeted order and the interface is removed, and I'm not leaving any reference which could cause memory leak.

The problem (sometimes) comes to the surface if the steps above are getting called in a different order:

#1 onCreate(), orientation: LANDSCAPE (inst #1)
#2 lock phone
#3 onDestroy(), orientation: PORTRAIT (inst #1)
#4 onCreate(), orientation: PORTRAIT (inst #2)
#5 unlock phone
#6 onCreate(), orientation: LANDSCAPE (inst #3)
#7 onDestroy(), orientation: PORTRAIT (inst #2)

As you can see, step 6 and 7 are inverse compared to the previous steps. onCreate is called before onDestroy . Also I have added a number (inst #N) , which indicates how many activity instances are getting created and which is getting destroyed.

According to the logs, there are a total of 3 different activity instances which are getting created while I'm locking/unlocking the phone. The problem is caused by the last step, when the 3rd activity (inst #3) is created, and the 2nd activity (inst #2) is destroyed. I'm using a Singleton (only 1 instance exists in the whole application) to deal with events and manage the game, at activity's onDestroy the Singleton expects that the game has ended, and it must remove the references to the activity. This way, if an event occur in the game a NullPointerException is being thrown because the Singleton has cleared all the references.

  1. Should I use a different approach than Singleton? (How to deal with game events?)
  2. Should I use onSave/onRestore instances? (Why and how?)
  3. Should I onPause/onResume instead of onCreate/onDestroy?
  4. Out of ideas. If you have any good idea how to solve this issue is welcomed.

I think that you should create a new class that extends Application and init your Singleton in the onCreate() metod of them.

public class MyApplication extends Application {

@Override
public void onCreate() {
    super.onCreate();
    GameManager.getInstance().init();
    }
}

In your manifest, in the application tag, add android:name="MyApplication" In any to have a better singleton thread safe, due to your costructor, I suggest you to create a static istance of your objet in application.

public class MyApplication extends Application {

public static GameManager gameManager = new GameManager();

@Override
public void onCreate() {
    super.onCreate();
    gameManager.init();
    }
}

And after you can call your singleton by

MyApplication.gameManager

This is the best way to have a singleton thread safe

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM