Image for post
Image for post
Photo by Hal Gatewood on Unsplash

The Heisenbug that had us baffled for days: The strange tale of tasks and launch intents

Pixplicity
Jun 4, 2018 · 3 min read

A few weeks ago, an exceptional QA tester raised this issue:

The app forgets its state when started from the app drawer.

This had the dev team baffled for days, because it was a software bug that seemed to disappear or alter its behavior when one attempted to study it. Was it a crash? Is the launch mode incorrect? Surely it’s not a platform bug?

When Android was in its infancy, a strange phenomenon with activities was observed when launching an app from the IDE:

Activity stack behaves incorrectly during the first run of an app
issue report from 2009

A few years pass, and where this issue is first dismissed, a new one pops up:

Activity stack confused when launching app from browser
issue report from 2014

Again the issue is left for obsolete, but user reports continue to come in left and right that there really is a problem with application states when they’re launched in different ways.

Image for post
Image for post
This is how a simple app with main and detail activities should be resumed from the launcher.
Image for post
Image for post
Unfortunately, this is the reality of how tasks are resumed from the launcher: a new instance of the launch activity is created.

Now, I know what you’re thinking: just set launchMode to singleTop. Here’s the kicker: it doesn’t matter, because upon each launch, MainActivity will appear as another new instance on the back stack. And things get even messier with singleInstance, where all other activities will be mercilessly finished.

Upon each launch, MainActivity will appear as another new instance on the back stack.

Why should I care?

Many users will launch your app from Google Play after installing it. As long as the app is still running a task (i.e. the user presses the home button), this issue will result in your user being brought to a new instance of the launcher activity, regardless of where they left off.

Imagine user Alice installing the fictional SuperMsg messaging app:

  1. SuperMsg is opened through Google Play
  2. Alice composes a new chat message to Bob
  3. She switches out out of app to check something
  4. Alice locates the app on her home screen to resume typing her message
  5. SuperMsg launches to its start screen instead of the composed message

At this point, Alice will perceive that her draft was lost.
And since this was her first experience in using the app, she will no doubt be very frustrated and inclined to uninstall it.

What can I do?

The good news is that this broken behavior can be detected and mitigated. The gist of it is this:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Check that the activity was launched correctly
if (!isTaskRoot
&& intent.hasCategory(Intent.CATEGORY_LAUNCHER)
&& intent.action == Intent.ACTION_MAIN) {
// This activity doesn't belong
finish()
return
}
}

More details

We’ve set up a repository to illustrate this problem. Check out Poor Intentions on GitHub or Google Play.

Pixplicity

The Creative Tech Agency

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store