Program Club

Android : launchMode =“singleTask”의 버그?

proclub 2020. 11. 28. 12:49
반응형

Android : launchMode =“singleTask”의 버그? -> 활동 스택이 보존되지 않음


내 주요 활동 Aandroid:launchMode="singleTask"매니페스트에 설정 되어 있습니다. 이제 거기에서 다른 활동을 시작할 때마다, 예를 들어 휴대폰 B에서을 눌러 HOME BUTTON홈 화면으로 돌아간 다음 다시 내 앱으로 돌아갑니다. 앱의 버튼을 누르거나 HOME BUTTON버튼을 눌러 가장 최근 앱을 표시합니다. 내 활동 스택을 보존 A하지 않고 예상되는 활동 대신에 바로 반환 합니다 B.

여기에 두 가지 동작이 있습니다.

Expected: A > B > HOME > B
Actual: A > B > HOME > A (bad!)

내가 놓친 설정이 있습니까 아니면 버그입니까? 후자의 경우 버그가 수정 될 때까지 이에 대한 해결 방법이 있습니까?

참고 :이 질문은 이미 여기 에서 논의 되었습니다 . 그러나 아직 이에 대한 실질적인 해결책은없는 것 같습니다.


이것은 버그가 아닙니다. 기존 singleTask활동이 시작되면 스택에서 그 위에있는 다른 모든 활동이 삭제됩니다.

HOME활동을 다시 눌렀다 가 실행하면 ActivityManger인 텐트를 호출합니다.

{act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]flag=FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_RESET_IF_NEEDED cmp=A}

결과는 A> B> HOME> A입니다.

A의 launchMode가 "Standard"이면 다릅니다. A를 포함하는 태스크는 포 그라운드로 나오고 이전과 동일한 상태를 유지합니다.

예를 들어 "표준"활동을 생성 할 수 있습니다. C의 onCreate 메소드에서 실행기 및 startActivity (A)로 C

또는

인 텐트를 A로 호출 할 때마다 플래그를 제거 launchMode="singleTask"하고 설정 하십시오.FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP


에서 http://developer.android.com/guide/topics/manifest/activity-element.htmlsingleTask

시스템은 새 작업의 루트에 활동을 만들고 인 텐트를 여기에 라우팅합니다. 그러나 액티비티의 인스턴스가 이미 존재하는 경우 시스템은 새 인스턴스를 만드는 대신 onNewIntent () 메서드에 대한 호출을 통해 인 텐트를 기존 인스턴스로 라우팅합니다.

즉, action.MAIN 및 category.LAUNCHER 플래그가 Launcher에서 애플리케이션을 대상으로하는 경우 시스템은 새 작업을 만들고 새 ActivityA를 루트로 설정하는 대신 인 텐트를 기존 ActivityA로 라우팅합니다. 오히려 ActivityA가 존재하는 기존 작업 위의 모든 활동을 분해하고 onNewIntent ()를 호출합니다.

singleTop 및 singleTask의 동작을 모두 캡처하려면 onCreate ()에서 singleTop 활동을 호출 한 다음 자체적으로 완료되는 singleTask launchMode를 사용하여 SingleTaskActivity라는 별도의 "대리자"활동을 만듭니다. singleTop 활동은 여전히 ​​MAIN / LAUNCHER 인 텐트 필터를 사용하여 애플리케이션의 기본 Launcher 활동으로 계속 작동하지만 다른 활동이이 singleTop 활동을 호출하려는 경우 singleTask 동작을 보존하기 위해 대신 SingleTaskActivity를 호출해야합니다. singleTask 활동에 전달되는 의도는 singleTop 활동에도 전달되어야하므로 singleTask 및 singleTop 실행 모드를 모두 갖고 싶었 기 때문에 다음과 같은 것이 저에게 효과적이었습니다.

<activity android:name=".activities.SingleTaskActivity"
              android:launchMode="singleTask">

public class SingleTaskActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        intent.setClass(this, SingleTop.class);
        startActivity(intent);
    }
}

그리고 singleTop 활동은 singleTop 실행 모드를 계속 유지합니다.

    <activity
        android:name=".activities.SingleTopActivity"
        android:launchMode="singleTop"/>

행운을 빕니다.


스테판, 이것에 대한 답을 찾았나요? 나는 이것에 대한 테스트 케이스를 작성하고 동일한 (복잡한) 동작을보고 있습니다 ... 누군가가 와서 명백한 것을 볼 경우를 대비하여 아래 코드를 붙여 넣겠습니다.

AndroidManifest.xml :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example" >

  <uses-sdk android:minSdkVersion="3"/>

  <application android:icon="@drawable/icon" android:label="testSingleTask">

    <activity android:name=".ActivityA"
              android:launchMode="singleTask">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>

    <activity android:name=".ActivityB"/>

  </application>
