9

I've got an Android application which needs to be woken up sporadically throughout the day.

To do this, I'm using the AlarmManager to set up a PendingIntent and have this trigger a BroadcastReceiver. This BroadcastReceiver then starts an Activity to bring the UI to the foreground.

All of the above seems to work, in that the Activity launches itself correctly; but I'd like the BroadcastReceiver to notify the Activity that it was started by the alarm (as opposed to being started by the user). To do this I'm trying, from the onReceive() method of the BroadcastReceiver to set a variable in the extras bundle of the intent, thus:

    Intent i = new Intent(context, MyActivity.class);
    i.putExtra(wakeupKey, true);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(i);

In the onResume() method of my Activity, I then look for the existence of this boolean variable:

protected void onResume() {
    super.onResume();

    String wakeupKey = "blah";      
    if (getIntent()!=null && getIntent().getExtras()!=null)
        Log.d("app", "onResume at " + System.currentTimeMillis() + ":" + getIntent().getExtras().getBoolean(wakeupKey));
    else
        Log.d("app", "onResume at " + System.currentTimeMillis() + ": null");
}

The getIntent().getExtras() call in onResume() always returns null - I don't seem to be able to pass any extras through at all in this bundle.

If I use the same method to bind extras to the PendingIntent which triggers the BroadcastReceiver however, the extras come through just fine.

Can anyone tell me what's different about passing a bundle from a BroadcastReceiver to an Activity, as opposed to passing the bundle from an Activity to a BroadcastReceiver? I fear I may be doing something very very obvious wrong here...

Tom Hume
  • 399
  • 1
  • 4
  • 12
  • Curiouser and curiouser... it just started working (in the emulator), exactly as I'd expect. Worked repeatedly for a bit. I shut down the emulator, restarted (without any code changes or recompilation), and I'm back to the old behaviour (Activity launches but no Bundle passed through). I can see logs for both runs still - in the former, Bundles are coming through, in the latter not. – Tom Hume Apr 11 '10 at 11:46

5 Answers5

30

Just to make it clear (because I used a lot of time figuring out how to make it work)

In the service class that extends BroadcastReceiver. Put in the following code in onReceive()

Intent intent2open = new Intent(context, YourActivity.class);
intent2open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent2open.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
String name = "KEY";
String value = "String you want to pass";
intent2open.putExtra(name, value);
context.startActivity(intent2open);

The FLAG_ACTIVITY_SINGLE_TOP makes sure the apps doesn't re-open if already open. This means that the "old" intent that opened YourActivity in the first place is re-used and it will NOT contain the extra values. You have to catch them in another method called onNewIntent() in YourActivity.

public class YourActivity extends Activity {
    private String memberFieldString;

    @Override
    public void onCreate(Bundle savedInstanceState) { 
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);

         // Code doing your thing...
    } // End of onCreate()

    @Override
    protected void onNewIntent(Intent intent) {
    Log.d("YourActivity", "onNewIntent is called!");

    memberFieldString = intent.getStringExtra("KEY");

    super.onNewIntent(intent);
} // End of onNewIntent(Intent intent)

    @Override
    protected void onResume() {
        if (memberFieldString != null) {
            if (opstartsIntent.getStringExtra(KEY) != null) {
               Log.d("YourActivity", "memberFieldString: "+ memberFieldString);
            } else {
               Log.d("YourActivity", "The intent that started YourActivity did not have an extra string value");
            }
        }
    } // End of onResume()

}  // End of YourActivity

Please note the two if statements - the onResume() does not know if it's called after OnCreate()->OnStart() OR onRestart()->onStart()
Please see: http://www.anddev.org/images/android/activity_lifecycle.png

It's just used to test if the app is launched by the user (intent with no extras) OR by the BroadcastReceiver (intent with extras).

Bhavin Nattar
  • 3,105
  • 2
  • 18
  • 30
Norfeldt
  • 5,230
  • 13
  • 70
  • 118
  • but stl i am facing same problem ? ..my activity starting again and again..have any other update? – CoDe Jan 18 '12 at 08:44
  • 1
    This might sound stupid: But have you checked if you have set the right flag and followed the description to every point? It took me a long time to figure this out and I realized that not all flags work like I would expect them to do. If you add more flags than described then try to remove them. Make that work and then add them one by one to see what happens. – Norfeldt Jan 19 '12 at 17:31
  • 1
    What is "opstartsIntent" I can not find/compile that. – MrHIDEn Aug 10 '13 at 23:29
  • Wont new task be replaced with single top using `intent2open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent2open.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);`? I would rather make it `intent2open .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);` – aggregate1166877 Oct 28 '13 at 08:00
5

This BroadcastReceiver then starts an Activity to bring the UI to the foreground.

This may be the crux of your problem here. Try overriding onNewIntent() and seeing if the Intent passed to it has your extra. If so, that's because of the way you set up the activity in the manifest (e.g., you're using singleTop) and the fact that, in this specific case, the activity already existed.

You might also consider getting rid of i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); and see if that changes matters.

The upshot is that what you're doing -- putting the extra in the Intent for startActivity() -- should work just fine. In fact, you can see examples of that here. This suggests it's something funky about the activity (e.g., singleTop) or the way you're invoking the activity (e.g., FLAG_ACTIVITY_NEW_TASK).

EDIT:

Since my first shots didn't work, and given your "curiouser" comment above...

This feels a bit like a PendingIntent -- with those, unless you take steps, you will not be able to update extras.

On a whim, try adding a second <intent-filter> to your activity in the manifest, just on some unique action string, and try starting your activity from your receiver using that. Or, just toss some action string into your Intent that the receiver is using for startActivity(), without messing with the manifest.

CommonsWare
  • 910,778
  • 176
  • 2,215
  • 2,253
  • Nope, onNewIntent() doesn't get it either... The FLAG_ACTIVITY_NEW_TASK is there because without it I get a RuntimeException: E/AndroidRuntime( 274): java.lang.RuntimeException: Unable to start receiver com.futureplatforms.twitchr.BirdArrivesAlarmReceiver: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want? – Tom Hume Apr 11 '10 at 12:09
  • I edited my earlier answer with other things you can try -- commenting here in case you don't get notified on an answer edit... – CommonsWare Apr 12 '10 at 12:39
  • 1
    OK, retried again. onNewIntent() does get it now - I changed the activity launchmode to singleTask, and it's working exactly as I had hoped. I then overrode newIntent and set a variable which the activity could pick up on elsewhere to determine it had been woken by an alarm. Thankyou! – Tom Hume Apr 18 '10 at 17:13
2

Instead of getIntent().getExtras().getBoolean(wakeupKey) it is more conventional to write getIntent().getBooleanExtra(wakeupKey, defaultValue). I can't be sure if this is related to your problem, but there is some stuff to do with creating a bundle inside getExtras() that I'm not sure about, so it might be worth a go anyway.

Jim Blackler
  • 21,883
  • 11
  • 82
  • 100
  • Thanks Jim. Just tried making that change, but the value is always false (and I'm only ever setting it to true). – Tom Hume Apr 11 '10 at 11:48
1

Set flag SingleTop works (don't mix with other flags)

Intent intent = new Intent(ContextManager.getContext(),OrderList.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Bundle bundle = new Bundle();       

bundle.putString("username",username);
bundle.putString("password",password);

intent.putExtras(bundle);
startActivityForResult(intent,0); 
0

Just override the onNewIntent like this and the Bundle var will be available in the onresume method:

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
}
RClemens
  • 191
  • 1
  • 7