[英]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.