Это довольно загадка:Приложение перезагружается от неправильной деятельности
Открываю приложение. Он запускает действие, которое выступает в качестве заставки (ASplashscreen
), в котором я загружаю некоторые данные из локального хранилища (raw
) и сохраняю его в памяти в singleton object
(статический). После того, как этот процесс осуществляется автоматически движется к основной деятельности (AMain
)
я выйти из приложения, нажав home button
и запускать другие приложения, игры и т.д. Когда я возобновлять мое приложение, приложение выходит из строя внутри метода onCreate
AMain
, потому что он пытается использовать некоторые данные внутри singleton object
, но данные null
. Поэтому он выбрасывает NullPointerException
, когда он это делает. Похоже, что он перезапускает AMain
вместо ASplashscreen
, поэтому у singleton
нет возможности повторной инициализации.
Это происходит случайным образом на нескольких таких попыток ...
У меня есть два предположения ...
Моя первая презумпция, и от того, что я знаю о Android OS, является то, что в то время как я запускал эти другие приложения (особенно игры), один из них требовал большой памяти, поэтому ОС выпустила мое приложение из памяти, чтобы освободить место, поэтому
singleton data
былgarbage collected
.Я также полагаю, что в то время как
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'
}
}
}
}
Возможно, это проблема с вашим файлом AndroidManifest. Можете ли вы опубликовать его? – curob
Ваш синглтон не должен очищаться, пока ваше приложение не будет (по крайней мере, не на постоянной основе, как вы заявляете). Что, вероятно, происходит, ваше приложение полностью убивается и пытается начать, но, как указано, пропускает ваш заставку. Как заявил @curob, я начну с публикации вашего манифеста и, возможно, вашего кода ASplashscreen (поскольку он мог бы делать то, что вы не думаете, может вызвать проблему, когда это действительно так). – zgc7009
Я отправил свой «AndroidManifest», пожалуйста, проверьте его ... – AndreiBogdan