9

As per the docs, singleTask activities can't have multiple instances. The only activity of my app is singleTask, and it has 2 instances at the same time.

Steps to recreate the issue

Step 1

Create a new project in Android Studio 3.3.1, Add No Activity, name it singleTaskBug, (package com.example.singletaskbug), using Java language with minimum API level 21 without support for instant apps.

Step 2

Add a new activity manually by editing AndroidManifest.xml then creating a new Java Class in appjavacom.example.singletaskbug named LauncherActivity.

Content of AndroidManifest.xml:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".LauncherActivity"
        android:excludeFromRecents="true"
        android:launchMode="singleTask">

        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.HOME" />
            <category android:name="android.intent.category.DEFAULT" />

        </intent-filter>

    </activity>
</application>

Content of LauncherActivity.java:

package com.example.singletaskbug;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;

public class LauncherActivity extends Activity {

    static int instanceCounter = 0;
    final int instanceId;
    final String TAG = "STB";

    public LauncherActivity() {
        instanceId = ++instanceCounter;
        Log.d(TAG, "Constructed instance " + instanceId + ".");
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "Created instance " + instanceId + ".");
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.d(TAG, "New intent to instance " + instanceId + ".");
    }

    @Override
    protected void onDestroy() {
        Log.d(TAG, "Destroyed instance " + instanceId + ".");
        super.onDestroy();
    }
}

Step 3

Go to RunEdit Configurations... and in the Launch Options section set Launch: to Specified Activity, and Activity: com.example.singletaskbug.LauncherActivity, then click OK, and Run 'app' ShiftF10.

Step 4

Wait until the activity becomes visible. Now on the test device (API 21 in my case), go to settings to set this app as the default launcher. Then press the home button. At this point you'll see this in Logcat:

