2

I have an application with only one activity.In the onCreate I am showing a notification, on whose click I show my activity, reusing it if necessary ( or so I thought ) The code is:

package com.example.multiplestartupifusingpendingintent;

import android.app.Activity;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {

    public static final String TAG = "hexor";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d(TAG,
                "onCreate " + getClass().getSimpleName()
                        + Integer.toHexString(hashCode()));

        NotificationManager notifMgr = (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
        showReuseNotification(notifMgr);
    }

    private void showReuseNotification(NotificationManager notifMgr) {
        Intent reuseIntent = new Intent(this, MainActivity.class);
        reuseIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
                | Intent.FLAG_ACTIVITY_SINGLE_TOP);

        PendingIntent reusePendingIntent = PendingIntent.getActivity(this, 2,
                reuseIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        Notification.Builder builder = new Builder(this)
                .setSmallIcon(R.drawable.ic_launcher)
                .setContentIntent(reusePendingIntent)
                .setContentTitle("Resue from memory");

        notifMgr.notify(2, builder.build());
    }

    @Override
    protected void onDestroy() {

        Log.d(TAG,
                "onDestroy " + getClass().getSimpleName()
                        + Integer.toHexString(hashCode()));
        super.onDestroy();
    }
}

The manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.multiplestartupifusingpendingintent"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="19"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.multiplestartupifusingpendingintent.MainActivity"
            android:configChanges="orientation|screenSize"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

My problem is a behavior happening, that I don't understand:

  1. If I start the application (first onCreate call ), then put it to backround, then click on its desktop icon, the same instance is reused. In logcat I can see just 1 onCreate call. It is what I want

  2. If I start the application ( first onCreate call ), put it to background, click the notification , then click the desktop icon ( second onCreate call ) , then I see 2 onCreate calls in logcat. As I press back I go twice before I can exit the app, and normally I see 2 onDestroy calls, since I have 2 identical stacked activities.

Why is the last behavior happening? Why is Android not reusing the existing activity, and creates 2 copies of them stacked? More weird, why is it doing only in case 2/why is it not doing it in case 1 also?

Thanks

adragomir
  • 457
  • 4
  • 16
  • 33

3 Answers3

9

In the first case you comment, your activity is paused and has not been destroyed, so it moves from paused state to resumed state. You may check the activity lifecycle to learn more about it.

You should set

android:launchMode="singleInstance"

to your activity (in the manifest) and receive new intents inside:

  • onCreate(...) in case the activity has been destroyed
  • onNewIntent(...) in case the activity is paused/resumed

Hope it helps.

Junior Buckeridge
  • 2,075
  • 2
  • 21
  • 27
  • Yes but this introduces a new problem: normally for an Android app, when you click the desktop icon, you launch the app in the last state ( with all the activities on top of the main activity ). But if you use singleInstance for the main activity, then all activities on top of it, will be cleared. So, how can I preserve the stack of activities when the user clicks the desktop icon in this case ( of using singleInstance for main activity )? Thank you – adragomir Dec 22 '13 at 07:44
  • Using special launch modes (`singleInstance` and `singleTask`) is generally a bad idea, because it tends to bring with it a host of other side-effects that you don't want. You should avoid this. – David Wasser Dec 27 '13 at 13:03
2

If you only have a single Activity, you should be able to do this in showReuseNotification():

    Intent reuseIntent = new Intent(this, MainActivity.class);
    reuseIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    PendingIntent reusePendingIntent = PendingIntent.getActivity(this, 2,
            reuseIntent, PendingIntent.FLAG_UPDATE_CURRENT);

This will bring an existing instance of your application to the foreground (if there is one), or start a new one (if there isn't already an active one).

David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • Hi, If I have one because, I guess it's on the top the stack... But if I have more? – adragomir Dec 27 '13 at 14:41
  • If you have more you need to decide how you want it to behave. There's a few different possibilities. Did you try this suggestion for your problem? – David Wasser Dec 27 '13 at 16:42
  • It works for 1 activity... I guess, like I've said, it's because it's at the top of its stack. I am interested in knowing how to do it for an app with many activities on top of main one. But, I didn't specify in my answer how many activities. If you think it's fair to grant you correct answer please say so, I will. But I'd be more glad if you would like to solve it together for multiple activities ( without using SINGLE_TASK). Thanks – adragomir Dec 27 '13 at 16:59
  • 1
    As I said, if you have multiple activities, you just need to be clear about how you want it to behave. This works for the root activity (ie: the activity that has `ACTION=MAIN` and `CATEGORY=LAUNCHER`). It has nothing to do with what activity is on the top of the stack. This code will always bring your application to the foreground in whatever state it is in (if it is active), or launch the root activity of your application if it is not active. Regardless of how many activities you have in the stack. – David Wasser Dec 27 '13 at 17:43
0

In the second case, your activity is still visible, but not in the foreground, because the notification bar uses the trick of stacking its own transparent activity on top of the second activity to make it appear as if a status bar was getting pulled down on top of the Activity.

And because of this special case, Android assigns a different priority to the activity underlying that foreground activity.

Let me look for a reference to what I'm saying. I'll be back soon.

Stephan Branczyk
  • 9,363
  • 2
  • 33
  • 49
  • can you go in detail about this. Moreover, what do I need to do to behave in the second case as in first? Please explain the priority involvement in this, thanks – adragomir Dec 22 '13 at 15:36