Xóa toàn bộ ngăn xếp lịch sử và bắt đầu một hoạt động mới trên Android


332

Có thể bắt đầu một hoạt động trên ngăn xếp, xóa toàn bộ lịch sử trước đó không?

Tình huống

Tôi có một ngăn xếp hoạt động đi A-> B-> C hoặc B-> C (màn hình A chọn mã thông báo người dùng, nhưng nhiều người dùng chỉ có một mã thông báo).

Trong màn hình C, người dùng có thể thực hiện một hành động khiến màn hình B không hợp lệ, vì vậy ứng dụng muốn đưa họ đến màn hình A, bất kể nó đã có trong ngăn xếp hay chưa. Màn hình A sau đó sẽ là mục duy nhất trên ngăn xếp trong ứng dụng của tôi.

Ghi chú

Có nhiều câu hỏi tương tự khác, nhưng tôi không tìm thấy câu trả lời chính xác cho câu hỏi này. Tôi đã thử gọi getParent().finish()- điều này luôn dẫn đến một ngoại lệ con trỏ null. FLAG_ACTIVITY_CLEAR_TOPchỉ hoạt động nếu hoạt động đã có trên ngăn xếp.

Câu trả lời:


658

Trong API cấp 11, một Cờ ý định mới đã được thêm vào chỉ này: Intent.FLAG_ACTIVITY_CLEAR_TASK

Chỉ cần làm rõ, sử dụng này:

Java

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);


Kotlin

intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK


Thật không may cho API lv <= 10, tôi chưa tìm thấy giải pháp sạch cho vấn đề này. Các "DontHackAndroidLikeThis" giải pháp thực sự là hackery tinh khiết. Bạn không nên làm điều đó. :)

Chỉnh sửa: Theo nhận xét của @ Ben Pearson , đối với API <= 10 bây giờ, người ta có thể sử dụng lớp IntentCompat cho cùng. Người ta có thể sử dụng IntentCompat.FLAG_ACTIVITY_CLEAR_TASKcờ để xóa nhiệm vụ. Vì vậy, bạn cũng có thể hỗ trợ API cấp 11.


23
Chỉ cần làm rõ, sử dụng cái này: aim.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
dùng123321

2
không có Intent.FLAG_ACTIVITY_NEW_TASK đôi khi ứng dụng chỉ tự đóng trên Android 4
max4ever

22
Hiện tại IntentCompat cũng có cờ để xóa nhiệm vụ, vì vậy bạn có thể hỗ trợ API cấp 11 trước - developer.android.com/reference/android/support/v4/content/
Ben Pearson

10
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK bị bỏ qua trên các thiết bị có cấp API <10. Developer.android.com/reference/android/support/v4/content/,
David

7
Cờ của IntentCompat chỉ để tránh sự cố, nhưng không làm gì như @David nói.
Triển khai

49

Trường hợp 1: Chỉ có hai hoạt động A và B:

Ở đây Luồng hoạt động là A-> B. Khi nhấp vào nút bấm từ B, chúng ta cần đóng ứng dụng sau đó trong khi bắt đầu Hoạt động B từ A chỉ cần kết thúc cuộc gọi () điều này sẽ ngăn Android lưu trữ Hoạt động A vào Backstack.eg cho hoạt động A là Màn hình Loding / Splash của ứng dụng.

Intent newIntent = new Intent(A.this, B.class);
startActivity(newIntent);
finish();

Trường hợp 2: Nhiều hơn hai hoạt động:

Nếu có một luồng như A-> B-> C-> D-> B và khi nhấp vào nút quay lại trong Hoạt động B trong khi đến từ Hoạt động D. Trong trường hợp đó chúng ta nên sử dụng.

Intent newIntent = new Intent(D.this,B.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent);

Tại đây, Hoạt động B sẽ được bắt đầu từ backstack chứ không phải là một trường hợp mới vì Intent.FLAG_ACTIVITY_CLEAR_TOP và Intent.FLAG_ACTIVITY_NEW_TASK xóa ngăn xếp và biến nó thành ứng dụng hàng đầu. Vì vậy, khi chúng tôi nhấn nút quay lại, toàn bộ ứng dụng sẽ bị chấm dứt.


