J'ai deux activités
MainActivity
DeepLinkActivity
J'ai tout mis en place pour utiliser la NavUtils
pour naviguer comme conseillé ici , ici et ici .
Ce que je veux réaliser, c'est:
DeepLinkActivity
via un lien profondMainActivity
Tout fonctionne bien tant qu'il y a une tâche de mon application dans les applications récentes.
Cependant, lorsque je balaye mon application des applications récentes, elle se comporte comme suit:
DeepLinkActivity
via un lien profondJ'ai débogué le code et découvert que NavUtils.shouldUpRecreateTask()
renvoie false
.upIntent
tout est réglé sur normal, comme mon Component
set . Mais néanmoins, NavUtils.navigateUpTo()
se comporte comme un appel à finish()
. , rien.
Des idées pour réparer celà?
AndroidManifest.xml
<activity
Android:name=".DeepLinkActivity"
Android:parentActivityName="my.package.MainActivity">
<meta-data
Android:name="Android.support.PARENT_ACTIVITY"
Android:value="my.package.MainActivity"/>
<intent-filter>
<!-- Some intent filter -->
</intent-filter>
</activity>
DeepLinkActivity.Java
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case Android.R.id.home:
Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// create new task
TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
.startActivities();
} else {
// Stay in same task
NavUtils.navigateUpTo(this, upIntent);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
----- Modifier -----
Je me suis rendu compte que quelques Google Apps sont cassés de la même manière. Si vous sautez par exemple Dans Contacts depuis la recherche, appuyez en haut sur AB et vous vous retrouverez sur l'écran d'accueil au lieu de l'application Contacts. (API19/cm11)
Je pense que cette méthode est buggée. J'ai lu le code source de la bibliothèque de support et cette méthode vérifie l'action de l'intention. Elle ne fonctionne que lorsque votre application a été créée précédemment. Ainsi que vous l'avez décrit, si vous la supprimez de l'aperçu des applications, la méthode ShouldUp ne fonctionne plus.
J'ai corrigé cela en utilisant ma propre "shouldUpRecreateTask". Lorsque je reçois une notification qui crée directement une activité (comme votre comportement), j'envoie de mon BroadCastReceiver une action personnalisée à l'intérieur de l'intention. Ensuite, dans ma méthode, je fais la chose suivante:
private final boolean shouldUpRecreateTask(Activity from){
String action = from.getIntent().getAction();
return action != null && action.equals(com.xxxxxx.activities.Intent.FROM_NOTIFICATION);
}
..........................
if (shouldUpRecreateTask(this)) {
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(upIntent)
.startActivities();
} else {
fillUpIntentWithExtras(upIntent);
NavUtils.navigateUpTo(this, upIntent);
}
Ma solution au problème des PO:
public void navigateUp() {
final Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot()) {
Log.v(logTag, "Recreate back stack");
TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent).startActivities();
} else {
NavUtils.navigateUpTo(this, upIntent);
}
}
isTaskRoot()
retournera true si DeepLinkActivity est la racine d'une tâche (le lancement initial de l'application ou l'application était précédemment terminé via le gestionnaire de tâches). De cette façon, je ne perds pas la pile de réserve existante si une activité était lancée via un lien alors que la tâche des applications était déjà au premier plan.
Il semble que NavUtils.shouldUpRecreateTask(this, upIntent)
ne soit pas préparé à ce cas particulier.
Ma solution actuelle consiste à vérifier si la Activity
était liée de manière profonde et à forcer une nouvelle pile de tâches pour des cas comme celui-ci.
Dans mon cas, je passe des objets dans EXTRA
s en interne . Mais ACTION
et Uri
est défini si Activity
est démarré de l'extérieur en suivant un lien vers une URL spécifique ou en touchant des périphériques NFC.
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case Android.R.id.home:
Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)
|| getIntent().getAction() != null) { // deep linked: force new stack
// create new task
TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
.startActivities();
} else {
// Stay in same task
NavUtils.navigateUpTo(this, upIntent);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Utilisez-vous un <activity-alias>
comme MAIN/LAUNCHER? Lorsque je change le <activity-alias>
en <activity>
, la navigation vers le haut fonctionne correctement. Pour une raison étrange, la navigation ne fonctionne pas correctement lorsque des alias sont impliqués.
Il y a aussi un petit problème d'interface utilisateur étrange qui indique que le ActivityManager natif a un bogue. Après avoir accédé à l'activité principale de l'application à l'aide de navigateUpTo()
, appuyez sur la touche Retour de l'appareil. L'application en cours exécute l'animation de sortie d'activité, puis immédiatement après l'application la plus récente, exécute également une animation de sortie d'activité. Cela se produit même si vous avez lancé l'application actuelle à partir de l'écran d'accueil d'Android. Si vous effacez toutes les applications récentes et recommencez, une application aléatoire (c'est-à-dire inattendue) sera présentée lors de la sortie de l'animation.
D'autres applications ne devraient jamais être présentées dans ces cas. Il semble que le gestionnaire d'activités ne gère pas correctement la pile d'historique.
Le bogue incriminé peut être trouvé dans la méthode suivante qui apparaît au bas de this diff :
+ public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
+ Intent resultData) {
+ ComponentName dest = destIntent.getComponent();
+
+ synchronized (this) {
+ ActivityRecord srec = ActivityRecord.forToken(token);
+ ArrayList<ActivityRecord> history = srec.stack.mHistory;
+ final int start = history.indexOf(srec);
+ if (start < 0) {
+ // Current activity is not in history stack; do nothing.
+ return false;
+ }
+ int finishTo = start - 1;
+ ActivityRecord parent = null;
+ boolean foundParentInTask = false;
+ if (dest != null) {
+ TaskRecord tr = srec.task;
+ for (int i = start - 1; i >= 0; i--) {
+ ActivityRecord r = history.get(i);
+ if (tr != r.task) {
+ // Couldn't find parent in the same task; stop at the one above this.
+ // (Root of current task; in-app "home" behavior)
+ // Always at least finish the current activity.
+ finishTo = Math.min(start - 1, i + 1);
+ parent = history.get(finishTo);
+ break;
+ } else if (r.info.packageName.equals(dest.getPackageName()) &&
+ r.info.name.equals(dest.getClassName())) {
+ finishTo = i;
+ parent = r;
+ foundParentInTask = true;
+ break;
+ }
+ }
+ }
+
+ if (mController != null) {
+ ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean resumeOK = true;
+ try {
+ resumeOK = mController.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mController = null;
+ }
+
+ if (!resumeOK) {
+ return false;
+ }
+ }
+ }
+ final long origId = Binder.clearCallingIdentity();
+ for (int i = start; i > finishTo; i--) {
+ ActivityRecord r = history.get(i);
+ mMainStack.requestFinishActivityLocked(r.appToken, resultCode, resultData,
+ "navigate-up");
+ // Only return the supplied result for the first activity finished
+ resultCode = Activity.RESULT_CANCELED;
+ resultData = null;
+ }
+
+ if (parent != null && foundParentInTask) {
+ final int parentLaunchMode = parent.info.launchMode;
+ final int destIntentFlags = destIntent.getFlags();
+ if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
+ (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+ parent.deliverNewIntentLocked(srec.app.uid, destIntent);
+ } else {
+ try {
+ ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
+ destIntent.getComponent(), 0, UserId.getCallingUserId());
+ int res = mMainStack.startActivityLocked(srec.app.thread, destIntent,
+ null, aInfo, parent.appToken, null,
+ 0, -1, parent.launchedFromUid, 0, null, true, null);
+ foundParentInTask = res == ActivityManager.START_SUCCESS;
+ } catch (RemoteException e) {
+ foundParentInTask = false;
+ }
+ mMainStack.requestFinishActivityLocked(parent.appToken, resultCode,
+ resultData, "navigate-up");
+ }
+ }
+ Binder.restoreCallingIdentity(origId);
+ return foundParentInTask;
+ }
+ }
J'ai trouvé ce qui suit a fonctionné pour moi. Si l'activité était profondément liée à une autre activité ou notification, la pile serait créée au fur et à mesure de votre navigation, sinon les activités seraient simplement mises en avant.
case Android.R.id.home:
Intent upIntent = NavUtils.getParentActivityIntent(this);
upIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(upIntent);
finish();
return true;
Pourvu que vous ayez
Android:parentActivityName="parent.activity"
dans votre manifeste
Si vous accédez à votre activité depuis une notification, vous devrez créer la pile lors de la création de la notification, vérifiez cette réponse: NavUtils.shouldUpRecreateTask échoue sur JellyBean
Cela ne fonctionne pas pour moi aussi. Alors je me débrouille comme ça:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == Android.R.id.home) {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
Mon MainActivity est marqué comme singleTask dans le manifeste Android
Android:launchMode="singleTask"
Essaye ça :
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case Android.R.id.home:
Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// create new task
TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
.startActivities();
} else {
// Stay in same task
NavUtils.navigateUpTo(this, upIntent);
}
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
Essayez la réponse suivante en travaillant pour moi dans les deux cas, démarrez l'activité à partir de la notification et démarrez une activité à partir de l'activité parent.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Android.R.id.home:
Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot()) {
TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
.startActivities();
} else {
finish();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}