</manifest>

ActivityA.java :

public class ActivityA extends Activity implements View.OnClickListener
{
  @Override
  public void onCreate( Bundle savedInstanceState )
  {
    super.onCreate( savedInstanceState );
    setContentView( R.layout.main );
    View button = findViewById( R.id.tacos );
    button.setOnClickListener( this );
  }

  public void onClick( View view )
  {
    //Intent i = new Intent( this, ActivityB.class );
    Intent i = new Intent();
    i.setComponent( new ComponentName( this, ActivityB.class ) );
    startActivity( i );
  }
}

ActivityB.java :

public class ActivityB extends Activity
{
  @Override
  public void onCreate( Bundle savedInstanceState )
  {
    super.onCreate( savedInstanceState );
    setContentView( R.layout.layout_b );
  }
}

minSdkVersion을 아무 소용이 없도록 변경해 보았습니다. 이것은 최소한 다음과 같은 문서 에 따르면 버그 인 것 같습니다.

As noted above, there's never more than one instance of a "singleTask" or "singleInstance" activity, so that instance is expected to handle all new intents. A "singleInstance" activity is always at the top of the stack (since it is the only activity in the task), so it is always in position to handle the intent. However, a "singleTask" activity may or may not have other activities above it in the stack. If it does, it is not in position to handle the intent, and the intent is dropped. (Even though the intent is dropped, its arrival would have caused the task to come to the foreground, where it would remain.)


I think this is the behaviour you want:

singleTask resets the stack on home press for some retarded reason that I don't understand. The solution is instead to not use singleTask and use standard or singleTop for launcher activity instead (I've only tried with singleTop to date though).

Because apps have an affinity for each other, launching an activity like this:

Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
if(launchIntent!=null) {
    launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}

will cause your activty stack to reappear as it was, without it starting a new activity upon the old one (which was my main problem before). The flags are the important ones:

FLAG_ACTIVITY_NEW_TASK Added in API level 1

If set, this activity will become the start of a new task on this history stack. A task (from the activity that started it to the next task activity) defines an atomic group of activities that the user can move to. Tasks can be moved to the foreground and background; all of the activities inside of a particular task always remain in the same order. See Tasks and Back Stack for more information about tasks.

This flag is generally used by activities that want to present a "launcher" style behavior: they give the user a list of separate things that can be done, which otherwise run completely independently of the activity launching them.

When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in. See FLAG_ACTIVITY_MULTIPLE_TASK for a flag to disable this behavior.

This flag can not be used when the caller is requesting a result from the activity being launched.

And:

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Added in API level 1

If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed.

Without them the launched activity will just be pushed ontop of the old stack or some other undesirable behaviour (in this case of course)

I believe the problem with not receiving the latest Intent can be solved like this (out of my head):

@Override
public void onActivityReenter (int resultCode, Intent data) {
    onNewIntent(data);
}

Try it out!


If both A and B belong to the same Application, try removing

android:launchMode="singleTask"

from your Activities and test because I think the default behavior is what you described as expected.


Whenever you press the home button to go back to your home screen the activity stack kills some of the previously launched and running apps.

To verify this fact try to launch an app from the notification panel after going from A to B in your app and come back using the back button ..........you will find your app in the same state as you left it.


When using launch mode as singleTop make sure to call finish() (on current activity say A) when starting the next activity (using startActivity(Intent) method say B). This way the current activity gets destroyed. A -> B -> Pause the app and click on launcher Icon, Starts A In oncreate method of A, you need to have a check,

if(!TaskRoot()) {
    finish();
     return;
  }

This way when launching app we are checking for root task and previously root task is B but not A. So this check destroys the activity A and takes us to activity B which is currently top of the stack. Hope it works for you!.


This is how I finally solved this weird behavior. In AndroidManifest, this is what I added:

Application & Root activity
            android:launchMode="singleTop"
            android:alwaysRetainTaskState="true"
            android:taskAffinity="<name of package>"

Child Activity
            android:parentActivityName=".<name of parent activity>"
            android:taskAffinity="<name of package>"

        <activity android:name=".MainActivity"
         android:launchMode="singleTop">
         <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
         </intent-filter>
       </activity>

//Try to use launchMode="singleTop" in your main activity to maintain single instance of your application. Go to manifest and change.


Add below in android manifest activity, it will add new task to top of the view destroying earlier tasks. android:launchMode="singleTop" as below

 <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:launchMode="singleTop"
        android:theme="@style/AppTheme.NoActionBar">
    </activity>

참고URL : https://stackoverflow.com/questions/2417468/android-bug-in-launchmode-singletask-activity-stack-not-preserved

반응형