如何防止以不同意图启动的多个活动实例

当我的应用程序使用Android市场上的“打开”button启动时,遇到了一个错误。 看起来,从市场上推出它使用不同的意图,然后从手机的应用程序菜单启动它。 这导致了正在启动的相同活动的多个副本,这些副本彼此冲突。

例如,如果我的应用程序包含活动ABC,则上述问题可能会导致堆栈ABCA。

我尝试使用android:launchMode="singleTask"在所有活动来解决这个问题,但它有不必要的副作用,每当我打开HOME清除活动堆栈根。

例如: ABC – > HOME – > A当我需要的是ABC – > HOME – > ABC

使用HOME时,是否有防止启动同一types的多个活动而不重置为根活动的好方法?

添加到onCreate,你应该很好去:

 // 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; } } 

我只是要解释它为什么失败,以及如何以编程方式重现此错误,以便您可以将其纳入您的testing套件:

  1. 当您通过Eclipse或Market App启动应用程序时,它将以意向标志启动:FLAG_ACTIVITY_NEW_TASK。

  2. 当通过启动器(主页)启动时,它使用标志:FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_BROUGHT_TO_FRONT | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,并使用动作“ MAIN ”和类别“ LAUNCHER ”。

如果您想在testing用例中重现这一点,请使用以下步骤:

 adb shell am start -f 0x10000000 -n com.testfairy.tests.regression.taskroot/.MainActivity 

然后做任何需要去其他活动。 为了我的目的,我只是放了一个button,开始另一个活动。 然后,回到发射器(家)与:

 adb shell am start -W -c android.intent.category.HOME -a android.intent.action.MAIN 

并模拟通过启动器启动它:

 adb shell am start -a "android.intent.action.MAIN" -c "android.intent.category.LAUNCHER" -f 0x10600000 -n com.testfairy.tests.regression.taskroot/.MainActivity 

如果您尚未合并isTaskRoot()解决方法,则会重现此问题。 我们在自动testing中使用它来确保这个错误再也不会发生。

希望这可以帮助!

你有没有尝试过SingleTop启动模式?

以下是http://developer.android.com/guide/topics/manifest/activity-element.html中的一些说明:;

…也可以创build一个“singleTop”活动的新实例来处理新的意图。 但是,如果目标任务已经在其堆栈顶部存在一个活动的现有实例,那么该实例将接收新的目标(在onNewIntent()调用中)。 一个新的实例不会被创build。 在其他情况下,例如,如果“singleTop”活动的现有实例位于目标任务中,但不在堆栈顶部,或者位于堆栈顶部,但不在目标任务中 – 新的实例将被创build并推入堆栈。

也许是这个问题 ? 还是其他forms的相同的错误?

我认为接受的答案( Duane Homick )有未经处理的情况:

你有不同的额外(和结果重复的应用程序):

  • 当您从市场或主屏幕图标(由市场自动放置)启动应用程序时,
  • 当您通过启动程序启动应用程序或手动创build的主屏幕图标

这是一个解决scheme(通知SDK_INT> = 11),我相信处理这些情况和状态栏通知也。

清单

  <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" /> 

启动器活动

 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); } 

服务

 @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); } 

通知

 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); 

我意识到这个问题与Xamarin Android没有任何关系,但我想发布一些东西,因为我没有在其他地方看到它。

为了解决这个问题,在Xamarin Android中,我使用了@DuaneHomick中的代码,并添加到MainActivity.OnCreate() 。 与Xamarin的区别在于Xamarin.Forms.Forms.Init(this, bundle);必须去Xamarin.Forms.Forms.Init(this, bundle);LoadApplication(new App()); 。 所以我的OnCreate()将如下所示:

 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 } } } 

*编辑:从Android 6.0开始,上述解决scheme对于某些情况是不够的。 我现在也将LaunchMode设置为SingleTask ,这似乎使事情再次正常工作。 不幸的是,不知道这可能对其他事情有什么影响。

我也有这个问题

  1. 不要叫finish(); 在家庭活动中,它会无休止地运行 – ActivityManager在完成时正在调用家庭活动。
  2. 通常当configuration改变(即旋转屏幕,改变语言,电话服务改变,即mcc mnc等)活动重新创build – 如果家庭活动正在运行,然后再次调用A.为需要添加到清单android:configChanges="mcc|mnc" – 如果您有连接到手机,请参阅http://developer.android.com/guide/topics/manifest/activity-element.html#config在启动系统或推开放或其他。;

试试这个解决scheme
创buildApplication类并在其中定义:

 public static boolean IS_APP_RUNNING = false; 

然后在你的第一个(Launcher)Activity中,在setContentView(...)之前添加onCreate

 if (Controller.IS_APP_RUNNING == false) { Controller.IS_APP_RUNNING = true; setContentView(...) //Your onCreate code... } else finish(); 

PS Controller是我的Application类。

我有同样的问题,我使用下面的解决scheme来解决它。

在您的主要活动中,将这些代码添加到onCreate方法的顶部:

 ActivityManager manager = (ActivityManager) this.getSystemService( ACTIVITY_SERVICE ); List<RunningTaskInfo> tasks = manager.getRunningTasks(Integer.MAX_VALUE); for (RunningTaskInfo taskInfo : tasks) { if(taskInfo.baseActivity.getClassName().equals(<your package name>.<your class name>) && (taskInfo.numActivities > 1)){ finish(); } } 

不要忘记在清单中添加这个权限。

 < uses-permission android:name="android.permission.GET_TASKS" /> 

希望它可以帮助你。

我find了一种方法来防止开始同样的活动,这对我来说很好

 if ( !this.getClass().getSimpleName().equals("YourActivityClassName")) { start your activity } 

尝试使用SingleInstance启动模式与亲和力设置allowtaskreparenting这将始终在新任务中创build活动,但也允许其reparationing。 检查dis: 亲和属性