Resume singleTask activity

2019-09-02 09:48发布

问题:

I am trying to "resume" a single task activity so it appears in the foreground when a user clicks my notification. (Same behavior as if the user tapped on the app icon from the applications menu.)

My notification creates a PendingIntent which broadcasts an action that is received by my broadcast receiver. If the app is in not in the foreground, I try to resume the app. Additionally, I'm trying to pass a message to my onResume function through the intent. However, I'm hitting an error:

Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

Despite this error, my app is being resumed...don't understand why. However, my extras are not being passed to my onResume function.

So first I create a notification.

public static class MyNotificationCreator {

  private static final int MY_NOTIFICATION_ID = 987;

  public static void createNotification(Context context) {
    Intent openAppIntent = new Intent(context, MyReceiver.class);
    openAppIntent.setAction("PleaseOpenApp");
    PendingIntent pi = PendingIntent.getBroadcast(context, /*requestCode*/0, openAppIntent, /*flags*/0);

    Notification notification = ne Notification.Builder(context)
      .setContentTitle("")
      .setContentText("Open app")
      .setSmallIcon(context.getApplicationInfo().icon)
      .setContentIntent(pi)
      .build();

    NotificationManager notificationManager = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(MY_NOTIFICATION_ID, notification);  }
}

Which broadcasts "PleaseOpenApp" for MyReceiver.

public class MyReceiver extends BroadcastReceiver {
  @Override
  public void onRecieve(Context context, Intent intent) {
    if (intent.action() == "PleaseOpenApp" && !MyPlugin.isForeground) {
      PackageManager pm = context.getPackageManager();
      //Perhaps I'm not supposed to use a "launch" intent?
      Intent launchIntent = pm.getLaunchIntentForPackage(context.getPackageName());
      //I'm adding the FLAG_ACTIVITY_NEW_TASK, but I'm still hitting an error saying my intent does not have the FLAG_ACTIVITY_NEW_TASK...
      launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      launchIntent.putExtra("foo", "bar");
      context.startActivity(launchActivity);
    } else {
      //do other stuff
    }
  }
}

My plugin keeps track of whether or not we're in the foreground. Also, it tries to get "food" after my receiver attempts to start the app.

public class MyPlugin extends CordovaPlugin {

  public static boolean isForeground = false;

  @Override
  public void initialize(CordovaInterface cordova, CordovaWebView webview) {
    super.initialize(cordova, webview);
    isForeground = true;
  }

  @Override
  public void onResume(boolean multitasking) {
    isForeground = true;
    String foo = activity.getIntent().getStringExtra("foo");
    Log.d("MyPlugin", foo); //foo is null after clicking the notification!
  }

  @Override
  public void onPause(boolean multitasking) {
    isForeground = false;
  }

  @Override
  public void onDestroy() {
    isForeground = false;
  }
}

Note: because I'm using cordova my activity has a singleTask launchMode.

Also, I'm new to Android development so any help about resuming activities not in the foreground vs resuming activities that have been destroyed and info about general concepts / best practices that I'm not understanding would be appreciated!

回答1:

I don't think your Broadcast/Broadcast Receiver pattern is necessary.

Intents can be used to directly launch an activity, and when you build the Intent, you can add the extras. Then, your activity onResume() can extract them directly.

Here is a sample Intent and PendingIntent construction that can be sent in a notification:

Intent startActivity = new Intent(context, MyActivity.class);
// You can experiment with the FLAGs passed here to see what they change
startActivity.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
                    .putExtra("Extra1", myExtra1)
                    .putExtra("Extra2", myExtra2)
                    // ADDING THIS MAKES SURE THE EXTRAS ATTACH
                    .setAction("SomeString"); 

// Then, create the PendingIntent
// You can experiment with the FLAG passed here to see what it changes
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, startActivity, PendingIntent.FLAG_UPDATE_CURRENT);

// Then, create and show the notification
Notification notif = new NotificationCompat.Builder(context)
            .setSmallIcon(R.drawable.my_small_icon)
            .setContentTitle(myTitle)
            .setContentText(myContent)
            .setOngoing(isOngoingNotif)
            .setAutoCancel(shouldAutoCancel)
            .setOnlyAlertOnce(shouldAlertOnce)
            .setContentIntent(pendingIntent)
            .build();

NotificationManagerCompat manager = NotificationManagerCompat.from(context);
manager.notify(MY_NOTIFICATION_ID, notif);


回答2:

In your code you are using a "launch Intent" to resume your application. You've added "extras" to the Intent but they will never be seen.

If your app is running, but in the background, and you call startActivity() with a "launch Intent", all this does it bring your task from the background to the foreground. It does not deliver the Intent to the Activity!.

A "launch Intent" does exactly the same thing as when you press the app icon of an app on the HOME screen (if it is already running, but in the background). This just brings the existing task in its current state, from the background to the foreground.

If you want to delivery "extras" to your app, you cannot use a "launch Intent". You must use a regular 'Intent. Depending on your architecture, you could either start a newActivity(which would get the "extras" inonCreate(), or you could start an existingActivity(which would get the "extras" inonNewIntent()`.