5

I have a service I am reusing (it a both a "bound" and "started" service) in my own app because it does a lot of useful data acquisition I'm interested in. Everything was working but I noticed a problem. An exception is thrown in this code:

Intent dialogIntent = new Intent();
    dialogIntent.setClassName(service.getBaseContext(), "com.mycompany.receiver.ui.DialogActivity"); // names changed to protect the innocent
    dialogIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
    dialogIntent.putExtra("message", message);
    service.getApplicationContext().startActivity(dialogIntent);

The exception warns about "unknown activity" and "have you checked the manifest". Now, this is not caused by me not putting the activity in the manifest. In fact, there really should be an exception thrown here because my application doesn't have this activity! This was an activity written for the original application. I didn't notice it at first because this code is only hit when the data gathered is outside a certain range. I can change the service code, but the service and original application have to continue to work as before. It's just that in my application, I'm not particularly interested in popping up an activity for this particular message (it's not that important to me).
I did think about this, and I could just throw a try/catch block around everything. But that seems a little hacky. If something else is going wrong in the original app/service pair, I want to know about it. I came up with the following ideas, which I need a helping hand to point me in the right direction.

  1. Is there a way for the service to know what applications are currently running?
  2. Or perhaps currently in the foreground?
  3. Or how about currently bound to the service?

I'm interested in 1-3 (for general knowledge and for solving this problem) but 2 is probably the most useful. After all, there could be multiple apps bound to the service, and I'm not sure I want to bring the original app to the foreground over my own just because of this message. And finally.... Forget everything I said, and 4. How would you solve this?

I have the same a very similar problem elsewhere in the code:

Intent toLaunch = new Intent();
    toLaunch.setClassName(context, "com.mycompany.receiver.ui.BankingActivity");

    toLaunch.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent intentBack = PendingIntent.getActivity(context, 0, toLaunch, PendingIntent.FLAG_CANCEL_CURRENT);

    notify.setLatestEventInfo(context, title, message, intentBack);
    notifier.notify(NOTIFY_1, notify);

This one doesn't throw an exception; it puts up a notification and when I click on it, the original app is launched. This might actually be ok, but again, it might be nice not to have this (or perhaps a compromise - have the notifier notify if the app is at least bound, but not necessarily in foreground). Thoughts?

Many thanks, Dave

Dave
  • 8,095
  • 14
  • 56
  • 99
  • i kinda understand that your service and the activity are in two diff android applications. is that right? – Varun Jan 06 '12 at 18:29
  • 2
    and may be before you launch the intent you can possibly check if the `Intent` has a match. you can do this `private boolean isCallable(Intent intent) { List list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); return list.size() > 0; }` to know if your intent can be called. – Varun Jan 06 '12 at 18:31

1 Answers1

7

How would you solve this?

You never really stated what "this" is. I am going to guess that "this" is:

How do I have a service notify a foreground activity of mine if there is one, and otherwise raise a Notification (or perhaps do nothing at all)?

For that "this", use an ordered broadcast. As I wrote in a blog post from a ways back, here is the recipe for either notifying a foreground activity or raising a Notification:

  1. Define an action string you will use when the event occurs that you want to go to the activity or notification (e.g., com.commonsware.java.packages.are.fun.EVENT).

  2. Dynamically register a BroadcastReceiever in your activity, with an IntentFilter set up for the aforementioned action string and with a positive priority (the default priority for a filter is 0). This receiver should then have the activity do whatever it needs to do to update the UI based on this event. The receiver should also call abortBroadcast() to prevent others from getting it. Be sure to register the receiver in onStart() or onResume() and unregister the receiver in the corresponding onStop() or onPause() method.

  3. Register in your manifest a BroadcastReceiver, with an <intent-filter> set up for the aforementioned action string. This receiver should raise the Notification.

  4. In your service (e.g., an IntentService), when the event occurs, call sendOrderedBroadcast().

If you simply want to notify one of your foreground activities, but do nothing if it is not in the foreground, skip step #3.

Here is a sample project that demonstrates this.

Is there a way for the service to know what applications are currently running?

You can ask ActivityManager, but, frankly, that's asking for a fragile app.

Or perhaps currently in the foreground?

Not reliably.

Or how about currently bound to the service?

Only if you track it yourself.

Christopher Mills
  • 711
  • 10
  • 28
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thank you for the quick answer. I'll study your example, and buy your books! You're right; I was long-winded. Let me be succinct. I give you this service, and say, "Use this as you will, but if you change the code it still needs to work with my app". How would you modify the above code? You don't have a "DialogActivity" in your app, and you don't even have my app. It would be nice if you didn't ask me to change my app (although not essential). You don't want to receive a notification on your phone. You don't have my app. You just want to use the service, but not these parts. – Dave Jan 06 '12 at 21:22
  • @Dave: Off the cuff, I'd have the service take a `PendingIntent` as an extra on the `Intent` used with `startService()`, and have the service invoke that `PendingIntent` when whatever event it is you are worrying about occurs. If the `PendingIntent` is `null`, then the service just skips it. Anyone wanting an `Activity` to appear would supply a `getActivity()` `PendingIntent`; anyone wanting a `Notification` would (probably) supply a `getBroadcast()` `PendingIntent` that routes to a `BroadcastReceiver` that raises the `Notification`. – CommonsWare Jan 06 '12 at 22:56