Why doesn't my SplashActivity's initialization code get executed? - android

My app has some global/static data structures that need to be initialized before showing the main Activity, so I put the work into onCreate method of my SplashActivity, which just shows a splash image for 2 seconds, starts another activity, and finishes itself:
initializeGlobalData();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent i = new Intent(SplashActivity.this, MainActivity.class);
startActivity(i);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
finish();
}
}, 2000);
Now, my app sometimes mysteriously crashes because of null pointer reference - some global data structures are not initialized. It could only mean that the onCreate method of SplashActivity is not called (right?).
I have no idea how to reproduce this, but it happens quite often. It's possible I left the app in the background, and re-enter. But application level data should not be released, right?

It's possible I left the app in the background, and re-enter. But application level data should not be released, right?
It depends on what you mean when you say "global/static data structures that need to be initialized".
If the user leaves your app, it is expected that the Android OS might terminate your app's process. When this happens, anything that is stored only in memory will be lost.
A common example is e.g. some public static value that you load once and then refer to throughout your application. When the OS terminates your app's process, and then the user returns to your app, that public static value will need to be re-initialized.

Splash Activities are, by nature, short lived and shouldn't be relied upon for any global data structures. If you need such, you need to create an Application class and do all of your global data structure initialization there. Those will not go out of scope for the lifetime of the app.

Why not just initialize them in Application class
public class MyApplication extends Application {
private int globalData = 0;
public int getGlobalData() {
return globalData;
}
public void setGlobalData(int globalData) {
this.globalData = globalData ;
}
#Override
public void onCreate() {
super.onCreate();
setGlobalData(100)
}
}
Change the application tag in manifest file-
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:name=".MyApplication" . // declare the application class
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
Now you can access this anywhere in the app like
((MyApplication) getApplicationContext()).getGlobalData()
The behavior you are having is because if the app is in the background even if it has not been closed, Android OS can clear the initialized variable if not being used.

Related

Importing two Unity projects into Android Studio doesn't work as expected