02-15 17:22:01.906 26429-26429/com.example.singletaskbug D/STB: Constructed instance 1.
02-15 17:22:01.916 26429-26429/com.example.singletaskbug D/STB: Created instance 1.
02-15 17:22:24.228 26429-26429/com.example.singletaskbug D/STB: Constructed instance 2.
02-15 17:22:24.248 26429-26429/com.example.singletaskbug D/STB: Created instance 2.
Tamás Bolvári
  • 2,976
  • 6
  • 34
  • 57
  • Is this a known bug of Android? – Tamás Bolvári Feb 15 '19 at 15:13
  • This app is for API 21. Is the behavior described in the docs relevant for this version? – Tamás Bolvári Feb 15 '19 at 15:14
  • There is a log command in the constructor of the activity, and in onCreate and onDestroy too. The activitiy is instantiated first by the Run command of Android Studio (configured to launch specified activity). Then I navigate to an other app, then press the home button, which should bring my activity to front (it's a launcher app), but a second instance is created instead. In my logs: `Instance 1 is constructed.`, `Instance 1 is created.`, `Instance 2 is constructed.`, `Instance 2 is created.`. Nothing is destroyed. – Tamás Bolvári Feb 15 '19 at 15:27
  • Someone advised me to use `android:taskAffintiy=""`. I've tried it, it doesn't help. – Tamás Bolvári Feb 15 '19 at 15:31
  • Using `android:launchMode="singleInstance"` there is only 1 instance, but this won't let other activities in the same task, so this is not what I need. – Tamás Bolvári Feb 15 '19 at 15:33
  • `adb shell dumpsys activity activities` shows one instance of my activity in a task of a stack, and shows the second instance of my activity in a different stack of an other stack. – Tamás Bolvári Feb 15 '19 at 15:35
  • I've checked tens of SO questions, and all the relevant docs, blog posts etc. Still don't get it. – Tamás Bolvári Feb 15 '19 at 15:38
  • 1
    Sounds odd. What is the smallest amount of code you are able to replicate this with? – Richard Le Mesurier Feb 15 '19 at 15:39
  • 1
    I'll add more code, and step-by-step instructions in a few minutes. – Tamás Bolvári Feb 15 '19 at 15:41
  • 1
    I've created a new Android Studio project from zero, and the bug is there, so I hope it'll be easy to reproduce it. – Tamás Bolvári Feb 15 '19 at 16:27
  • I'll simplify the steps in a few hours. – Tamás Bolvári Feb 15 '19 at 16:48
  • 1
    I think this is a similar issue (asked 6 months ago, no solution): https://stackoverflow.com/questions/51455763/adb-shell-dumpsys-activity-activities-shows-two-instances-of-single-task-in-sepa – Tamás Bolvári Feb 15 '19 at 16:57
  • 1
    Interesting when running this app with `dumpsys` the actual intent for the 2 copies is different. the `rootWasReset` value is also different. Anyway, just to confirm you've definitely replicated the issue reliably. – Richard Le Mesurier Feb 15 '19 at 17:42
  • 1
    And then after a reboot, if you set this app as default launcher, then behaviour becomes as expected. Only one instance exists at any one time. – Richard Le Mesurier Feb 15 '19 at 17:48
  • 1
    Exactly, the first intent's category is launcher, the second's is home. The flags also differ. But there should be only 1 instance even if it gets a hundred different start intents, I guess. I don't know what `rootWasReset` is, I'll read about it. Thank you for confirming the issue. – Tamás Bolvári Feb 15 '19 at 19:04
  • I suppose the problem will show up again after a reboot too, if the activity gets an explicit intent. Not sure though. – Tamás Bolvári Feb 15 '19 at 19:06
  • 1
    I would recommend logging a bug and linking to it in your post. I got no further ideas, raised it on another forum and got no useful feedback. Your example app is great for proving a bug. Hope to see an answer on this one day, even if it is just for "why" it does this. – Richard Le Mesurier Feb 18 '19 at 10:24
  • The phrasing (and particularly the use of tables with titles *like* **Multiple Instances?**) used in the docs is imprecise. God knows how anyone who does not have English as a first language cope with these docs. See [Tasks + Back Stack](https://developer.android.com/guide/components/activities/tasks-and-back-stack). **`Specialized launches ONLY (not recommended for general use)`**. `singleTask` "The system creates the activity at the root of a **new task** and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance". – Jon Goodwin Feb 23 '19 at 02:27
  • 2
    Basically we have to **assume** that the **code is right** (and **not** a bug) and the **docs are wrong** (or misleading/badly worded), because **the code is out there** (being used) and the *docs have bugs*, just like the code. I know, I have raised problems with docs and have had them changed (e.g. [raw assets](https://issuetracker.google.com/issues/79311037) ). – Jon Goodwin Feb 23 '19 at 02:49
  • @JonGoodwin The 4-digit code to my safe is [MTIzNA==](https://codebeautify.org/base64-decode). This might be hard to understand for anyone who doesn't know what base64 encoding is. But the real problem is this: my safe won't open for 1234. It's the same with the docs in this case: even if you understand every word, the information is incorrect. – Tamás Bolvári Feb 23 '19 at 03:11
  • @JonGoodwin I don't know whether this is a bug in Android or in the docs. A buggy code can be out there too, so I can't assume that the code is right just because it's being used. – Tamás Bolvári Feb 23 '19 at 03:23
  • @JonGoodwin ...not to mention if this strange behaviour is caused by a bug of Android Studio, or something else. – Tamás Bolvári Feb 23 '19 at 03:29
  • This is the problem with the written word, it's not mathematically correct, but code is (more so). Is the information incorrect ? (or have you not understood it in the larger context ?) . I don't know. I did not say *"assume that the code is right just because it's being used"*, I said **assume** the docs are wrong **{if they disagree with experiment}** (and raise an issue) . ;O) – Jon Goodwin Feb 23 '19 at 03:32
  • You say **"even if you understand every word, the information is incorrect."**. So you have identified the information that is wrong and can correct/query the documentation, no ? So do it. I've shown you the link already. – Jon Goodwin Feb 23 '19 at 03:42
  • Your whole **premise** that there is **any problem at all** is based on your **understanding** of the documentation (which you provide a link to, but do not say which part is wrong). – Jon Goodwin Feb 23 '19 at 04:00
  • 1
    hmm we seem to have brocken stackoverflow ;O) – Jon Goodwin Feb 23 '19 at 08:02
  • Remove android:excludeFromRecents="true" from activity tag. – Stanojkovic Feb 23 '19 at 11:07
  • Could be related to the strange behaviors described in Unpredictable relaunch — Strange App launcher behaviors (part 2) https://medium.com/@elye.project/three-important-yet-unbeknown-android-app-launcher-behaviors-part-2-139a4d88157 – Xavier Rubio Jansana Mar 01 '19 at 22:28
  • I am struggiling with this as well. Seems like the OS creates a 'special task' for Home (Launcher) activities, which cannot be accessed by third-party apps. Hence, when launching the Home activity from a third-party app, a new task (so a new activity instance ass well) is created. My attempt to fix this goes through creating a shortcut application which indeed brings the activity from the 'Home special task' to the foreground, but can't find the way to do it. The shortcut activity would allow to ignore the way each launcher can start an activity – BamsBamx Sep 07 '20 at 11:29

1 Answers1

0

An Android application can have multiple tasks. Each task can have multiple activities. singleTask and singleInstance control the behaviour of the activity (its uniqueness) inside the task, but it can happen that an App has two or more tasks with the same Activity inside.

This is what is actually seen here, an App with two tasks with an LauncherActivity inside, one for each tasks. As workaround, ensure that there is always one task in the app. On the LauncherActivity onCreate add:

val appTasks = getSystemService(ActivityManager::class.java).appTasks
if (appTasks.size >= 2) {
    appTasks.dropLast(1).forEach { it.finishAndRemoveTask() }
    appTasks.last().moveToFront()
    return
}
AndrewBloom
  • 2,171
  • 20
  • 30