다른 인 텐트로 시작될 때 활동의 여러 인스턴스를 방지하는 방법 프로그램 메뉴 아이콘에서

Google Play 스토어 앱 (이전에는 Android 마켓이라고 함) 의 “열기” 버튼을 사용하여 애플리케이션을 시작할 때 내 애플리케이션에서 버그를 발견했습니다 . Play 스토어 Intent에서 실행하는 것은 전화의 응용 프로그램 메뉴 아이콘에서 실행 하는 것과 다른 것을 사용하는 것 같습니다 . 이로 인해 서로 충돌하는 동일한 활동의 ​​여러 복사본이 실행됩니다.

예를 들어 내 앱이 활동 ABC로 구성된 경우이 문제로 인해 ABCA 스택이 발생할 수 있습니다.

android:launchMode="singleTask"이 문제를 해결하기 위해 모든 활동을 사용하려고 시도했지만 홈 버튼을 누를 때마다 활동 스택을 루트로 지우는 원치 않는 부작용이 있습니다.

예상되는 동작은 다음과 같습니다. ABC-> HOME-> 앱이 복원되면 ABC-> HOME-> ABC가 필요합니다.

HOME 버튼을 사용할 때 루트 활동으로 재설정하지 않고 동일한 유형의 여러 활동이 시작되는 것을 방지하는 좋은 방법이 있습니까?



답변

이것을 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;
    }
}

답변

실패한 이유와이 버그를 프로그래밍 방식으로 재현하여 테스트 스위트에 통합 할 수있는 방법을 설명하겠습니다.

  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 “를 사용합니다.

테스트 케이스에서이를 재현하려면 다음 단계를 사용하십시오.

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 () 해결 방법을 통합하지 않은 경우 문제가 재현됩니다. 이 버그가 다시 발생하지 않도록 자동 테스트에서이를 사용합니다.

도움이 되었기를 바랍니다!


답변

singleTop 실행 모드 를 사용해 보셨습니까 ?

다음은 http://developer.android.com/guide/topics/manifest/activity-element.html의 일부 설명입니다 .

… 새로운 인 텐트를 처리하기 위해 “singleTop”활동의 새 인스턴스를 만들 수도 있습니다. 그러나 대상 작업의 스택 맨 위에 이미 활동의 기존 인스턴스가있는 경우 해당 인스턴스는 onNewIntent () 호출에서 새 인 텐트를 수신합니다. 새 인스턴스가 생성되지 않습니다. 다른 상황에서 (예 : “singleTop”활동의 기존 인스턴스가 대상 작업에 있지만 스택의 맨 위에 있지 않은 경우 또는 스택의 맨 위에 있지만 대상 작업에는없는 경우) 새 인스턴스가 생성되고 스택에 푸시됩니다.


답변

아마도이 문제 일까요? 아니면 같은 버그의 다른 형태?


답변

받아 들인 답변 ( Duane Homick )에는 처리되지 않은 사례가 있다고 생각합니다 .

다른 추가 기능이 있으며 결과적으로 앱이 중복됩니다.

  • 마켓 또는 홈 화면 아이콘 (마켓에서 자동으로 배치됨)에서 애플리케이션을 실행할 때
  • 런처 또는 수동으로 만든 홈 화면 아이콘으로 응용 프로그램을 시작할 때

다음은 이러한 사례와 상태 표시 줄 알림도 처리 할 수있는 솔루션 (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);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 이후로 위의 솔루션은 특정 상황에 충분하지 않습니다. 나는 이제 설정 LaunchModeSingleTask일을 다시 한 번 제대로 작동 만든 것으로 보인다. 불행히도 이것이 다른 것들에 어떤 영향을 미칠지 확실하지 않습니다.


답변

나는 같은 문제가 있었고 다음 솔루션을 사용하여 수정했습니다.

주요 활동에서 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" />

도움이되기를 바랍니다.