I am currently training to make applications which use Unity to add some features (AR, VR, etc...). For now I've been working on Android, with Android Studio, and once I'll be done with I'll train on iOS.
My aim is simple: my MainActivity displays two buttons, each one calls a separate Unity project (exported from Unity as Google Android project) to launch its scene.
To do so, I imported those two Unity projects Scene1 and Scene2 as libraries and I call them by starting their activities (see the code below).
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void goToUnity1(View v){
Intent intent = new Intent(this, com.example.unityscene1.UnityPlayerActivity.class);
startActivity(intent);
}
public void goToUnity2(View v){
Intent intent = new Intent(this, com.example.unityscene2.UnityPlayerActivity.class);
startActivity(intent);
}
}
And its AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.multipleunity">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.example.unityscene1.UnityPlayerActivity"></activity>
<activity android:name="com.example.unityscene2.UnityPlayerActivity"></activity>
</application>
The UnityPlayerActivity files in Scene1 and Scene2 are generated by Unity so they are similar (here are their onCreate method):
public class UnityPlayerActivity extends Activity {
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code
// Setup activity layout
#Override protected void onCreate (Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy
mUnityPlayer = new UnityPlayer(this);
setContentView(mUnityPlayer);
mUnityPlayer.requestFocus();
}
...
}
The code compiles without any problem, the application launches and displays the MainActivity with its two buttons. When I click on the first button, it launches the first Unity scene as I expected. However, when I click on the second button it also launches the first Unity scene instead of the second one.
So I tried to understand why this happens and here is what I can tell so far:
I didn't put twice the same Unity project by mistake
Before transforming Scene2 into library it works well on its own
The activity com.example.unityscene2.UnityPlayerActivity is called when I click on the second button
UPDATE
After spending hours on this I'm sure the problem comes from the name of the ressources Unity delivers when I exported in Google Android project which are always the same (unity-classes.jar).
Here's what I read here:
The Android development tools merges the resources of a library project with the resources of the application project. In the case that a resource’s ID is defined several times, the tools select the resource from the application, or the library with highest priority, and discard the other resource.
Adding to that, if I have multiple libraries the priority matches the dependency order (the order they appear in the Gradle dependencies block).
So I tried to reverse the order of the dependencies, I put the second Unity project before the first one and as that was expected both buttons launch the second Scene of Unity.
Now I know that, is there a way to avoid this name conflict ?
I did not solve my problem, but I managed to find a way to avoid it: instead of importing two small Unity projects I decided to make a bigger one which includes two different scenes (one with Vuforia AR and the other with some UI text). Then the challenge was to find a way to call the right Unity scene when I click on the associated button I designed on Android.
Unity
On each scene I created an empty object (I called it SceneManagerObject) which will be attached the script GoToScene. In this script there will be the following methods:
private void ChangeScene(string sceneName)
{
SceneManager.LoadScene (sceneName);
}
public void ReceiveJavaMessage(string message)
{
if (message.Equals ("Scene1") || message.Equals ("Scene2"))
{
ChangeScene (message);
}
else
{
Debug.Log ("The scene name is incorrect");
}
}
The second method will be the one being called from the Java code.
Then I created a UI button on each scene which will call onClick method when clicked, its purpose is to be able to go back to Android without destroying the Unity activity so we can resume it after if I wanted to. For this, I created another Empty Object with the script GoBackToAndroid attached:
public class GoBackToAndroid : MonoBehaviour {
public string activityName; // contains the name of the activity I want to go when I quit this scene
public void onClick()
{
callJavaMethod();
}
private void callJavaMethod()
{
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer);
AnroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
jo.Call("ReceiveUnityMessage", activityName); // Will call a method in Java with activityName as parameter
}
That's it for the Unity, after I exported the project as a Google Android project and I kept it warm for the next step.
Android Studio
After importing the Unity project as a library. I created three activities: MainActivity, SecondActivity and MultipleScenesUnityActivity:
public class MultipleScenesUnityActivity extends UnityPlayerActivity {
public static boolean unityIsRunning = false; // true if a Unity Activity is running on background
#Override protected void onDestroy()
{
unityIsRunning = false;
super.mUnityPlayer.quit();
super.onDestroy();
}
#Override protected void onResume()
{
Intent currentIntent = getIntent();
// If there's no Unity Activity inside the back stack
// (meaning it just had been created)
if (!unityIsRunning)
{
super.mUnityPlayer.UnitySendMessage("SceneManagerObject", "ReceiveJavaMessage", currentIntent.getStringExtra("sceneName");
}
unityIsRunning = true;
super.onResume();
super.mUnityPlayer.resume();
}
public void ReceiveUnityMessage (String messageFromUnity)
{
Intent intent;
if (messageFromUnity.equals("MainActivity"))
{
intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
else if (messageFromUnity.equals("SecondActivity"))
{
intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
else
Log.d("Test", "The activity " + messageFromUnity + " doesn't exist");
}
}
The ReceiveUnityMessage is the method called when we leave the Unity activity and its purpose is to lead us to the activity we desire to go.
Then the MainActivity (the SecondActivity follows the same pattern):
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void goToActivity2(View view) // called when I click on a button
{
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
public void goToUnity(View view) // called when I click on another button
{
Intent intentUnity = new Intent(this, MultipleScenesUnityActivity.class);
if (unityIsRunning)
{
intentUnity.removeExtra("sceneName"); // We have to clean the extra before putting another one otherwise we'll get always the same Unity scene
/* If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought
* to the front of its task's history stack if it is already running. */
intentUnity.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
/* We send a string to Unity which, when received, will change the scene.
* This ONLY works when the Unity activity is running on background. */
UnitySendMessage("SceneManagerObject", "ReceiveJavaMessage", "Scene1");
}
else
{
/* The intent gets a key with its String value. The value has to be the name of the Unity
* scene and is received in UnityActivity1 when the activity starts.*/
intentUnity.putExtra("sceneName", "Scene1");
}
startActivity(intentUnity);
}
}
So what I did is connecting everything by sending messages so I can manage the scene I'd like to launch and the same goes for the activities.
I'm sure there are a better ways to do so, I'm still a novice and I can be clumsy.. Anyway, I managed to avoid my original problem so if anyone faces the same issue this answer might be an interesting alternative.
One possible solution which i came across is dynamically loading multiple smaller unity apks inside a single apk . I have done some research and found out that it is possible to launch one apk from other without even actually installing the apk. This can be done by various methods and there are many library available for doing this to. Few of them are.
DexClassLoader
class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.
Grab-n-Run
Securely load code dynamically into your Android application from APK containers or JAR libraries translated to be executable by both the Dalvik Virtual Machine (DVM) and the Android Runtime (ART).

Android - embedding Unity3d scene in activity - need to unregister receiver?

I've been a SO member for a while but never actually asked a question, so here goes..
My Aim
I'm trying to make an Android app with two activities. The first is a menu screen (using standard Android UI elements) with a button that opens the gameplay activity. The gameplay activity will have some standard Android UI elements and a FrameLayout containing a Unity 3D scene.
I'm using Unity 5 and Eclipse Luna.
What I've got working
I've made the menu screen with a simple button to start the second activity.
I've followed this tutorial and managed to get my Unity scene embedded in my second activity. All good so far...
The problem
The first time I start the gameplay activity it works fine (I can see the Unity scene embedded in my FrameLayout) but if I close the activity (to return to the menu activity) and then start the gameplay activity again I get this error...
Activity has leaked IntentReceiver com.unity3d.player.UnityPlayer$17#41866de0 that was originally registered here. Are you missing a call to unregisterReceiver()?
What I've tried
I've done a lot of searching online and it looks like I need to call something like this in onPause or onDestroy;
m_UnityPlayer.currentActivity.unregisterReceiver(receiver);
...but I don't know what the receiver is so I can't reference it in my unregisterReceiver call. I've tried creating a BroadcastReceiver, registering it and then unregistering it onPause but it makes no difference.
I found a post on the unity forum that asks the exact same question but, frustratingly, it is unanswered (a lot of questions on the Unity forum seem to be unanswered, that's why I came here with this question).
I've also tried calling this method in onDestroy but it actually quits my whole app when I close the gameplay activity.
m_UnityPlayer.quit();
Code Sample
Here's the code for my gameplay activity (I've removed the imports for clarity);
public class MyEmbeddedUnityActivity extends Activity {
private UnityPlayer m_UnityPlayer;
IntentFilter filter = new IntentFilter("");
private BroadcastReceiver bRec = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//String action = intent.getAction();
Log.d("", "onReceive intent: " + intent);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_embedded_unity);
if(m_UnityPlayer==null){
m_UnityPlayer = new UnityPlayer(this);
int glesMode = m_UnityPlayer.getSettings().getInt("gles_mode", 1);
m_UnityPlayer.init(glesMode, false);
m_UnityPlayer.currentActivity.registerReceiver(bRec, filter);
FrameLayout layout = (FrameLayout) findViewById(R.id.my_frame);
LayoutParams lp = new LayoutParams(200, 300);
layout.addView(m_UnityPlayer, 0, lp);
}
}
public void onWindowFocusChanged(boolean hasFocus){
super.onWindowFocusChanged(hasFocus);
m_UnityPlayer.windowFocusChanged(hasFocus);
}
#Override
public void onDestroy (){
//m_UnityPlayer.quit();
m_UnityPlayer.currentActivity.unregisterReceiver(bRec);
super.onDestroy();
}
#Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
m_UnityPlayer.configurationChanged(newConfig);
}
#Override
protected void onResume() {
super.onResume();
m_UnityPlayer.resume();
}
#Override
protected void onPause() {
super.onPause();
m_UnityPlayer.pause();
}
}
And finally...
The steps I followed to embed the Unity scene were written in 2011. The whole process (of grabbing the files from the Unity project's staging area and copying files around etc.) seems really hacky and undocumented.
Is there a better way of embedding Unity scenes in Android activities with Unity 5? Surely adding a Unity scene to an existing app is a fairly common thing to try to do?
Thanks, any help would be greatly appreciated!
So, I did some more searching and eventually found an answer, right here on SO!
As discussed in this answer, adding this line to the manifest fixes the problem;
android:process=":UnityKillsMe"
so the relevant part of the manifest looks like this;
...
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".Studio"
android:configChanges="orientation|keyboardHidden|screenSize"
android:process=":UnityKillsMe"
android:label="#string/title_activity_home" >
</activity>
...
This forces the gameplay activity to launch in a separate process. This means you can call m_UnityPlayer.quit() in the activity's onDestroy method without the whole app closing.
Simple fix, but annoying that it seems to be completely undocumented.
Hope this helps someone!

Android Dev: Do things when App get started

im trying to implement a Sync process for my application. The internal sqlite database has to sync with the remote mysql database.
That works great, but now i want to implement, that always on app start the sync method gets called.
I simply put it in the onCreate() method but now the problem is, that everytime i go back from a further Activity with Intent, the sync method gets called.
Sorry for my bad english, i hope you guys understood.
Thanks in advance,
Joshua
Solution 1
You can extend Application, and sync your data in onCreate() in the Application class.
package com.your.package
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
// Sync data here
}
}
Now open AndroidManifest.xml and find <application> tag. Change it to
<application
android:name=".MyApplication"
...>
Solution 2
Create an SyncActivity, make it the first activity when app open. Sync your data here, and after that, open your original activity by
Intent intent = new Intent(SyncActivity.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
The two flags remove this InitActivity from stack, so when users press back button, they will not go back to this activity.

android: hiding and destroying activity started from a service

I'm new to Android development. I am trying to monetize a live wallpaper that I built and the ad delivery company wants me to call their code from the onCreate of an activity.
The live wallpaper didn't have an activity before I started to monetize it, being an extension to WallpaperService, so I've added one. I've managed to create the activity and make it translucent, but it doesn't close when the dialog closes. I cannot edit the dialog code since it is being created by a call into a .jar, so I thought I could setup a listener for when the dialog is dismissed, but I wasn't able to find any practical examples that might help with the code below.
LWP.java
public class SBLiveWallpaper extends WallpaperService {
super.onCreate();
Intent i = new Intent();
// i.setClass(this, MainActivity.class);
i.setComponent(new ComponentName("appname", "appname.MainActivity"));
// i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
activity_main.xml has no elements (just the RelativeLayout)
MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppBucksAPI.initialize(this, APPID, "APIKEY", true, null, null);
AppBucksAPI.userOptOutDialog(this, "marketname");
}
I could make the activity be non-transparent, and just add a close button, but that is ugly and confuses users.
Edit for clarification: I had tried originally to call the dialog directly from the service's onCreate(). It causes the LWP to crash in the screen where you can make it the active LWP. The error I get is android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application.
I contacted AppBucks support before making the original post here. Their response (pasted below) prompted me to create the translucent activity.:
I believe this error means that there is a problem with the first parameter you are passing to the AppBucksAPI.userOptOutDialog method… the call which looks like this from the docs:
AppBucksAPI.userOptOutDialog(this, "<App Name>");
This call expects an Activity or Activity context as the first parameter. It needs this because our default opt out dialog uses an AlertDialog call, which requires an active Activity for it to display correctly. If you are already creating an Activity along with your service, you should pass that activity as the first parameter instead of “this” (or you could move this call to the onCreate of that activity instead of onCreate for the service).
If you don’t have an Activity in your app, I found this StackOverflow question which has an answer that may help (in a nutshell, you can create a transparent activity when your service starts up, and make the userOptOutDialog call from that instead of your service’s onCreate method):
Display AlertDialog as system overlay window from Service
Unfortunately, the above article covers creating the activity and closing the dialog under the assumption that the person reading it has access to the dialog's code. Since I do not have access to that, because it is imported into my project as a library, I need to know how to listen, from the parent activity, for the child to finish.
I did some digging and it looks like either of these could work, depending on how the activity is started from the dialog call my code makes:
http://developer.android.com/reference/android/app/Activity.html#finishActivityFromChild(android.app.Activity, int)
or
http://developer.android.com/reference/android/app/Activity.html#finishFromChild(android.app.Activity)
I'll give those a try tonight.
The AppBucks SDK also exposes the following functions:
setIconAdsEnabledForUser
setPushAdsEnabledForUser
The AppBucksAPI.userOptOutDialog is basically a convenience function that wraps calls to these in an AlertDialog. For your app, it probably makes more sense to forego the convenience function and write your own AlertDialog that calls the enable functions directly. That way you will have full control over what happens when the dialog is dismissed and can close the new activity you created when you need to.
Looking at the AppBucks API and documentation, I don't think using an Activity is mandatory. It is just the most common way.
I think you can call AppBucks method in your service onCreate as well?
When dismissing your dialog, send an intent to your activity for it to close itself.
For instance
Put this in the dialog dismiss method:
sendBroadcast(new Intent(MainActivity.ACTION_TERMINATE));
Then in the MainActivity add and register a BroadcastReceiver:
Add fields for the receiver and the filter in the activity:
private ActivityBroadcastReceiver mReceiver;
static final IntentFilter mFilter = new IntentFilter();
static {mFilter.addAction(ACTION_TERMINATE);}
Instantiate it in onCreate():
mReceiver = new ActivityBroadcastReceiver();
Register it in onResume():
registerReceiver(mReceiver, mFilter);
Unregister it in onPause():
unregisterReceiver(mReceiver);
And the broadcast receiver's inner class in the activity would look like this
private class ActivityBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
String action = intent.getAction();
if (ACTION_TERMINATE.equals(action)) {
finish();
}
} catch (Exception e) {
Log.w(mTag, "Oops: " + e, e);
}
}
}

