2010-12-03 2 views
103

Я столкнулся с ошибкой в ​​своем приложении, когда он запущен с помощью кнопки «Открыть» на рынке Android. Кажется, что запуск с рынка использует другое намерение, а затем запускает его из меню приложений телефона. Это приводит к появлению нескольких копий того же самого действия, которые противоречат друг другу.Как предотвратить несколько экземпляров активности при ее запуске с различными намерениями

Например, Если мое приложение состоит из действий A-B-C, то вышеуказанная проблема может привести к стеку A-B-C-A.

Я попытался использовать android:launchMode="singleTask" во всех действиях по устранению этой проблемы, но у него есть нежелательный побочный эффект очистки стека активности от root, когда я нажимаю HOME.

Пример: ABC -> HOME -> A, когда мне нужен ABC -> HOME -> ABC

Есть хороший способ, чтобы предотвратить запуск нескольких мероприятий одного и того же типа без переустановку к корню активность при использовании HOME?

ответ

6

Вы пробовали singleTop режим запуска?

Вот некоторые описания из http://developer.android.com/guide/topics/manifest/activity-element.html:

... новый экземпляр «SingleTop» деятельности также могут быть созданы для обработки нового намерения. Однако, если задача target уже имеет существующий экземпляр активности в верхней части своего стека , этот экземпляр получит новое намерение (в вызове onNewIntent()); новый экземпляр не создан. В других обстоятельствах - например, если существующий экземпляр деятельности «SingleTop» в задаче целевой , но не на вершине стека, или если он находится в верхней части стека, но не в целевой задаче - новый экземпляр будет создан и помещен в стек.

+2

Я думал об этом, но что, если активность не находится в верхней части стека? Например, кажется, что singleTop предотвратит A-A, но не A-B-A. – bsberkeley 2010-12-03 01:23:36

+0

Можете ли вы достичь того, чего хотите, используя методы SingleTop и Finishing в Activity? – elevine 2010-12-03 01:37:20

+0

Я не знаю, выполнит ли он то, что я хочу. Пример: Если я нахожусь в действии C после появления A и B, тогда запускается новый актив A, и у меня будет что-то вроде C-A, не так ли? – bsberkeley 2010-12-03 06:02:52

-2

попробовать с помощью SingleInstance запуска режима с аффинностью установлена ​​в allowtaskreparenting Это всегда будет создавать свою деятельность в новых задачах, но и позволяет его reparenting. Проверьте dis: Affinity attribute

-1

У меня была такая же проблема, и я исправил ее, используя следующее решение.

В вашей основной деятельности добавить этот код на верхней части методы 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" /> 

надеюсь, что это вам поможет.

163

Добавьте к этому 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;  
    } 
} 
0

Я имел эту проблему также

  1. Не называйте отделку(); в домашней деятельности он будет работать бесконечно - домашняя деятельность вызывается ActivityManager по завершении.
  2. Обычно, когда конфигурация изменяется (например, поворот экрана, изменение языка, изменение службы телефонии, т. Е. Mcc mnc и т. Д.), Активность воссоздается - и если домашняя активность запущена, она снова обращается к A., для чего необходимо добавить манифест android:configChanges="mcc|mnc" - если у вас есть подключение к сотовой сети, см. http://developer.android.com/guide/topics/manifest/activity-element.html#config, для которого конфигурация существует при загрузке системы или нажатии кнопки открытия или что-то еще.
19

Я просто хочу, чтобы объяснить, почему это не удается, и как воспроизвести эту ошибку программно, так что вы можете включить это в набор тестов:

  1. При запуске приложения через Eclipse, или рынок Приложение, оно запускается с флагами намерений: FLAG_ACTIVITY_NEW_TASK.

  2. При запуске через пусковую установку (home) используются флаги: FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_BROUGHT_TO_FRONT | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, и использует действие «MAIN» и категории «LAUNCHER».

Если вы хотите воспроизвести это в тестовом случае, выполните следующие действия:

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

Затем сделать все необходимое, чтобы добраться до другой деятельности. Для моих целей я просто поместил кнопку, которая запускает другое действие. Затем вернитесь к пусковой (дома) с:

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, это будет воспроизведите проблему. Мы используем это в нашем автоматическом тестировании, чтобы убедиться, что эта ошибка больше не встречается.

Надеюсь, это поможет!

0

Попробуйте это решение:
Создать Application класс и определить там:

public static boolean IS_APP_RUNNING = false; 

Тогда в первом (Launcher) Активность в onCreate перед тем setContentView(...) добавить:

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

PS Controller - мой класс Application.

2

Я думаю, что принятый ответ (Duane Homick) имеет необработанное случаи:

У вас есть различные дополнительные услуги (и приложения дубликатов в результате):

  • при запуске приложения из Маркета или домашней значок экрана (который находится на рынке автоматически)
  • при запуске приложения с помощью ракеты-носителя или созданного вручную значок главного экрана

Вот решение (SDK_INT> = 11 для уведомлений), которым я тоже обращаюсь с этими случаями и уведомлениями о статусной панели.

Manifest:

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

Launcher активность:

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

Я нашел способ предотвращения запуска те же действия, это работает для меня

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

Я понимаю, что вопрос не имеет ничего общего с Xamarin Android, но я хотел чтобы опубликовать что-то, потому что я не видел его нигде.

Чтобы исправить это на Xamarin Android, я использовал код от @DuaneHomick и добавлен в MainActivity.OnCreate(). Разница с Xamarin заключается в том, что он должен идти после 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 
     } 
    } 
} 

* Edit: Так как Android 6.0, выше решение не достаточно для определенных ситуаций. Я также установил LaunchMode на SingleTask, что, похоже, еще раз заработало. Не уверен, какие последствия это может иметь для других вещей, но, к сожалению.