0

We have already the check of CATEGORY_MAIN and !isTaskRoot() but even then 2 instances of activity are launched.

SplashActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Log.i("OnCreate method.");

    if(checkIfActivityIsBroughtToFront() || checkIfActivityIsRootTask()) {
        return; // Found that if we finish and don't return then it will run the code below, hence start the recovery task.
    }

    Log.i("Checking if Recovery is required ...");
    new RecoveryTask(SplashActivity.this, this).execute();
}

private boolean checkIfActivityIsBroughtToFront() {
    if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
        // Activity was brought to front and not created,
        // Thus finishing this will get us to the last viewed activity
        Log.i("Detecting a brought to front, no need for recovery.");
        finish();

        return true;
    }

    return false;
}

private boolean checkIfActivityIsRootTask() {
    if (!isTaskRoot()) {
        final Intent intent = getIntent();
        final String intentAction = intent.getAction();
        if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
            Log.i("Main Activity is not the root. " + "Finishing Main Activity instead of launching.");
            finish();

            return true;
        }
    }

    return false;
}

Logs

2015-10-22 13:42:25.581 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.587 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.637 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.638 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.828 +0300 GeoFenceManager INFO[pool-5-thread-1] - Removing geofences ...
2015-10-22 13:42:25.872 +0300 GeoFenceManager INFO[pool-5-thread-2] - Removing geofences ...

Manifest

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="x.y.z"
    android:installLocation="internalOnly" >
<application
        android:name=".global.GlobalInstance"
        android:allowBackup="true"
        android:allowClearUserData="true"
        android:hardwareAccelerated="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:largeHeap="true"
        android:persistent="true" >

        <activity
            android:name=".activity.SplashActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:theme="@style/Theme.Background" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <receiver
            android:name=".receiver.BootUpReceiver"
            android:enabled="true"
            android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>

UPDATE: This is happening after restart, the BOOT_COMPLETED listener is following

public class BootUpReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i = new Intent(context, SplashActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(i);
    }
}

Any idea how this can be prevented?

Ahmed
  • 636
  • 1
  • 14
  • 31
  • have a look here for possible tips on how to avoid multiple instances of an activity -- http://stackoverflow.com/questions/10614565/how-to-avoid-multiple-instances-of-same-activity – Tasos Oct 23 '15 at 09:51
  • This is the root activity, so some of the tips mentioned might not apply here. Although i need to check if "singleTask", "singleInstance" is the solution or not. – Ahmed Oct 23 '15 at 10:00
  • post the manifest and post the code that starts the activity. – David Wasser Oct 23 '15 at 17:22
  • What does `RecoveryTask` do? – David Wasser Oct 23 '15 at 17:24
  • Added manifest file. This activity is started by the launcher, not from anyother activity. RecoveryTask, checks the state and takes user to the appropriate activity. – Ahmed Oct 23 '15 at 17:54
  • Adding android:launchMode="singleInstance" in your manifest file should do the job. Let me know if that helped. – Reaz Murshed Oct 26 '15 at 06:23
  • singleTask can slove this; and do u only have one activity?more detail.What do you want to achieve?ur RecoveryTask likes to skip from SplashActivity to SplashActivity? – tiny sunlight Oct 26 '15 at 06:26
  • RecoveryTask decides if we want to take the user to login activity or logged in state. @ReazMurshed i will add it, the logs are from production and this is happening for small amount of users. – Ahmed Oct 26 '15 at 07:43
  • Could you post the code of `RecoveryTask` ? If it's the launcher activity, and it's not internally called, then it should never be called twice. Can you tell me what' the API level of the users who got this issue ? – Henry Oct 28 '15 at 17:09
  • The recovery task read preferences and take user to the next activity according to his/her preferences. The android versions are Android v4.4.4 95% Android v4.3 1% Others 1% – Ahmed Oct 28 '15 at 17:37

3 Answers3

2

Add android:launchMode="SingleTask" to the splash activity element in the xml. And then on leaving the activity i.e. navigating away from the splash call finish(). The reason you should use this pattern as opposed to "SingleInstance" is that the user can never navigate back to the splash with the back-key (as this is not normal behavior).

Steven Mark Ford
  • 3,372
  • 21
  • 32
  • Yes I have added singleTask, and I am already calling finish() when leaving from the activity. I hope this solves the problem, it'll be going to production in the coming week. – Ahmed Oct 30 '15 at 07:48
  • Also, the singleInstance, didn't work well. I tried it, the app started to crash in the recovery task. – Ahmed Oct 30 '15 at 07:49
  • 1
    It should solve the problem since running splash in its own task will force the entire task to be deleted on finish and effectively removing it from the Android activity backstack (i.e. user cannot navigate back to it) – Steven Mark Ford Oct 30 '15 at 11:57
  • It didn't solve the problem, I am still seeing the same 2 instances being launched in parallel after adding singleTask and finish() in splash activity. – Ahmed Nov 10 '15 at 14:51
  • Oh one more thing, this activity is also launched from boot_completed receiver, and its happening after restart. The intent passes flag NEW_TASK. – Ahmed Nov 10 '15 at 15:28
0
   private boolean checkIfActivityIsRootTask() { 
    if (!isTaskRoot()) {
    final Intent intent = getIntent();
    final String intentAction = intent.getAction();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
          Log.i("Main Activity is not the root. " + "Finishing Main Activity   instead of launching.");
        finish();

        return true; 
      } 
   } 

return false; 
} ?

if this activity is roottask,it return false; what's RecoveryTask for?

tiny sunlight
  • 6,231
  • 3
  • 21
  • 42
0

You said:

We have already the check of CATEGORY_MAIN and !isTaskRoot()

But both the check failed. In your logs that you attached, when both instances are created, neither Detecting a brought to front, no need for recovery. nor Main Activity is not the root. " + "Finishing Main Activity instead of launching. is printed in the logs. This means that in both the methods the if part is never encountered and hence finish() never gets executed. Both the methods returned false.

Also, just because you call finish() and return in onCreate(), it doesn't mean that onStart() or onResume() won't be called.

Why does this happen? Launcher activity will never be called twice (considering that you don't internally start the activity). Probably a bug in the some SDK that the user has.

POSSIBLE FIX: You could try setting android:launchMode="singleTop" in your manifest for the SplashActivity. This will make sure only one instance is maintained.

Henry
  • 17,490
  • 7
  • 63
  • 98
  • Yes the checks failed. I have added `singleTask` in my next build to check if it solves the problem. Do you think `singleTask` is not suited here. Since the documentation says there will be only one instance of singleTask and singleInstance (singleInstance was causing problems when I minimize the app and open again from launcher, that's why I changed it to singleTask. – Ahmed Oct 28 '15 at 17:41
  • 1
    `singleTask` wont cause any issue. If an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one. – Henry Oct 28 '15 at 17:45
  • Refering to this thread, I think http://stackoverflow.com/questions/3268962/when-is-it-necessary-to-use-singletop-launchmode-in-an-android-widget-or-applica singleTop is not suited since the SplashActivity might not be at the top then it will lauch a new one. – Ahmed Oct 28 '15 at 17:48
  • Yes. But `singleTop` is different from `singleTask`. – Henry Oct 28 '15 at 17:56