Android - Wrong activity sometimes starts

I have an Android App with a number of activities. The wrong activity is being started sometimes.
Normally, an Application subclass starts, then start activity (StartAct... android:name="android.intent.action.MAIN", android:name="android.intent.category.LAUNCHER")
does some work and then launches InitializeActivity. This does some work and then fires off my main display activity (MainAct). The first two activities do some essential initialization including setting a static "isInitialized" flag just before the intent is launched for the MainAct.
Activities are launched with startActivity() using a specific intent (...activity.class specified), and call finish() after startActivity().
However, here is what sometimes happen, and I don't know why...
In short, the app is killed and when the icon is pressed to start it, it jumps straight to the third (MainAct) activity. This causes the app to detect an error (isInitialized flag is false) and stop:
Launch the app normally with the Icon:
...Application subclass starts, also fires up some worker threads
...StartActivity runs, then fires InitializeActivity and finishes
...InitializeActivity runs, then sets isInitialized and starts MainAct and finishes
...MainAct starts, runs okay
...Home button is hit and Angry Birds is run
...MainAct logs onPause, then onStop.
...Worker threads owned by Application subclass continue to periodically do stuff and log.
After 25 minutes, the entire application is suddenly killed. This observation is based on thhe end of logging activity,
Time goes by
Home button hit
Launcher ICON is pressed for the app
Application subclass onCreate is called and returns
*MainAct.onCreate is called! (no StartAct, no InitializeActivity)*
What am I missing?
Note: the initialize flag was added because of this issue. It is set in the only place in the code that starts the main activity, and checked only in onCreate in the main activity.
[per request]
Manifest file (slightly redacted). Note that the service in here is not currently used.
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="xxx.yyy.zzz"
android:versionCode="1" android:versionName="1.0.1">
<application
android:icon="#drawable/icon_nondistr"
android:label="#string/app_name"
android:name=".app.MainApp"
android:debuggable="true">
<activity
android:label="#string/app_name"
android:name=".app.StartAct" android:theme="#android:style/Theme.NoTitleBar">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:label="Html"
android:name=".app.HtmlDisplayAct"/>
<activity
android:label="Init"
android:configChanges="orientation"
android:name=".app.InitializeActivity" android:theme="#android:style/Theme.NoTitleBar"/>
<activity
android:label="MyPrefs"
android:name=".app.PrefsAct" />
<activity
android:label="#string/app_name"
android:theme="#android:style/Theme.NoTitleBar"
android:name=".app.MainAct">
</activity>
<service
android:name=".app.svcs.DataGetterService" />
</application>
<uses-sdk android:minSdkVersion="4"/>
<uses-permission
android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
android:name="com.android.vending.CHECK_LICENSE" />
<uses-feature
android:name="android.hardware.location.network"
android:required="false" />
</manifest>
The fact that the application is killed because of low memory should be transparent to the users. This is why when the application is killed, Android remembers what was the last activity running in this application, and creates directly this activity when the user returns to the application.
Perhaps you could do something in the onCreate() method of your Application (or of your MainAct) to ensure that everything is properly initialized.
By the way, unless you really need to, you shouldn’t have worker threads doing some work when the user is not using your application. Depending on what you do this could drain the battery quickly, or make the user think that it could drain the battery quickly (which is worse, because the user will uninstall your app!)
You could also make the application finish every activity when the user is quitting the application,
This is really a case of "adding insult to injury" - first Android kills my awesome app, and then when the user restarts my app Android tries to be "helpful" by launching the wrong activity, causing my app to crash. Sigh.
Here is my very kludgy workaround to counteract Android's helpfulness. My app requires that StartActivity must be the first activity, so for all other activities I add one line to the onCreate() method. For example:
public class HelpActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (StaticMethods.switchToStartActivityIfNecessary(this)) return; // <- this is the magic line
...
}
...
}
To make this work I've added a switch variable to my Application class:
public class OutBackClientApplication extends Application {
...
// Switch to indicate if StartActivity has been started.
// Values: -1 = StartActity has never been started or this is first invocation.
// 0 = normal situation, StartActivity has been run at least once.
private int _startActivityStatus = -1;
public int getStartActivityStatus() { return _startActivityStatus; }
public void setStartActivityStatus(int startActivityStatus) {
_startActivityStatus = startActivityStatus;
}
...
}
And I have a class called StaticMethods, which includes the following method:
public class StaticMethods {
/**
* Method to test for the problematic situation where Android has previously killed this app, and
* then when the user restarts the app Android tries to be helpful by restarting the activity
* that was in the foreground when it killed the app, instead of starting the activity specified
* in the manifest as the launch activity. See here:
* http://stackoverflow.com/questions/6673271/android-wrong-activity-sometimes-starts
*
* The following line should be added to the onCreate() method of every Activity, except for
* StartActivity, of course:
*
* if (StaticMethods.switchToStartActivityIfNecessary(this)) return;
*/
public static boolean switchToStartActivityIfNecessary(Activity currentActivity) {
OutBackClientApplication outBackClientApplication =
(OutBackClientApplication) currentActivity.getApplication();
if (outBackClientApplication.getStartActivityStatus() == -1) {
currentActivity.startActivity(new Intent(currentActivity, StartActivity.class));
currentActivity.finish();
return true;
}
return false;
}
}
Finally, when StartActivity starts it needs to reset the switch:
public class StartActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((OutBackClientApplication) getApplication()).setStartActivityStatus(0);
...
}
...
}
All that, just to counteract Android's "helpfulness" ...

Categories

Resources