2
Điều này làm việc cho tôi. Tôi đưa vào TẤT CẢ các hoạt động những lá cờ. Trong các hoạt động đó, các nút quay lại hoạt động hoàn hảo đi đến hoạt động trước đó và trong Hoạt động chính với ý định Intent = new Intent (Intent.ACTION_MAIN); aim.addC Category (Intent.CATEGORY_HOME); aim.addFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP); aim.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); startActivity (ý định); hoàn thành(); Toàn bộ ứng dụng đã đóng, vẫn còn trong bộ nhớ nhưng không hoạt động và nếu bạn khởi động lại, ứng dụng sẽ chuyển sang màn hình giật gân :)
Rako

Đây phải là câu trả lời tốt nhất. Nếu ai có kịch bản giống tôi: A-> B-> C-> D-> E -> (B) Từ E-> B nên có kết quả: A-> B
Shem Alexis Chavez

39

Với phiên bản mới hơn của Android> = API 16 sử dụng finishAffinity()

Cách tiếp cận phù hợp với> = API 16.

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
  • Nó giống như bắt đầu Activity mới và xóa tất cả stack.
  • HOẶC Khởi động lại vào MainActivity / FirstActivity.

1
Điều này đã tạo ra mánh khóe, những lá cờ không hoạt động trên 4.xx cho tôi và điều này hoạt động hoàn hảo! Cảm ơn
Jonathan Aste

1
Đây dường như là câu trả lời chính xác nếu mục tiêu của bạn là hoàn thành tất cả các hoạt động dưới đây và bao gồm cả hoạt động hiện tại và bắt đầu một hoạt động mới trong nhiệm vụ riêng của họ.
ToBe

24

Tôi cũng đã dành vài giờ cho việc này ... và đồng ý rằng FLAG_ACTIVITY_CLEAR_TOP nghe giống như những gì bạn muốn: xóa toàn bộ ngăn xếp, ngoại trừ hoạt động được khởi chạy, vì vậy nút Quay lại thoát khỏi ứng dụng. Tuy nhiên, như Mike Repass đã đề cập, FLAG_ACTIVITY_CLEAR_TOP chỉ hoạt động khi hoạt động bạn khởi chạy đã có trong ngăn xếp; khi hoạt động không có ở đó, cờ không làm gì cả.

Phải làm sao Đặt hoạt động đang khởi chạy trong ngăn xếp với FLAG_ACTIVITY_NEW_TASK, điều này làm cho hoạt động đó bắt đầu một tác vụ mới trên ngăn xếp lịch sử. Sau đó thêm cờ FLAG_ACTIVITY_CLEAR_TOP.

Bây giờ, khi FLAG_ACTIVITY_CLEAR_TOP đi tìm hoạt động mới trong ngăn xếp, nó sẽ ở đó và được kéo lên trước khi mọi thứ khác bị xóa.

Đây là chức năng đăng xuất của tôi; tham số View là nút mà chức năng được đính kèm.

public void onLogoutClick(final View view) {
    Intent i = new Intent(this, Splash.class);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    startActivity(i);
    finish();
}

1
bạn có nghĩa là CLEAR_TASK thay vì CLEAR_TOP?
Andy

14

Bạn không nên thay đổi ngăn xếp. Nút quay lại của Android sẽ hoạt động như trong trình duyệt web.

Tôi có thể nghĩ ra một cách để làm điều đó, nhưng nó khá là hack.

  • Thực hiện các hoạt động của bạn singleTaskbằng cách thêm nó vào AndroidManifest ví dụ:

    <activity android:name=".activities.A"
              android:label="@string/A_title"
              android:launchMode="singleTask"/>
    
    <activity android:name=".activities.B"
              android:label="@string/B_title"
              android:launchMode="singleTask"/>
  • Mở rộng Applicationmà sẽ giữ logic của nơi để đi.

Thí dụ:

public class DontHackAndroidLikeThis extends Application {

  private Stack<Activity> classes = new Stack<Activity>();

  public Activity getBackActivity() {
    return classes.pop();
  }

  public void addBackActivity(Activity activity) {
    classes.push(activity);
  }
}

Từ A đến B:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(A.class); 
startActivity(this, B.class);

Từ B đến C:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(B.class); 
startActivity(this, C.class);

Trong C:

If ( shouldNotGoBackToB() ) {
  DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
  app.pop();
}

và xử lý nút quay lại pop()từ ngăn xếp.

Một lần nữa, bạn không nên làm điều này :)


Cuối cùng, tôi quyết định giữ nguyên Stack và chỉ cho người dùng biết rằng màn hình hiện tại của họ không hợp lệ
Casebash

1
Rất bực bội vì Android không cho phép chúng tôi quản lý ngăn xếp hoạt động theo cách này. Tôi sẽ bị cám dỗ để sử dụng giải pháp này trong các ứng dụng Android trong tương lai của tôi.
Cephron

