簡體   English   中英

"<i>Android: bug in launchMode=&quot;singleTask&quot;?<\/i> Android:launchMode =“singleTask”中的錯誤?<\/b> <i>-&gt; activity stack not preserved<\/i> -&gt; 活動堆棧未保留<\/b>"

[英]Android: bug in launchMode="singleTask"? -> activity stack not preserved

我的主要活動A<\/code>在清單中設置了android:launchMode="singleTask"<\/code> 。 現在,每當我從那里開始另一個活動時,例如B<\/code>並按下手機上的主屏幕HOME BUTTON<\/code>以返回主屏幕,然后再次返回我的應用程序,或者通過按下應用程序的按鈕或長按HOME BUTTON<\/code>以顯示我的最近的應用程序它不保留我的活動堆棧並直接返回A<\/code>而不是預期的活動B<\/code> 。

這里有兩種行為:

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

這不是一個錯誤。 當啟動現有的singleTask活動時,堆棧中位於它之上的所有其他活動都將被銷毀。

當您按HOME並再次啟動ActivityManger時, ActivityManger調用一個 Intent

{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作為啟動器和C的onCreate方法中的startActivity(A)

或者

只需刪除launchMode="singleTask"並設置launchMode="singleTask" FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP標志,每當調用意圖 A

來自http://developer.android.com/guide/topics/manifest/activity-element.html on singleTask

系統在新任務的根創建活動並將意圖路由到它。 但是,如果 Activity 的實例已經存在,系統會通過調用其 onNewIntent() 方法將意圖路由到現有實例,而不是創建一個新實例。

這意味着當 action.MAIN 和 category.LAUNCHER 標志從啟動器針對您的應用程序時,系統寧願將意圖路由到現有的 ActivityA,而不是創建新任務並將新的 ActivityA 設置為根。 它寧願拆除 ActivityA 所在的現有任務之上的所有活動,並調用它的 onNewIntent()。

如果您想同時捕獲 singleTop 和 singleTask 的行為,請使用 singleTask launchMode 創建一個名為 SingleTaskActivity 的單獨“委托”活動,該活動僅在其 onCreate() 中調用 singleTop 活動,然后自行完成。 singleTop 活動仍然具有 MAIN/LAUNCHER 意圖過濾器以繼續充當應用程序的主啟動器活動,但是當其他活動希望調用此 singleTop 活動時,它必須改為調用 SingleTaskActivity 以保留 singleTask 行為。 傳遞給 singleTask 活動的意圖也應該轉移到 singleTop Activity,所以像下面這樣的東西對我有用,因為我想同時擁有 singleTask 和 singleTop 啟動模式。

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

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"
        android:noHistory="true"/>

祝你好運。

Stefan,你有沒有找到這個問題的答案? 我為此編寫了一個測試用例,並且看到了相同的(令人困惑的)行為......我將粘貼下面的代碼,以防有人出現並看到明顯的東西:

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>

活動A.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 );
  }
}

活動B.java:

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

我嘗試更改 minSdkVersion 無濟於事。 這似乎是一個錯誤,至少根據文檔,它指出以下內容:

如上所述,“singleTask”或“singleInstance”活動的實例永遠不會超過一個,因此預計該實例將處理所有新意圖。 “singleInstance”活動始終位於堆棧頂部(因為它是任務中唯一的活動),因此它始終處於處理意圖的位置。 但是,“singleTask”活動在堆棧中可能有也可能沒有其他活動。 如果是,則它無法處理該意圖,並且該意圖被丟棄。 (即使意圖被丟棄,它的到來也會導致任務出現在前台,它會留在那里。)

我認為這是你想要的行為:

由於某種我不明白的遲鈍原因,singleTask 重置了主頁上的堆棧。 相反,解決方案是不使用 singleTask 並使用標准singleTop來代替啟動器活動(盡管到目前為止我只嘗試過使用 singleTop )。

因為應用程序彼此有親和力,所以啟動這樣的活動:

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

將導致您的活動堆棧按原樣重新出現,而不會在舊活動上啟動新活動(這是我之前的主要問題)。 標志是重要的

FLAG_ACTIVITY_NEW_TASK 在 API 級別 1 中添加

