2015-04-07 5 views
2

Это довольно загадка:Приложение перезагружается от неправильной деятельности

Открываю приложение. Он запускает действие, которое выступает в качестве заставки (ASplashscreen), в котором я загружаю некоторые данные из локального хранилища (raw) и сохраняю его в памяти в singleton object (статический). После того, как этот процесс осуществляется автоматически движется к основной деятельности (AMain)

я выйти из приложения, нажав home button и запускать другие приложения, игры и т.д. Когда я возобновлять мое приложение, приложение выходит из строя внутри метода onCreateAMain, потому что он пытается использовать некоторые данные внутри singleton object, но данные null. Поэтому он выбрасывает NullPointerException, когда он это делает. Похоже, что он перезапускает AMain вместо ASplashscreen, поэтому у singleton нет возможности повторной инициализации.

Это происходит случайным образом на нескольких таких попыток ...

У меня есть два предположения ...

  1. Моя первая презумпция, и от того, что я знаю о Android OS, является то, что в то время как я запускал эти другие приложения (особенно игры), один из них требовал большой памяти, поэтому ОС выпустила мое приложение из памяти, чтобы освободить место, поэтому singleton data был garbage collected.

  2. Я также полагаю, что в то время как gc удалили мой синглтон из памяти, операционная система по-прежнему хранятся некоторые данные, относящихся к «состоянию» текущей текущей деятельности, так что знала, по крайней мере, что он имел AMain деятельности открыта, прежде чем я закрыл приложение. Это объясняет, почему оно возобновило деятельность AMain вместо ASplashscreen.

Я прав? Или есть другое объяснение, почему я получаю это исключение? Любые предложения/разъяснения приветствуются.

Кроме того, что было бы лучшим подходом к этому? Мой подход заключается в проверке наличия данных singleton при каждом использовании, а если он равен нулю, то просто перезапустите приложение. Это заставляет его пройти через ASplashscreen, так что JSON инициализируется, и все в порядке.

EDIT В соответствии с просьбой, вот мой AndroidManifest

<uses-permission android:name="android.permission.RECORD_AUDIO"/> 
<uses-permission android:name="android.permission.INTERNET"/> 
<uses-permission android:name="com.android.vending.BILLING"/> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> 

<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"> 

    <!--SPLASH SCREEN--> 
    <activity 
     android:name=".activities.ASplashscreen" 
     android:label="@string/app_name" 
     android:launchMode="singleTask" 
     android:screenOrientation="portrait" 
     android:theme="@style/AppTheme"> 
     <intent-filter> 
      <action android:name="android.intent.action.MAIN"/> 

      <category android:name="android.intent.category.LAUNCHER"/> 
     </intent-filter> 
    </activity> 

    <!--MAIN--> 
    <activity 
     android:name=".activities.AMain" 
     android:label="@string/app_name" 
     android:launchMode="singleTask" 
     android:screenOrientation="portrait" 
     android:theme="@style/AppTheme"/> 

    <!--MENU--> 
    <activity 
     android:name=".activities.AMenu" 
     android:label="@string/app_name" 
     android:launchMode="singleTask" 
     android:screenOrientation="portrait" 
     android:theme="@style/AppTheme"/> 

    <!--HELP--> 
    <activity 
     android:name=".activities.AHelp" 
     android:label="@string/app_name" 
     android:launchMode="singleTask" 
     android:screenOrientation="portrait" 
     android:theme="@style/AppTheme"/> 

    <!--ADMOB--> 
    <activity 
     android:name="com.google.android.gms.ads.AdActivity" 
     android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" 
     android:theme="@android:style/Theme.Translucent"/> 

    <!--FACEBOOK LOGIN ACTIVITY (SDK)--> 
    <activity 
     android:name="com.facebook.LoginActivity" 
     android:label="@string/app_name" 
     android:screenOrientation="portrait" 
     android:theme="@style/AppTheme"/> 

    <!--This meta-data tag is required to use Google Play Services.--> 
    <meta-data 
     android:name="com.google.android.gms.version" 
     android:value="@integer/google_play_services_version"/> 

    <!--FACEBOOK STUFF--> 
    <meta-data 
     android:name="com.facebook.sdk.ApplicationId" 
     android:value="@string/facebook_app_id"/> 

    <!--GOOGLE PLUS--> 
    <meta-data 
     android:name="com.google.android.gms.version" 
     android:value="@integer/google_play_services_version"/> 

    <!--CRASHLYTICS--> 
    <meta-data 
     android:name="com.crashlytics.ApiKey" 
     android:value="9249....."/> 

</application> 

Если вы, ребята, на самом деле хотите, вот содержание ASplashscreen

/** 
* @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<EGift>() { 
        @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 && gifts.data != null && 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<String> skus = new ArrayList<String>(); 
        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 > 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 -> 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 -> 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 < start.getTime() || end.getTime() < 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 < start.getTime() || end.getTime() < now) { 
        light.giftExpireStart = null; 
        light.giftExpireEnd = null; 
       } 
      } catch (ParseException e) { 
       //Do nothin' 
      } 
     } 
    } 

} 
+2

Возможно, это проблема с вашим файлом AndroidManifest. Можете ли вы опубликовать его? – curob

+1

Ваш синглтон не должен очищаться, пока ваше приложение не будет (по крайней мере, не на постоянной основе, как вы заявляете). Что, вероятно, происходит, ваше приложение полностью убивается и пытается начать, но, как указано, пропускает ваш заставку. Как заявил @curob, я начну с публикации вашего манифеста и, возможно, вашего кода ASplashscreen (поскольку он мог бы делать то, что вы не думаете, может вызвать проблему, когда это действительно так). – zgc7009

+0

Я отправил свой «AndroidManifest», пожалуйста, проверьте его ... – AndreiBogdan

ответ

5

Это довольно стандартное поведение Android. Когда ваше приложение находится в фоновом режиме, оно может быть убито в любое время по любой причине. Android просто убивает процесс ОС, поддерживающий ваше приложение.

Когда пользователь вернется в ваше приложение (или перезапустит приложение), Android осознает, что ранее он убил ваше приложение, поэтому он создает новый процесс ОС для размещения вашего приложения, а затем создает экземпляр Application для вашего приложения, то он создает экземпляр верхнего уровня Activity в стеке задач (то есть: Activity, который был на экране в то время, когда ваше приложение уходило в фоновый режим), затем он вызывает onCreate() на этом Activity, так что Activity может восстановить себя. Android передает последнее состояние сохраненных состояний в Activity как параметр Bundle на onCreate(). Таким образом, у Activity есть возможность восстановить себя.

Ваш Activity сбой, потому что он полагается на данные, которые должны были быть установлены ранее. В случае, когда Android убивает, а затем воссоздает процесс OS приложения, эти данные исчезли.

Существуют различные способы, чтобы обойти это, один из которых вы уже использовали:

  • в onCreate() всех видов деятельности, проверьте, если «инициализации приложения» была выполнена с использованием public static переменной или синглтон. Если инициализация не была выполнена, вы знаете, что процесс вашего приложения был убит и воссоздан, и вам необходимо либо перенаправить пользователя на ваш корень Activity (то есть: начать приложение заново), либо выполнить инициализацию сразу в onCreate()Activity.

  • Сохраните необходимые данные в файле onSaveInstanceState() и восстановите его в onCreate() и/или onRestoreInstanceState() или обоих.

  • Не храните эти данные в памяти. Храните его в базе данных или в другой, не связанной с памятью, постоянной структуре.

ПРИМЕЧАНИЕ: В общем, вы не должны использовать launchMode="singleTask". Для большинства ситуаций это необязательно и обычно вызывает больше проблем, чем решает. Это не имеет никакого отношения к проблеме, связанной с процессом kill/rereate, которую вы испытываете, но вам все равно следует избегать использования специальных режимов запуска singleTask и singleInstance. Это необходимо, только если вы создаете замену экрана HOME.

3

Решение:

  • вы не должны установить брызговик активность как singleTask, что означает корень стека деятельности, вместо того, чтобы ваш MainActivity должен установить singleTask как корень.

    , когда ваше приложение возвращается на передний план, в onCreate(...), вы должны проверить свои статические рефлексы одноэлементного класса, если не null, прежде чем использовать их, если null, а затем вернуться к вашей активности всплеска (означает перезагрузку и восстановление данных в статические ссылки) , Другими словами, если статические ссылки перерабатываются системой, просто перезапустите приложение.

Надеюсь, что эта помощь!

+0

В общем, вы не должны использовать 'launchMode =" singleTask "'. Для большинства ситуаций это необязательно и обычно вызывает больше проблем, чем решает. –

+0

В этом случае, поскольку всплеск - это активность запуска, поэтому, если вы не хотите возвращаться к всплеску перед выходом из приложения, тогда установить один корень MainActivity может быть одним способом. – Xcihnegn

+0

Существует несколько способов сделать это без использования специальных режимов запуска. Вот два: 1) Splash вызывает 'finish()' при запуске 'Main'. 2) Переопределите 'onBackPressed()' так, чтобы он вызывал 'startActivity()' с 'Splash', добавляя добавочное значение в' Intent', которое сообщает 'Splash' для вызова' finish() 'немедленно. –

2

Ну, на мой взгляд, есть два способа улучшить ваш текущий подход рестарт-приложение (ваш подход хорошо, но немного грязный):

1) Избавиться от ASplashscreen, перемещать загрузки логики в какой-то вспомогательный класс и планировать загрузку ваших данных с MainActivity, показывая ваш всплеск поверх Activity. Если вы используете RelativeLayout для своего MainActivity, вы можете легко достичь этого, добавив невидимый вид с параметрами "match_parent" в нижней иерархии представлений (чтобы перекрывать другие виды) и сделав их видимыми, когда это необходимо. Это чище по сравнению с двумя Activities и перезапуском приложения.

2) Сделайте свой синглтон Parcelable и сохраните его в onSaveInstanceState() в MainActivity. (конечно, ваш синглтон больше не будет синглом с таким подходом). В этом случае Android сохранит ваши данные вместе с MainActivity и после восстановления его в OnCreate() все будет на месте. Это немного чище, чем просто сохранить его до SharedPreferences.

7

При использовании синглтона должен быть некоторыми getInstane методом только с return instance, так что вы можете разместить свой чек внутри него, как следующее:

public static SingletonClass getInstance() { 
    if(instance == null) { 
     instance = StaticMethodToLoadInstance(); 
    } 
    return instance; 
} 

Я думаю, вы можете поместить весь код загрузки данных в статическом StaticMethodToLoadInstance().

ОБНОВЛЕНО

Да, загрузка данных может тратить много времени, так что это может быть сделано другим способом. Прежде всего, создать свой собственный интерфейс:

public static interface OnInstanceLoadedListener { 
    public void onIntsanceLoaded(SingletonClass instance); 
} 

Затем измените getInstance следующими:

public static void getInstance(final OnInstanceLoadedListener listener, Activity context) { 
    final ProgressDialog dialog = null; 
    if(instance == null) {//if there should be loading 
     dialog = StaticMethodToCreateProgressDialog(context); 
     dialog.show(); 
    } 
    new Thread(new Runnable() { 
     @Override 
     public void run() { 
      if(instance == null) { 
       instance = StaticMethodToLoadInstance(); 
      } 
      context.runOnUiThread(new Runnable() { 
       @Override 
       public void run() { 
        listener.onIntsanceLoaded(instance); 
        if(dialog != null && dialog.isShowing()) 
         dialog.dismiss(); 
       } 
      }); 
     } 
    }).start(); 
} 

И ваши getInstance изменения использования от

SingletonClass object = SingletonClass.getInstance(); 
String data = object.getData(); 

в

getInstance(new OnInstanceLoadedListener() { 
    @Override 
    public void onIntsanceLoaded(SingletonClass instance) { 
     String data = instance.getData(); 
    } 
}, YourActivityClass.this); 

Таким образом, ваши данные будут загружаться асинхронно. Да, это выглядит намного сложнее, но он может показывать диалог прогресса - пользователь может видеть, что приложение все еще работает.

+0

Привет. Да, это подход, который я думал о себе, но дело в том, что загрузка данных занимает кучу времени, поэтому я действительно надеялся, что смогу найти источник проблемы, а не делать это. Я мог бы очень легко проверить отсутствие данных, и если это произойдет, запустите весь процесс, чтобы загрузить его снова, но это заставит мое приложение время от времени загружать загрузчик случайно. Я хотел избежать этого ... – AndreiBogdan

+0

Я добавил к своему решению ответа, как я это вижу. Думаю, вам придется потратить некоторое время на загрузку в нулевой экземпляр. – Ircover

1

Я думаю, ваши данные JSON находятся в следующем формате.

{ 
    a : "A", 
    b : "B", 
    c : "C" 
} 

Теперь вы можете иметь класс с именем JsonData, который имеет следующую структуру,

public class JsonData { 
    public String a; 
    public String b; 
    public String c; 
} 

Теперь вы можете конвертировать ваши данные в формате JSON в качестве объекта Java с использованием библиотеки gson.

Теперь введите класс, например ObjectHolder, который структурирован следующим образом.

public class ObjectHolder { 
    public static JsonData jsonData; 
} 

хранить преобразованный объект в ObjectHolder.jsonData. Теперь вы можете получить доступ к этому объекту по всему проекту в любое время.

Примечание: этот объект станет null, когда вы очищаете свое приложение от resent apps list.

Этот метод работает для меня, поэтому я надеюсь, что это тоже поможет.

Смежные вопросы