应用程序从错误的活动重新启动
Posted
技术标签:
【中文标题】应用程序从错误的活动重新启动【英文标题】:App restarts from wrong activity 【发布时间】:2015-04-07 17:18:44 【问题描述】:这是一个难题:
我打开我的应用程序。它启动一个作为启动屏幕 (ASplashscreen
) 的活动,我在其中从本地存储 (raw
文件夹) 加载一些 JSON
数据并将其存储在内存中的 singleton object
(静态) 中。此过程完成后,它会自动移动到主要活动 (AMain
)
我通过按 home button
退出应用程序并运行其他应用程序、游戏等。当我重新打开我的应用程序时,应用程序在 AMain
的 onCreate
方法内崩溃,因为它试图使用一些singleton object
中的数据,但数据是 null
。所以它会抛出一个NullPointerException
。
它似乎重新启动了AMain
而不是ASplashscreen
,因此singleton
没有机会重新初始化。
这会在多次此类尝试中随机发生...
我有两个假设...
根据我对 android 操作系统的了解,我的第一个假设是,当我运行其他应用程序(尤其是游戏)时,其中一个需要大量内存,因此操作系统从内存中释放了我的应用程序腾出空间,所以singleton data
是garbage collected
。
我还推测,虽然 gc
从内存中删除了我的单例,但操作系统仍然保留了一些与当前运行活动的“状态”相关的数据,所以它至少知道它有 @987654338 @活动在我关闭应用程序之前打开。这可以解释为什么它重新打开了 AMain
活动而不是 ASplashscreen
。
我说的对吗?还是有另一种解释为什么我会得到这个异常?欢迎任何建议/澄清。
此外,处理此问题的最佳方法是什么?我的方法是在我尝试使用单例数据时检查它是否存在,如果它为空,则基本上重新启动应用程序。这使它通过ASplashscreen
,所以JSON
被初始化并且一切正常。
编辑根据要求,这是我的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 所说,我将首先发布您的清单和可能的 ASplashscreen 代码(因为它可能正在做一些您认为实际上会导致问题的事情)。 您是否考虑过将初始屏幕活动的launchMode
更改为standard
? (而不是singleTask
)
您也可以尝试将数据存储在 SharedPreferences 对象而不是单例中,这样可以使其更持久。
您在singleton object
为空时重新启动应用程序的方法对我来说听起来不错。我会把这个空检查放在onResume
的AMain
中。
【参考方案1】:
这几乎是标准的 Android 行为。当您的应用程序处于后台时,它可以随时以任何原因被终止。 Android 只会杀死托管您应用的操作系统进程。
当用户返回您的应用(或重新启动您的应用)时,Android 意识到它之前已经杀死了您的应用,因此它会创建一个新的操作系统进程来托管您的应用,然后它会为您的应用实例化 Application
实例,然后它实例化任务堆栈中最顶层的Activity
(即:应用程序进入后台时屏幕上的Activity
),然后在该Activity
上调用onCreate()
,这样@ 987654326@可以自行恢复。 Android 将Activity
的最近保存的实例状态作为Bundle
参数传递给onCreate()
。这样Activity
就有机会自我恢复了。
您的Activity
正在崩溃,因为它依赖于之前应该设置的数据。在 Android 杀死然后重新创建应用的操作系统进程的情况下,这些数据就消失了。
有多种方法可以解决这个问题,其中一种您已经使用过:
在所有活动的onCreate()
中,检查是否使用public static
变量或单例执行了“应用程序初始化”。如果初始化尚未完成,您知道您的应用程序的进程已被杀死并重新创建,您需要将用户重定向到您的根Activity
(即:重新启动应用程序)或立即在@中进行初始化Activity
的987654335@。
将您需要的数据保存在onSaveInstanceState()
中并在onCreate()
和/或onRestoreInstanceState()
或两者中恢复。
不要将此数据保存在内存中。将其保存在数据库或其他非基于内存的持久结构中。
注意:一般情况下,您不应使用launchMode="singleTask"
。在大多数情况下,这是不必要的,而且通常会导致比它解决的问题更多的问题。这与您遇到的进程终止/重新创建问题无关,但您仍应避免使用特殊启动模式singleTask
和singleInstance
。只有在创建主屏幕替换时才需要这些。
【讨论】:
【参考方案2】:在使用单例时,应该有一些 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);
通过这种方式,您的数据将异步加载。是的,它看起来要困难得多,但它可以显示进度对话框 - 用户可以看到应用仍在运行。
【讨论】:
嗨。是的,这是我自己想到的一种方法,但问题是数据的加载需要很多时间,所以我真的希望我能找到问题的根源而不是这样做。我可以很容易地检查是否缺少数据,如果它发生,则启动整个过程以再次加载它,但这会使我的应用程序不时随机地有一个加载器。我想避免这种情况...... 我添加到我的答案解决方案中,因为我可以看到它。我想,无论如何,你应该花一些时间加载到空实例中。【参考方案3】:解决方案:
您不应将启动活动设置为singleTask
,这意味着活动堆栈的根,而您的 MainActivity 应将singleTask
设置为根。
当你的应用程序回到前台时,在onCreate(...)
,你应该在使用它们之前检查你的单例类的静态引用如果不为空,如果为空则跳回你的启动活动(意味着重新加载并将数据恢复到静态引用)。换句话说,如果静态引用被系统回收,那么只需重启你的应用程序。
希望对您有所帮助!
【讨论】:
一般来说你不应该使用launchMode="singleTask"
。在大多数情况下,这是不必要的,而且通常会导致比它解决的问题更多的问题。
在这种情况下,因为splash是启动活动,所以如果你不想在退出应用程序之前返回splash,那么设置一个root MainActivity可能是一种方式
有几种方法可以在不使用特殊启动模式的情况下实现这一目标。这里有两个:1) 启动Main
时会调用finish()
。 2) 覆盖onBackPressed()
,以便它用Splash
调用startActivity()
,在Intent
中添加一个额外的内容,告诉Splash
立即调用finish()
。【参考方案4】:
嗯,在我看来,有两种方法可以改进您当前的重启应用方法(您的方法还可以,但有点脏):
1) 摆脱ASplashscreen
,将加载逻辑移至某个辅助类,并安排从MainActivity
加载数据,同时在Activity
布局上显示您的启动画面。如果您将RelativeLayout
用于您的MainActivity
,您可以通过在视图层次结构的底部添加带有"match_parent"
参数的不可见视图(以与其他视图重叠)并在必要时使其可见,从而轻松实现此目的。与两个Activities
和应用重启相比,这更干净。
2) 制作你的单例 Parcelable
并将其存储在 onSaveInstanceState()
中的 MainActivity
中。 (当然,使用这种方法,您的单身人士将不再是单身人士)。在这种情况下,Android 会将您的数据与MainActivity
一起保存,并且在将其恢复到OnCreate()
之后,一切都会到位。这比将其保存到 SharedPreferences
更简洁。
【讨论】:
【参考方案5】:我猜你的 JSON 数据格式如下。
a : "A",
b : "B",
c : "C"
现在您可以拥有一个名为JsonData
的类,其结构如下,
public class JsonData
public String a;
public String b;
public String c;
现在您可以使用gson 库将您的 json 数据转换为 Java 对象。
现在创建一个类似ObjectHolder
的类,其结构如下。
public class ObjectHolder
public static JsonData jsonData;
将转换后的对象存储在ObjectHolder.jsonData
中。现在您可以随时在整个项目中访问此对象。
注意:当您从resent apps list
清除应用程序时,此对象将变为null
。
这个方法对我有用,所以我希望这对你也有帮助。
【讨论】:
以上是关于应用程序从错误的活动重新启动的主要内容,如果未能解决你的问题,请参考以下文章
从通知意图启动活动时重新创建 Android ViewModel