如果設置,此活動將成為此歷史堆棧上新任務的開始。 任務(從啟動它的活動到下一個任務活動)定義了用戶可以移動到的活動原子組。 任務可以移動到前台和后台; 特定任務中的所有活動始終保持相同的順序。 有關任務的更多信息,請參閱任務和返回堆棧。

此標志通常由想要呈現“啟動器”樣式行為的活動使用:它們為用戶提供可以完成的單獨事情的列表,否則這些活動完全獨立於啟動它們的活動運行。

使用此標志時,如果您正在啟動的活動的任務已經在運行,則不會啟動新的活動; 相反,當前任務將簡單地以其上次處於的狀態被帶到屏幕的前面。請參閱 FLAG_ACTIVITY_MULTIPLE_TASK 以獲取禁用此行為的標志。

當調用者從正在啟動的活動中請求結果時,不能使用此標志。

和:

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 在 API 級別 1 中添加

如果設置,並且此活動正在新任務中啟動或將現有任務帶到頂部,則它將作為任務的前門啟動。 這將導致應用使該任務處於正確狀態所需的任何關聯(將活動移入或移出),或者在需要時簡單地將該任務重置為其初始狀態。

沒有它們,啟動的活動將被推到舊堆棧或其他一些不良行為的頂部(當然在這種情況下)

我相信沒有收到最新的 Intent 的問題可以這樣解決(在我的腦海中):

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

試試看!

我發現只有當啟動器活動的啟動模式設置為 singleTask 或 singleInstance 時才會發生此問題。

因此,我創建了一個新的啟動器活動,其啟動模式為標准或單頂。 並使這個啟動器活動調用我的舊主活動,其啟動模式為單任務。

LauncherActivity (standard/no history) -> MainActivity (singleTask).

將啟動畫面設置為啟動器活動。 在我調用主要活動后立即殺死啟動器活動。

public LauncherActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Intent intent = new Intent(this, HomeActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
    startActivity(intent);
    finish();
  }
}
<activity
  android:name=".LauncherActivity"
  android:noHistory="true"
  android:theme="@style/Theme.LauncherScreen">

    <intent-filter>
      <action android:name="android.intent.action.MAIN"/>

      <category android:name="android.intent.category.DEFAULT"/>
      <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>

</activity>

<!-- Launcher screen theme should be set for the case that app is restarting after the process is killed. -->
<activity
  android:name=".MainActivity"
  android:launchMode="singleTask"
  android:theme="@style/Theme.LauncherScreen"/>

優點:可以保持 MainActivity 的啟動模式為 singleTask 以確保始終不超過一個 MainActivity。

如果 A 和 B 屬於同一個Application ,請嘗試刪除

android:launchMode="singleTask"

來自您的Activities和測試,因為我認為默認行為是您所描述的預期行為。

每當您按下主頁按鈕返回主屏幕時,活動堆棧都會殺死一些之前啟動和運行的應用程序。

要驗證這一事實,請嘗試在您的應用程序中從 A 轉到 B 后從通知面板啟動一個應用程序,然后使用后退按鈕返回 ..........您會發現您的應用程序處於與您相同的狀態留下它。

當使用啟動模式作為 singleTop 時,請確保在啟動下一個活動(使用 startActivity(Intent) 方法說 B)時調用完成()(在當前活動上說 A)。 這樣當前的活動就會被破壞。 A -> B -> 暫停應用程序並單擊啟動器圖標,啟動 A 在 A 的 oncreate 方法中,您需要進行檢查,

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

這樣,當啟動應用程序時,我們正在檢查根任務,而之前的根任務是 B 而不是 A。因此,此檢查會破壞活動 A 並將我們帶到當前位於堆棧頂部的活動 B。 希望這對你有用!。

這就是我最終解決這種奇怪行為的方法。 在 AndroidManifest 中,這是我添加的內容:

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

在 android manifest 活動中添加下面的內容,它將在視圖頂部添加新任務,破壞先前的任務。 android:launchMode="singleTop" 如下

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

在兒童活動或 B 活動中

<\/blockquote>

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

//嘗試在您的主要活動中使用launchMode="singleTop"來維護您的應用程序的單個實例。 去顯化和改變。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM