Как предотвратить несколько экземпляров действия, когда оно запускается с разными намерениями - PullRequest
114 голосов
/ 03 декабря 2010

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

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

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

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

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

Ответы [ 11 ]

176 голосов
/ 13 октября 2011

Добавьте это в onCreate, и все будет хорошо:

24 голосов
/ 05 апреля 2014

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

  1. Когда вы запускаете приложение через Eclipseили приложение Market, которое запускается с флагами намерений: 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 ()Обходной путь, это воспроизведет проблему.Мы используем это в нашем автоматическом тестировании, чтобы убедиться, что эта ошибка больше никогда не возникнет.

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

6 голосов
/ 03 декабря 2010

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

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

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

4 голосов
/ 28 сентября 2011

Возможно, это эта проблема ? Или какая-то другая форма той же ошибки?

2 голосов
/ 15 февраля 2016

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

Чтобы исправить это в Android Xamarin, я использовал код из @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
        }
    }
}

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

2 голосов
/ 22 июля 2015

Я думаю, что принятый ответ ( 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" />

Активность запуска :

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);
0 голосов
/ 29 декабря 2015

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

if ( !this.getClass().getSimpleName().equals("YourActivityClassName")) {
    start your activity
}
0 голосов
/ 17 февраля 2015

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

public static boolean IS_APP_RUNNING = false;

Затем в своей первой (Launcher) Activity в onCreate перед setContentView(...) добавьте:

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

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

0 голосов
/ 29 августа 2013

У меня тоже была эта проблема

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

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

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

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

...