J'ai rencontré un bogue dans mon application lors de son lancement à l'aide du bouton "Ouvrir" de l'application Google Play Store (précédemment appelé Android Market). Il Il semble que son lancement à partir du Play Store utilise _ Intent
autre chose que son lancement à partir du menu d'applications du téléphone, ce qui entraîne le lancement de plusieurs copies de la même activité en conflit.
Par exemple, si mon application se compose des activités A-B-C, ce problème peut alors générer une pile de A-B-C-A.
J'ai essayé d'utiliser Android:launchMode="singleTask"
sur toutes les activités pour résoudre ce problème, mais cela a pour effet indésirable d'effacer la pile d'activités à la racine, dès que je clique sur le bouton ACCUEIL.
Le comportement attendu est: A-B-C -> HOME -> Et lorsque l'application est restaurée, j'ai besoin de: A-B-C -> HOME -> A-B-C
Existe-t-il un moyen efficace d’empêcher le lancement de plusieurs activités du même type sans réinitialiser l’activité racine lorsque vous utilisez le bouton ACCUEIL?
Ajoutez ceci à onCreate et vous devriez être prêt à partir:
// Possible work around for market launches. See https://issuetracker.google.com/issues/36907463
// for more details. Essentially, the market launches the main activity on top of other activities.
// we never want this to happen. Instead, we check if we are the root and if not, we finish.
if (!isTaskRoot()) {
final Intent intent = getIntent();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(intent.getAction())) {
Log.w(LOG_TAG, "Main Activity is not the root. Finishing Main Activity instead of launching.");
finish();
return;
}
}
Je vais simplement expliquer pourquoi cela échoue et comment reproduire ce bogue par programme afin de l'intégrer à votre suite de tests:
Lorsque vous lancez une application via Eclipse ou Market App, celle-ci se lance avec des indicateurs d'intention: FLAG_ACTIVITY_NEW_TASK.
Lors du lancement via le programme de lancement (home), il utilise des indicateurs: FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_BROUGHT_TO_FRONT | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED et utilise l'action " PRINCIPALE " et la catégorie " LANCEUR ".
Si vous souhaitez reproduire cela dans un scénario de test, procédez comme suit:
adb Shell am start -f 0x10000000 -n com.testfairy.tests.regression.taskroot/.MainActivity
Ensuite, faites ce qui est nécessaire pour passer à l’autre activité. Pour mes besoins, je viens de placer un bouton qui lance une autre activité. Ensuite, retournez au lanceur (maison) avec:
adb Shell am start -W -c Android.intent.category.HOME -a Android.intent.action.MAIN
Et simulez le lancement via le lanceur avec ceci:
adb Shell am start -a "Android.intent.action.MAIN" -c "Android.intent.category.LAUNCHER" -f 0x10600000 -n com.testfairy.tests.regression.taskroot/.MainActivity
Si vous n'avez pas incorporé la solution de contournement isTaskRoot (), cela reproduira le problème. Nous l'utilisons dans nos tests automatiques pour nous assurer que ce bogue ne se reproduira plus jamais.
J'espère que cela t'aides!
Avez-vous essayé le mode de lancement singleTop ?
Voici une partie de la description de http://developer.Android.com/guide/topics/manifest/activity-element.html :
... une nouvelle instance d'une activité "singleTop" peut également être créée pour gérer une nouvelle intention. Cependant, si la tâche cible a déjà une instance existante de l'activité au sommet de sa pile, cette instance recevra la nouvelle intention (dans un appel onNewIntent ()); une nouvelle instance n'est pas créée. Dans d’autres circonstances - par exemple, si une instance existante de l’activité "singleTop" se trouve dans la tâche cible, mais pas en haut de la pile, ou si elle se trouve en haut d’une pile, mais pas dans la tâche cible, une nouvelle instance serait créé et mis sur la pile.
Peut-être que c'est ce numéro ? Ou une autre forme du même bug?
Je pense que la réponse acceptée ( Duane Homick ) a des cas non traités:
Vous avez différents extras (et les doublons d'applications en conséquence):
Voici une solution (SDK_INT> = 11 pour les notifications) qui, à mon avis, permet également de gérer ces cas et les notifications de la barre d'état.
manifeste :
<activity
Android:name="com.acme.activity.LauncherActivity"
Android:noHistory="true">
<intent-filter>
<action Android:name="Android.intent.action.MAIN" />
<category Android:name="Android.intent.category.LAUNCHER" />
<category Android:name="Android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service Android:name="com.acme.service.LauncherIntentService" />
Activité du lanceur :
public static Integer lastLaunchTag = null;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInflater = LayoutInflater.from(this);
View mainView = null;
mainView = mInflater.inflate(R.layout.act_launcher, null); // empty layout
setContentView(mainView);
if (getIntent() == null || getIntent().getExtras() == null || !getIntent().getExtras().containsKey(Consts.EXTRA_ACTIVITY_LAUNCH_FIX)) {
Intent serviceIntent = new Intent(this, LauncherIntentService.class);
if (getIntent() != null && getIntent().getExtras() != null) {
serviceIntent.putExtras(getIntent().getExtras());
}
lastLaunchTag = (int) (Math.random()*100000);
serviceIntent.putExtra(Consts.EXTRA_ACTIVITY_LAUNCH_TAG, Integer.valueOf(lastLaunchTag));
startService(serviceIntent);
finish();
return;
}
Intent intent = new Intent(this, SigninActivity.class);
if (getIntent() != null && getIntent().getExtras() != null) {
intent.putExtras(getIntent().getExtras());
}
startActivity(intent);
}
Service :
@Override
protected void onHandleIntent(final Intent intent) {
Bundle extras = intent.getExtras();
Integer lastLaunchTag = extras.getInt(Consts.EXTRA_ACTIVITY_LAUNCH_TAG);
try {
Long timeStart = new Date().getTime();
while (new Date().getTime() - timeStart < 100) {
Thread.currentThread().sleep(25);
if (!lastLaunchTag.equals(LauncherActivity.lastLaunchTag)) {
break;
}
}
Thread.currentThread().sleep(25);
launch(intent);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void launch(Intent intent) {
Intent launchIintent = new Intent(LauncherIntentService.this, LauncherActivity.class);
launchIintent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIintent.setAction(Intent.ACTION_MAIN);
launchIintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIintent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
if (intent != null && intent.getExtras() != null) {
launchIintent.putExtras(intent.getExtras());
}
launchIintent.putExtra(Consts.EXTRA_ACTIVITY_LAUNCH_FIX, true);
startActivity(launchIintent);
}
Notification :
ComponentName actCN = new ComponentName(context.getPackageName(), LauncherActivity.class.getName());
Intent contentIntent = new Intent(context, LauncherActivity.class);
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 11) {
contentIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // if you need to recreate activity stack
}
contentIntent.addCategory(Intent.CATEGORY_LAUNCHER);
contentIntent.setAction(Intent.ACTION_MAIN);
contentIntent.putExtra(Consts.EXTRA_CUSTOM_DATA, true);
Je me rends compte que la question n’a rien à voir avec Xamarin Android mais je voulais poster quelque chose car je ne l’ai vue nulle part ailleurs.
Pour résoudre ce problème dans Xamarin Android, j’ai utilisé le code de @DuaneHomick et l’a ajouté à MainActivity.OnCreate()
. La différence avec Xamarin est qu’il faut aller après Xamarin.Forms.Forms.Init(this, bundle);
et LoadApplication(new App());
. Ainsi, ma OnCreate()
ressemblerait à ceci:
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
if(!IsTaskRoot) {
Intent intent = Intent;
string action = intent.Action;
if(intent.HasCategory(Intent.CategoryLauncher) && action != null && action.Equals(Intent.ActionMain, System.StringComparison.OrdinalIgnoreCase)) {
System.Console.WriteLine("\nIn APP.Droid.MainActivity.OnCreate() - Finishing Activity and returning since a second MainActivity has been created.\n");
Finish();
return; //Not necessary if there is no code below
}
}
}
* Edit: Depuis Android 6.0, la solution ci-dessus ne suffit pas dans certaines situations. J'ai également paramétré LaunchMode
sur SingleTask
, ce qui semble avoir rendu les choses fonctionnent correctement une fois de plus. Je ne suis pas sûr des effets que cela pourrait avoir sur d’autres choses, malheureusement.
Essayez cette solution:
Créez la classe Application
et définissez-la ici:
public static boolean IS_APP_RUNNING = false;
Ensuite, dans votre première activité (lanceur) dans onCreate
avant setContentView(...)
, ajoutez ceci:
if (Controller.IS_APP_RUNNING == false)
{
Controller.IS_APP_RUNNING = true;
setContentView(...)
//Your onCreate code...
}
else
finish();
P.S. Controller
is ma classe Application
.
J'ai aussi eu ce problème
Android:configChanges="mcc|mnc"
- si vous avez une connexion cellulaire, voir http://developer.Android.com/guide/topics/manifest/activity-element.html#config pour quelle configuration il y a lors du démarrage le système ou Push ouvert ou autre.