title: App restarts from wrong activity
body: <p>This is quite a conundrum:</p>
  
  <p>I open my app. It launches an activity which acts as a splashscreen (<code>ASplashscreen</code>) in which I load some <code>JSON</code> data from local storage (<code>raw</code> folder) and store it in memory in a <code>singleton object</code> (static). After this process is done it automatically moves along to the main activity (<code>AMain</code>)</p>
  
  <p>I exit the app by pressing the <code>home button</code>and run other applications, games, etc. When I reopen my app, the app crashes inside the <code>onCreate</code> method of the <code>AMain</code> because it tries to use some of the data inside the <code>singleton object</code> but the data is <code>null</code>. So it throws a <code>NullPointerException</code> when it does so.
  It appears that it restarts the <code>AMain</code> instead of <code>ASplashscreen</code> so the <code>singleton</code> doesn't have a chance to reinitialize.</p>
  
  <p>This happens randomly across multiple such tries...</p>
  
  <p>I have two presumptions ...</p>
  
  <ol>
  <li><p>My first presumption, and from what I know about the Android OS, is that while I was running those other applications (especially the games) one of them required a lot of memory so the OS released my app from memory to make room, so the <code>singleton data</code> was <code>garbage collected</code>. </p></li>
  <li><p>I also presume that while the <code>gc</code> removed my singleton from memory, the OS still kept some data relating to the "state" of the current running activity, so it knew at least that it had the <code>AMain</code> activity opened before i closed the app. This would explain why it reopened the <code>AMain</code> activity instead of the <code>ASplashscreen</code>.</p></li>
  </ol>
  
  <p>Am I right? Or is there another explanation why I get this exception? Any suggestions/clarifications are welcomed. </p>
  
  <p>Also, what would be the best approach to handle this? My approach is to check the existence of he singleton data whenever I try to use it and if it's null then just basically restart the app. This makes it go through the <code>ASplashscreen</code> so the <code>JSON</code> gets initialized and everything is ok.</p>
  
  <p><strong>EDIT</strong> As requested, here's my <code>AndroidManifest</code></p>
  
  <p></p>
  
  <pre><code> &lt;uses-permission android:name="android.permission.RECORD_AUDIO"/&gt;
  &lt;uses-permission android:name="android.permission.INTERNET"/&gt;
  &lt;uses-permission android:name="com.android.vending.BILLING"/&gt;
  &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/&gt;
  &lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/&gt;
  
  &lt;application
      android:name=".global.App"
      android:allowBackup="true"
      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name"
      android:largeHeap="true"
      android:theme="@style/AppTheme"&gt;
  
      &lt;!--SPLASH SCREEN--&gt;
      &lt;activity
          android:name=".activities.ASplashscreen"
          android:label="@string/app_name"
          android:launchMode="singleTask"
          android:screenOrientation="portrait"
          android:theme="@style/AppTheme"&gt;
          &lt;intent-filter&gt;
              &lt;action android:name="android.intent.action.MAIN"/&gt;
  
              &lt;category android:name="android.intent.category.LAUNCHER"/&gt;
          &lt;/intent-filter&gt;
      &lt;/activity&gt;
  
      &lt;!--MAIN--&gt;
      &lt;activity
          android:name=".activities.AMain"
          android:label="@string/app_name"
          android:launchMode="singleTask"
          android:screenOrientation="portrait"
          android:theme="@style/AppTheme"/&gt;
  
      &lt;!--MENU--&gt;
      &lt;activity
          android:name=".activities.AMenu"
          android:label="@string/app_name"
          android:launchMode="singleTask"
          android:screenOrientation="portrait"
          android:theme="@style/AppTheme"/&gt;
  
      &lt;!--HELP--&gt;
      &lt;activity
          android:name=".activities.AHelp"
          android:label="@string/app_name"
          android:launchMode="singleTask"
          android:screenOrientation="portrait"
          android:theme="@style/AppTheme"/&gt;
  
      &lt;!--ADMOB--&gt;
      &lt;activity
          android:name="com.google.android.gms.ads.AdActivity"
          android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
          android:theme="@android:style/Theme.Translucent"/&gt;
  
      &lt;!--FACEBOOK LOGIN ACTIVITY (SDK)--&gt;
      &lt;activity
          android:name="com.facebook.LoginActivity"
          android:label="@string/app_name"
          android:screenOrientation="portrait"
          android:theme="@style/AppTheme"/&gt;
  
      &lt;!--This meta-data tag is required to use Google Play Services.--&gt;
      &lt;meta-data
          android:name="com.google.android.gms.version"
          android:value="@integer/google_play_services_version"/&gt;
  
      &lt;!--FACEBOOK STUFF--&gt;
      &lt;meta-data
          android:name="com.facebook.sdk.ApplicationId"
          android:value="@string/facebook_app_id"/&gt;
  
      &lt;!--GOOGLE PLUS--&gt;
      &lt;meta-data
          android:name="com.google.android.gms.version"
          android:value="@integer/google_play_services_version"/&gt;
  
      &lt;!--CRASHLYTICS--&gt;
      &lt;meta-data
          android:name="com.crashlytics.ApiKey"
          android:value="9249....."/&gt;
  
  &lt;/application&gt;
  </code></pre>
  
  <p></p>
  
  <p>If you guys really want it, here's the content of the <code>ASplashscreen</code></p>
  
  <pre><code>/**
   * @author MAB
   */
  public class ASplashscreen extends ABase implements IIosLikeDialogListener {
  
      private final float SHEEP_WIDTH_FRAC = 0.8f;
  
      private final int SPLASHSCREEN_DELAY_MS = 500;
  
      //View references
      private View sheep_image;
  
      /** The timestamp recorded when this screen came into view. We'll used this to determine how much we'll need to keep the splash screen awake */
      private long mStartTimestamp;
  
      private IosLikeDialog mDialog;
  
      private IabHelper mIabHelper;
  
      // Listener that's called when we finish querying the items and subscriptions we own
      IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
          public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
  
              // Have we been disposed of in the meantime? If so, quit.
              if (mIabHelper == null) {
                  System.out.println("=== IAB INVENTORY PROBLEM :: WE'VE BEEN DISPOSED");
                  displayAppStoreUnavailableDialog();
                  return;
              }
  
              // Is it a failure?
              if (result.isFailure()) {
                  displayAppStoreUnavailableDialog();
                  System.out.println("=== IAB INVENTORY PROBLEM :: FAILED TO QUERY INVENTORY :: " + result);
                  return;
              }
  
              //Sync our static stuff with the app store
              HSounds.instance().populate(ASplashscreen.this, inventory);
              HLights.instance().populate(ASplashscreen.this, inventory);
  
              //Store the stuff locally just to be sure
              HStorage.persistObjectToFile(ASplashscreen.this, HVersions.SOUNDS);
              HStorage.persistObjectToFile(ASplashscreen.this, HVersions.LIGHTS);
  
              System.out.println("=== SUCCESSFULLY SYNCED WITH STORE !");
  
              jumpToMainActivity();
  
          }
      };
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          setContentView(R.layout.a_splashscreen);
  
          init();
  
      }
  
      @Override
      protected void onDestroy() {
          super.onDestroy();
  
          if (mIabHelper != null) {
              mIabHelper.dispose();
          }
          mIabHelper = null;
      }
  
      @Override
      public void onIosLikeDialogBtnsClick(int btnStringResID) {
          if (btnStringResID == IosLikeDialog.BTN_OK) {
              jumpToMainActivity();
          }
      }
  
      private void init() {
          //Get view references
          sheep_image = findViewById(R.id.splashscreen_sheep);
  
          mStartTimestamp = System.currentTimeMillis();
  
          VersionTracking.setVersions(this);
  
          //Set the width of the sheep
          RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) sheep_image.getLayoutParams();
          params.width = (int) ((float) UScreen.getScreenWidthInPortrait(this) * SHEEP_WIDTH_FRAC);
          sheep_image.setLayoutParams(params);
  
          mDialog = new IosLikeDialog()
                  .with(findViewById(R.id.ios_like_dialog_main_container))
                  .listen(this);
  
          new Thread(new Runnable() {
              @Override
              public void run() {
  
                  parseJsons();
  
                  //Get the filler bar values from shared prefs
                  HBrightness.instance().retrieveFromPersist(ASplashscreen.this);
                  HSensorAndTimer.instance().retrieveFromPersist(ASplashscreen.this);
  
                  WsBuilder.build(ASplashscreen.this).getGift(new ResponseListener&lt;EGift&gt;() {
                      @Override
                      public void onSuccess(EGift gifts) {
                          long now = System.currentTimeMillis();
                          SimpleDateFormat fmt = new SimpleDateFormat(HJsonDataBase.GIFT_DATE_FORMAT);
                          Date start;
                          Date end;
  
                          //Handle the gifts
                          if (gifts != null &amp;&amp; gifts.data != null &amp;&amp; gifts.responseOK()) {
                              //Go through the SOUNDS and check if we need to set them as gifts, if not reset them
                              for (ESound sound : HSounds.instance().getValues().getSounds()) {
                                  String sku = sound.getSku(ASplashscreen.this);
                                  sound.giftStart = null;
                                  sound.giftEnd = null;
                                  for (String giftSku : gifts.data.inapps) {
                                      if (giftSku.equals(sku)) {
                                          sound.giftStart = gifts.data.start_date;
                                          sound.giftEnd = gifts.data.end_date;
                                          break;
                                      }
                                  }
                                  //Check if redeemed gift expired and if so, reset the dates
                                  checkSoundGiftExpired(sound, fmt, now);
                              }
                              //Go through the LIGHTS and check if we need to set them as gifts, if not reset them
                              for (ELight light : HLights.instance().getValues().getLights()) {
                                  String sku = light.getSku(ASplashscreen.this);
                                  light.giftStart = null;
                                  light.giftEnd = null;
                                  for (String giftSku : gifts.data.inapps) {
                                      if (giftSku.equals(sku)) {
                                          light.giftStart = gifts.data.start_date;
                                          light.giftEnd = gifts.data.end_date;
                                          break;
                                      }
                                  }
                                  //Check if redeemed gift expired and if so, reset the dates
                                  checkLightGiftExpired(light, fmt, now);
                              }
                              //Persist the data in the local storage
                              HStorage.persistObjectToFile(ASplashscreen.this, HVersions.SOUNDS);
                              HStorage.persistObjectToFile(ASplashscreen.this, HVersions.LIGHTS);
                          }
  
                          //Run the IAB helper now
                          runIabHelper();
                      }
  
                      @Override
                      public void onErrorResponse(VolleyError error) {
                          //This might mean we're in offline mode, so check if the gifts expired
                          checkAllLightsGiftExpired();
                          checkAllSoundsGiftExpired();
  
                          //Run the IAB helper now
                          runIabHelper();
                      }
                  }, getPackageName());
              }
          });
      }
  
      /**
       * This is run on a non-UI thread !!
       */
      private void parseJsons() {
  
          /**
           * Versions
           */
          parseVersions();
  
  
          /**
           * BACKGROUND
           */
          parseBackgrounds();
          try {
              validateBackgrounds();
          } catch (NullPointerException e) {
              removeBackgroundsFile();
              parseBackgrounds();
          }
  
          /**
           * LIGHTS
           */
          parseLights();
          try {
              validateLights();
          } catch (NullPointerException e) {
              removeLightsFile();
              parseLights();
          }
  
          /**
           * SOUNDS
           */
          parseSounds();
          try {
              validateSounds();
          } catch (NullPointerException e) {
              removeSoundsFile();
              parseSounds();
          }
  
      }
  
      private void parseVersions() {
          InputStream in = getResources().openRawResource(R.raw.versions);
          EVersions versions = null;
          try {
              versions = UGson.jsonToObject(in, EVersions.class);
          } catch (Exception e) {
              System.out.println("==== PARSE ERROR :: VERSIONS :: " + e.getMessage());
              e.printStackTrace();
              return;
          }
          HVersions.instance().setValues(this, versions);
      }
  
      private void parseBackgrounds() {
          //Get the version of he JSONS at which we've last updated them from the "raw" folder
          int lastVersionBckgnds = UPersistent.getInt(ASplashscreen.this, HVersions.SHARED_PREF_LAST_JSONS_VERSION_BCKGNDS, 0);
  
          InputStream in;
          //If there are no files in local storage OR there's a new version of the JSON files that we need to retrieve
          if (!HStorage.fileExists(ASplashscreen.this, HStorage.FILE_JSON_BACKGROUNDS) ||
                  HVersions.instance().shouldUpdateFromResources(HVersions.BACKGROUNDS, lastVersionBckgnds)) { //Update from raw folder
              in = getResources().openRawResource(R.raw.backgrounds);
          } else { //Update from local storage
              in = HStorage.getInputStreamForFile(ASplashscreen.this, HStorage.FILE_JSON_BACKGROUNDS);
          }
          EBackgrounds bckgnds = null;
          try {
              bckgnds = UGson.jsonToObject(in, EBackgrounds.class);
          } catch (Exception e) {
              System.out.println("==== PARSE ERROR :: BACKGROUNDS :: " + e.getMessage());
              e.printStackTrace();
          }
          HBackgrounds.instance().setValues(this, bckgnds);
      }
  
      private void parseLights() {
          //Get the version of he JSONS at which we've last updated them from the "raw" folder
          int lastVersionLights = UPersistent.getInt(ASplashscreen.this, HVersions.SHARED_PREF_LAST_JSONS_VERSION_LIGHTS, 0);
  
          InputStream in;
          //If there are no files in local storage OR there's a new version of the JSON files that we need to retrieve
          if (!HStorage.fileExists(ASplashscreen.this, HStorage.FILE_JSON_LIGHTS) ||
                  HVersions.instance().shouldUpdateFromResources(HVersions.LIGHTS, lastVersionLights)) { //Update from raw folder
              in = getResources().openRawResource(R.raw.lights);
          } else { //Update from local storage
              in = HStorage.getInputStreamForFile(ASplashscreen.this, HStorage.FILE_JSON_LIGHTS);
          }
          ELights lights = null;
          try {
              lights = UGson.jsonToObject(in, ELights.class);
          } catch (Exception e) {
              System.out.println("==== PARSE ERROR :: LIGHTS :: " + e.getMessage());
              e.printStackTrace();
          }
          if (lights != null) {
              HLights.instance().setValues(this, lights);
          }
      }
  
      private void parseSounds() {
          int lastVersionSounds = UPersistent.getInt(ASplashscreen.this, HVersions.SHARED_PREF_LAST_JSONS_VERSION_SOUNDS, 0);
  
          InputStream in;
          //If there are no files in local storage OR there's a new version of the JSON files that we need to retrieve
          if (!HStorage.fileExists(ASplashscreen.this, HStorage.FILE_JSON_SOUNDS) ||
                  HVersions.instance().shouldUpdateFromResources(HVersions.SOUNDS, lastVersionSounds)) { //Update from raw folder
              in = getResources().openRawResource(R.raw.sounds);
          } else { //Update from local storage
              in = HStorage.getInputStreamForFile(ASplashscreen.this, HStorage.FILE_JSON_SOUNDS);
          }
          ESounds sounds = null;
          try {
              sounds = UGson.jsonToObject(in, ESounds.class);
          } catch (Exception e) {
              System.out.println("==== PARSE ERROR :: SOUNDS" + e.getMessage());
          }
          if (sounds != null) {
              HSounds.instance().setValues(this, sounds);
          }
      }
  
      private void validateBackgrounds() throws NullPointerException {
          if (HBackgrounds.instance().getValues() == null) {
              throw new NullPointerException();
          }
          if (HBackgrounds.instance().getValues().getBackgrounds() == null) {
              throw new NullPointerException();
          }
      }
  
      private void validateLights() throws NullPointerException {
          if (HLights.instance().getValues() == null) {
              throw new NullPointerException();
          }
          if (HLights.instance().getValues().getLights() == null) {
              throw new NullPointerException();
          }
      }
  
      private void validateSounds() throws NullPointerException {
          if (HSounds.instance().getValues() == null) {
              throw new NullPointerException();
          }
          if (HSounds.instance().getValues().getSounds() == null) {
              throw new NullPointerException();
          }
      }
  
      private void removeBackgroundsFile() {
          HStorage.deleteFile(this, HStorage.FILE_JSON_BACKGROUNDS);
      }
  
      private void removeLightsFile() {
          HStorage.deleteFile(this, HStorage.FILE_JSON_LIGHTS);
      }
  
      private void removeSoundsFile() {
          HStorage.deleteFile(this, HStorage.FILE_JSON_SOUNDS);
      }
  
      private void runIabHelper() {
  
          //If there's no network connection, then ... sorry
          if (!UNetwork.isNetworkAvailable(this)) {
              displayAppStoreUnavailableDialog();
              System.out.println("=== IAB ERROR :: NO NETWORK");
              return;
          }
  
          try {
              mIabHelper = new IabHelper(ASplashscreen.this, CIab.IAB_PUBLIC_KEY);
              mIabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
                  @Override
                  public void onIabSetupFinished(IabResult result) {
                      if (!result.isSuccess()) {
                          // Oh noes, there was a problem.
                          System.out.println("=== IAB ERROR :: CONNECTION :: " + result);
                          displayAppStoreUnavailableDialog();
                          return;
                      }
  
                      //Obtain and create the list of skus from both the LIGHTS and the SOUNDS handlers
                      List&lt;String&gt; skus = new ArrayList&lt;String&gt;();
                      skus.addAll(HSounds.instance().createSkuList(ASplashscreen.this, true));
                      skus.addAll(HLights.instance().createSkuList(ASplashscreen.this, true));
  
                      //Get the inventory
                      try {
                          mIabHelper.queryInventoryAsync(true, skus, mGotInventoryListener, new Thread.UncaughtExceptionHandler() {
                              @Override
                              public void uncaughtException(Thread thread, Throwable ex) {
                                  //                            Crashlytics.logException(ex);
                                  System.out.println("=== IAB ERROR :: query inventory crashed :: " + ex.getMessage());
                                  displayAppStoreUnavailableDialog();
                              }
                          });
                      } catch (IllegalStateException e) {
                          displayAppStoreUnavailableDialog();
                      }
                  }
              });
          } catch (NullPointerException e1) {
              //            Crashlytics.logException(e1);
              System.out.println("=== IAB ERROR :: query inventory crashed :: " + e1.getMessage());
              displayAppStoreUnavailableDialog();
          } catch (IllegalArgumentException e2) {
              //            Crashlytics.logException(e2);
              System.out.println("=== IAB ERROR :: query inventory crashed :: " + e2.getMessage());
              displayAppStoreUnavailableDialog();
          }
      }
  
      private void displayAppStoreUnavailableDialog() {
          runOnUiThread(new Runnable() {
              @Override
              public void run() {
                  if (mDialog == null) {
                      return;
                  }
                  mDialog.reset()
                          .header(R.string.inapp_store_unavailable_header)
                          .subheader(R.string.inapp_store_unavailable_subheader)
                          .btnOK()
                          .show();
              }
          });
      }
  
      private void jumpToMainActivity() {
  
          int timePassed = (int) (System.currentTimeMillis() - mStartTimestamp);
  
          int delay = (timePassed &gt; SPLASHSCREEN_DELAY_MS) ? 0 : (SPLASHSCREEN_DELAY_MS - timePassed);
  
          new Handler().postDelayed(new Runnable() {
              @Override
              public void run() {
  
                  //In case we need to display the tutorial, then do so
                  if (AHelp.shouldDisplayTutorial(ASplashscreen.this)) {
                      CrashReport.log("ASplashscreen -&gt; AHelp");
                      Intent i = new Intent(ASplashscreen.this, AHelp.class);
                      i.putExtra(AHelp.BUNDLE_SHOW_TUTORIAL, true);
                      startActivity(i);
                      finish();
                      overridePendingTransition(R.anim.anim_slide_in_from_bottom, R.anim.anim_stay_put);
                      return;
                  } else { //Otherwise continue with normal flow
                      CrashReport.log("ASplashscreen -&gt; AMain");
                      Intent i = new Intent(ASplashscreen.this, AMain.class);
                      i.putExtra(AMain.BUNDLE_DEBUGGING_CAME_FROM_SPLASHSCREEN, true);
                      startActivity(i);
                      finish();
                  }
  
              }
          }, delay);
      }
  
      private void checkAllSoundsGiftExpired() {
          SimpleDateFormat fmt = new SimpleDateFormat(HJsonDataBase.GIFT_DATE_FORMAT);
          long now = System.currentTimeMillis();
  
          for (ESound sound : HSounds.instance().getValues().getSounds()) {
              if (sound != null) {
                  checkSoundGiftExpired(sound, fmt, now);
              }
          }
      }
  
      private void checkAllLightsGiftExpired() {
          SimpleDateFormat fmt = new SimpleDateFormat(HJsonDataBase.GIFT_DATE_FORMAT);
          long now = System.currentTimeMillis();
  
          for (ELight light : HLights.instance().getValues().getLights()) {
              if (light != null) {
                  checkLightGiftExpired(light, fmt, now);
              }
          }
      }
  
      private void checkSoundGiftExpired(ESound sound, SimpleDateFormat fmt, long now) {
          if (UString.stringsExist(sound.giftExpireStart, sound.giftExpireEnd)) {
              try {
                  Date start = fmt.parse(sound.giftExpireStart);
                  Date end = fmt.parse(sound.giftExpireEnd);
                  if (now &lt; start.getTime() || end.getTime() &lt; now) {
                      sound.giftExpireStart = null;
                      sound.giftExpireEnd = null;
                  }
              } catch (ParseException e) {
                  //Do nothin'
              }
          }
      }
  
      private void checkLightGiftExpired
              (ELight light, SimpleDateFormat fmt, long now) {
          if (UString.stringsExist(light.giftExpireStart, light.giftExpireEnd)) {
              try {
                  Date start = fmt.parse(light.giftExpireStart);
                  Date end = fmt.parse(light.giftExpireEnd);
                  if (now &lt; start.getTime() || end.getTime() &lt; now) {
                      light.giftExpireStart = null;
                      light.giftExpireEnd = null;
                  }
              } catch (ParseException e) {
                  //Do nothin'
              }
          }
      }
  
  }
  </code></pre>