Dennis
Dennis

Reputation: 1168

Service's Intent.replaceExtras(bundle) not working as expected

I have a Service that is, for this discussion, holding a number and a string to be presented to an Activity. Think of it like a Service-oriented copy buffer. Upon initiation, the Service is started (using startService) like so:

Intent srvIntent = new Intent(this, SetManager.class);
Bundle bundle = new Bundle();
bundle.putLong(getString(R.string.intent_key_setnbr), setNbr);
bundle.putString(getString(R.string.intent_key_setmsg), setMsg);
srvIntent.putExtras(bundle);
startService(srvIntent);
return true;

All working fine. The service starts up, displays the message identified by setMsg in a notification (e.g. "MESSAGE1").

When the user touches on the Notification, an Activity is started, and that Activity, among other things, updates the setNbr and the setMsg, and sends those back to the Service. The service in turn updates the Notification with the new message, e.g. "MESSAGE2" (which works fine also).

But here's the rub: When the user touches this Notification, the Original setMsg ("MESSAGE1") is what the Activity shows, rather than "MESSAGE2". I've put in Log.d() stuff to see where the process is going wrong, but I do not see it. The Service records the incoming "MESSAGE2" just fine, and hits the code after the replaceExtras(). In fact, the incoming values have to be right, else the Notification would not be updated to "MESSAGE2". So it seems that the Notification Intent's extras bundle is not being updated properly, since all the other round-trip stuff works fine. Or, less likely, the Activity only works properly on the first try?

So I question my approach to updating the Notification. I believe it to be "by the book," but as a relative android newbie there's a lot of book that I haven't read yet. Would appreciate your pointers. Here's the Service code:

package yada.yada.boom.Setlines;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.text.ClipboardManager;
import android.util.Log;

public class NoteManager extends Service {

    private NotificationManager mNM = null;
    private Notification notification = null;
    private Context context;
    private final CharSequence contentTitle = "Setlines";
    private CharSequence contentText;
    private Intent notificationIntent;
    private PendingIntent pendingIntent;
    private int notificationId = 0;
    private final String MY_TAG = "SetlinesSvc";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        long setNbr = 0L;
        String setMsg = null;

        super.onStartCommand(intent, flags, startId);
        context = getApplicationContext();
        if (intent != null) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                setNbr = extras.getLong(getString(R.string.intent_key_setnbr));
                setMsg = extras
                        .getString(getString(R.string.intent_key_setmsg));
            }
        }
        if ((setNbr == 0L) || (setMsg == null)) {
            Log.d(MY_TAG, "Setting default message.");
            setNbr = 0L;
            setMsg = "Click to view setMsg.";
        } else {
            Log.d(MY_TAG, "Got set number (" + setNbr + ") and string ("
                    + setMsg.substring(0, 10) + "...) from intent.");
        }
        if (notification == null) {
            createNotification(setNbr, setMsg);
        } else {
            updateNotificationText(setNbr, setMsg);
        }
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        // Log.d(MY_TAG, "onDestroy() called");
        mNM.cancel(notificationId);
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    // Create a status bar notification
    void createNotification(long setNbr, String setMsg) {
        CharSequence tickerText = null;
        int icon = 0;

        // Log.d(MY_TAG, "createNotification called");
        mNM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        icon = R.drawable.ic_notification;
        tickerText = "Setlines";

        // Instantiate the Notification

        long when = System.currentTimeMillis();

        notification = new Notification(icon, tickerText, when);

        context = getApplicationContext();
        contentText = setMsg;
        notificationIntent = new Intent(this, SetServiceMenu.class);
        Bundle bundle = new Bundle();
        bundle.putLong(getString(R.string.intent_key_setnbr), setNbr);
        if (setNbr != 0L) {
            bundle.putString(getString(R.string.intent_key_setmsg), setMsg);
            Log.d(MY_TAG, "Added extras: SetNbr(" + setNbr + ") SetMsg("
                    + setMsg.substring(0, 10) + "...)");
            notificationIntent.putExtras(bundle);
        }
        pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
                PendingIntent.FLAG_ONE_SHOT);
        notification.setLatestEventInfo(context, contentTitle, contentText,
                pendingIntent);
        notificationId += 1; // Bump the notification ID number
        // Pass the Notification to the NotificationManager:
        Log.d(MY_TAG, "createNotification() ... passing notification");
        mNM.notify(notificationId, notification);
        startForeground(notificationId, notification);
    }

    void updateNotificationText(long setNbr, String setMsg) {
        contentText = setMsg;
        notificationIntent = new Intent(this, SetServiceMenu.class);
        Bundle bundle = new Bundle();
        bundle.putLong(getString(R.string.intent_key_setnbr), setNbr);
        bundle.putString(getString(R.string.intent_key_setmsg), setMsg);
        // notificationIntent.putExtras(bundle);
        notificationIntent.replaceExtras(bundle);
            Log.d(MY_TAG, "Replaced extras: SetNbr(" + setNbr + ") SetMsg("
                    + setMsg.substring(0, 10) + "...)");
        pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
                PendingIntent.FLAG_ONE_SHOT);
        notification.setLatestEventInfo(context, contentTitle, contentText,
                pendingIntent);

        mNM.notify(notificationId, notification);
    }

}

