简体   繁体   English

Android:活动生命周期和Singleton

[英]Android: Activity lifecycle and Singleton

I'm experiencing strange behaviors and NullPointerException (sometimes) with a Singleton pattern using in an Activity . 我在Activity使用Singleton模式遇到奇怪的行为和NullPointerException (有时)。

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: GameManager单例:

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). 如您所见,我正在使用GameManager单例来处理游戏事件(玩家选择纸牌,游戏结束等)。 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. 如果方向是横向的并且将interface传递给单例并将其存储为类的成员,则在onCreate()实例化单例。 The interface is removed at activity's onDestroy() . 在活动的onDestroy()处删除了该interface

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). 一切都按预期工作,直到我通过锁定/解锁并按设备(真实设备,而非仿真器)上的HOME按钮开始测试应用程序。

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. onCreateonDestroy被称为服务器次(正常行为,因为方向变化),但是这并不造成问题,因为活动的lifecycle方法被称为在excpeted顺序和interface被删除,我不会离开它可以在任何参考导致内存泄漏。

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. 如您所见,步骤67与之前的步骤相比是相反的。 onCreate is called before onDestroy . onCreateonDestroy之前调用。 Also I have added a number (inst #N) , which indicates how many activity instances are getting created and which is getting destroyed. 另外,我还添加了一个数字(inst #N) ,该数字指示正在创建多少活动实例以及正在破坏多少活动实例。

According to the logs, there are a total of 3 different activity instances which are getting created while I'm locking/unlocking the phone. 根据日志,在我锁定/解锁手机时,总共创建了3个不同的活动实例。 The problem is caused by the last step, when the 3rd activity (inst #3) is created, and the 2nd activity (inst #2) is destroyed. 问题是由最后一个步骤引起的,当创建第三个活动(实例3)并破坏了第二个活动(实例2)时。 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. 我正在使用一个Singleton (整个应用程序中只有1个实例)来处理事件和管理游戏,在活动的onDestroySingleton期望游戏已经结束,并且必须删除对活动的引用。 This way, if an event occur in the game a NullPointerException is being thrown because the Singleton has cleared all the references. 这样,如果游戏中发生事件,则由于Singleton已清除所有引用,将引发NullPointerException

  1. Should I use a different approach than Singleton? 我应该使用与Singleton不同的方法吗? (How to deal with game events?) (如何处理游戏事件?)
  2. Should I use onSave/onRestore instances? 我应该使用onSave / onRestore实例吗? (Why and how?) (为什么以及如何?)
  3. Should I onPause/onResume instead of onCreate/onDestroy? 我应该onPause / onResume代替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. 我认为您应该创建一个扩展Application的新类,并在它们的onCreate()方法中初始化您的Singleton。

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. 在清单中的application标记中,添加android:name =“ MyApplication” 。由于构造函数的原因,在任何一种具有更好的单例线程安全性的情况下,建议您在应用程序中创建对象的静态等距。

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 这是确保单例线程安全的最佳方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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