4
Chỉ cần rõ ràng tại sao điều này không nên được sử dụng: đó là một cách hay để tạo rò rỉ bộ nhớ. Tại một số điểm, HĐH có thể quyết định Applicationhủy các hoạt động nền, nhưng kể từ khi có các phiên bản của chúng, HĐH sẽ không thể giải phóng RAM còn lại khỏi các hoạt động bị phá hủy.
Vit Khudenko

@Arhimed Có vấn đề nào khác không? Rò rỉ bộ nhớ có thể được vá bằng cách chỉ giữ các tham chiếu yếu.
Navin

1
@Navin có, rò rỉ có thể tránh được với các ref yếu, nhưng nếu sau GC sẽ không có Ref hoạt động trực tiếp thì toàn bộ cách tiếp cận là vô ích. Một lần nữa - đừng làm điều này, đây là một cách tiếp cận sai cho Android.
Vit Khudenko

12

Ngay sau khi bạn bắt đầu một hoạt động mới, sử dụng startActivity, hãy đảm bảo bạn gọi finish()để hoạt động hiện tại không bị xếp sau hoạt động mới.


+1 Giải pháp hay để ngăn chặn chính xác một hoạt động trong một tình huống nhất định từ việc đưa ong vào ngăn xếp lịch sử.
marsbear

27
không hoạt động nếu bạn có nhiều hơn một hoạt động trong ngăn xếp, kết thúc sẽ chỉ xóa hoạt động trước đó nhưng không hoạt động khác ....
Necronet

5

Thử cái này:

Intent logout_intent = new Intent(DashboardActivity.this, LoginActivity.class);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(logout_intent);
finish();

4

Kotlin nâng cao có thể tái sử dụng:

Bạn có thể đặt cờ trực tiếp bằng phương thức setter. Trong Kotlin orlà sự thay thế cho Java bitwise hoặc |.

intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK

Nếu bạn có kế hoạch sử dụng điều này thường xuyên, hãy tạo chức năng mở rộng Ý định

fun Intent.clearStack() {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

Sau đó, bạn có thể gọi trực tiếp chức năng này trước khi bắt đầu ý định

intent.clearStack()

Nếu bạn cần tùy chọn để thêm các cờ bổ sung trong các tình huống khác, hãy thêm một tham số tùy chọn vào chức năng mở rộng.

fun Intent.clearStack(additionalFlags: Int = 0) {
    flags = additionalFlags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

2
Intent i = new Intent(MainPoliticalLogin.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);

2

Hãy thử mã dưới đây,

Intent intent = new Intent(ManageProfileActivity.this, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|
                Intent.FLAG_ACTIVITY_CLEAR_TASK| 
                Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

Nếu tôi đang sử dụng như hoạt động này thì một lần nữa hãy gọi api nhưng trước đó tất cả statck đã bị xóa
Harsha

2

Đối với tôi không có phương pháp nào ở trên không hoạt động.

Chỉ cần làm điều này để xóa tất cả các hoạt động trước đó :

finishAffinity() // if you are in fragment use activity.finishAffinity()
Intent intent = new Intent(this, DestActivity.class); // with all flags you want
startActivity(intent)

-1

Đôi khi trình giả lập Android của bạn có thể không kết nối được công cụ DDMS nhật thực và yêu cầu adb bắt đầu thủ công. Trong trường hợp đó, bạn có thể bắt đầu hoặc dừng adb bằng dấu nhắc lệnh.


1
Đôi khi trình giả lập Android của bạn có thể không kết nối được công cụ DDMS nhật thực và yêu cầu adb bắt đầu thủ công. Trong trường hợp đó, bạn có thể bắt đầu hoặc dừng adb bằng dấu nhắc lệnh. Ý định i = Ý định mới (OldActivity.this, NewActivity. Class); // đặt tác vụ mới và xóa cờ i.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) startActivity (i);
RajeshkumarG

-2

Tôi thấy hack quá đơn giản chỉ cần làm điều này thêm yếu tố mới vào AndroidManifest: -

<activity android:name=".activityName"
          android:label="@string/app_name"
          android:noHistory="true"/>

những android:noHistorysẽ xóa hoạt động không mong muốn của bạn từ Stack.


2
Sự bỏ qua này có thể gây ra sự cố trên Android 6.0+, nếu bạn yêu cầu hoán vị trong Hoạt động này.
Vitaliy A
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.