... and here is the onCreate from the Activity being called:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    context = this.getApplicationContext();
    Bundle extras = getIntent().getExtras();
    String setMsg = "";

    dbAdapter = new SetDbAdapter(context);

    dbAdapter.openReadable();
    if (savedInstanceState != null) {
        setNbr = savedInstanceState.getLong("setNbr");
        // Log.d(MY_TAG, "Retrieved set# (" + setNbr + ") from intent.");
        setMsg = savedInstanceState.getString("setMsg");
        // Log.d(MY_TAG, "Retrieved text (" + setMsg.substring(0, 10)
        // + "...) from intent.");
        setMsg = dbAdapter.getSetById(setNbr);
    } else if (extras != null) {
        setNbr = extras.getLong(getString(R.string.intent_key_setnbr));
        // Log.d(MY_TAG, "Retrieved set# (" + setNbr + ") from extras.");
        setMsg = extras.getString(getString(R.string.intent_key_setline));

        if ((setMsg == null) && (setNbr != 0)) {
            setMsg = dbAdapter.getSetById(setNbr);
        }
    }
    if ((setNbr == 0) || (setMsg == null)) {
        setNbr = nextMessageNbr();
        setMsg = messageText(setNbr);
    }
    dbAdapter.close();
    svcTagNbr = setNbr;
    svcTagline = setMsg;
    d = new myDialog(this);
    d.show();
    notifyService(false);
}

Finally, here is notifyService()

private void notifyService(boolean getNew) {

  // Here we get a new setMsg, and notify the server to create
  // notification with it.
  long mySetNbr = svcSetNbr;
  String mySetMsg = svcSetMsg;

  if (getNew) {
    mySetNbr = nextMessageNbr();
    mySetline = messageText(mySetNbr);
  }

  Intent srvIntent = new Intent(context, SetManager.class);
  Bundle bundle = new Bundle();
  bundle.putLong(getString(R.string.intent_key_setnbr), mySetNbr);
  bundle.putString(getString(R.string.intent_key_setmsg), mySetMsg);
  srvIntent.putExtras(bundle);
  startService(srvIntent);
}

This is all compounded by the fact that I cannot seem to find a way to debug a Service. Any pointers on debugging Services would also be much appreciated!

Upvotes: 2

Views: 1888

Answers (1)

Dennis
Dennis

Reputation: 1168

Found it! The problem was in a missing flag. Apparently even though we use updateExtras(), Android doesn't pay attention unless you use a special flag (FLAG_UPDATE_CURRENT) on the getActivity() method.

From the docs at http://developer.android.com/reference/android/app/PendingIntent.html :

... if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent. This can be used if you are creating intents where only the extras change, and don't care that any entities that received your previous PendingIntent will be able to launch it with your new extras even if they are not explicitly given to it.

So the new code looks like:

void updateNotificationText(long setNbr, String setMsg) {
    contentText = setMsg;
    Bundle bundle = new Bundle();
    bundle.putLong(getString(R.string.intent_key_setnbr), setNbr);
    bundle.putString(getString(R.string.intent_key_setmsg), setMsg);
    notificationIntent.replaceExtras(bundle);
    pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
          PendingIntent.FLAG_ONE_SHOT
        + PendingIntent.FLAG_UPDATE_CURRENT);
    notification.setLatestEventInfo(context, contentTitle, contentText,
            pendingIntent);
    mNM.notify(notificationId, notification);
}

Upvotes: 6

Related Questions