ล้างสแต็กประวัติทั้งหมดและเริ่มกิจกรรมใหม่บน Android


332

เป็นไปได้หรือไม่ที่จะเริ่มกิจกรรมบนสแต็กทำการล้างประวัติทั้งหมดก่อนหน้า

สถานการณ์

ฉันมีกิจกรรมสแต็กที่ไป A-> B-> C หรือ B-> C (หน้าจอ A เลือกโทเค็นผู้ใช้ แต่ผู้ใช้จำนวนมากมีโทเค็นเดียวเท่านั้น)

ในหน้าจอ C ผู้ใช้อาจดำเนินการซึ่งทำให้หน้าจอ B ไม่ถูกต้องดังนั้นแอปพลิเคชันต้องการนำพวกเขาไปที่หน้าจอ A ไม่ว่าจะอยู่ในสแต็กหรือไม่ก็ตาม หน้าจอ A ควรเป็นรายการเดียวในสแต็กในแอปพลิเคชันของฉัน

หมายเหตุ

มีคำถามที่คล้ายกันอื่น ๆ อีกมากมาย แต่ฉันไม่พบสิ่งใดที่ตอบคำถามตรงนี้ ฉันพยายามโทรgetParent().finish()- ซึ่งจะส่งผลให้เกิดข้อยกเว้นตัวชี้โมฆะเสมอ FLAG_ACTIVITY_CLEAR_TOPใช้งานได้เฉพาะถ้ากิจกรรมอยู่ในสแต็กแล้ว

คำตอบ:


658

ในระดับ API 11 มีการเพิ่มการตั้งค่าสถานะใหม่สำหรับสิ่งนี้: Intent.FLAG_ACTIVITY_CLEAR_TASK

เพียงชี้แจงให้ใช้สิ่งนี้:

ชวา

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


น่าเสียดายสำหรับ API lvl <= 10 ฉันยังไม่พบวิธีแก้ปัญหาที่ชัดเจนสำหรับเรื่องนี้ "DontHackAndroidLikeThis" การแก้ปัญหาย่อม hackery บริสุทธิ์ คุณไม่ควรทำอย่างนั้น :)

แก้ไข: ตามความเห็นของ @ Ben Pearsonสำหรับ API <= 10 ในขณะนี้เราสามารถใช้คลาสIntentCompatในแบบเดียวกันได้ หนึ่งสามารถใช้การIntentCompat.FLAG_ACTIVITY_CLEAR_TASKตั้งค่าสถานะเพื่อล้างงาน ดังนั้นคุณสามารถรองรับ pre API ระดับ 11 ได้เช่นกัน


23
เพียงชี้แจงให้ใช้สิ่งนี้: intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
user123321

2
โดยไม่ต้องมีเจตนา FLAG_ACTIVITY_NEW_TASK บางครั้งแอปจะปิดตัวเองลงใน Android 4
max4ever

22
IntentCompat มีการตั้งค่าสถานะเพื่อล้างภารกิจในขณะนี้ดังนั้นคุณจึงสามารถสนับสนุนระดับ API ล่วงหน้า 11 - developer.android.com/reference/android/support/v4/content/ …
Ben Pearson

10
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK จะถูกละเว้นบนอุปกรณ์ที่มีระดับ API < 10. developer.android.com/reference/android/support/v4/content/...
เดวิด

7
การตั้งค่าสถานะของ IntentCompat เพียงเพื่อหลีกเลี่ยงความผิดพลาด แต่ไม่ได้ทำอะไรตามที่ @David พูด
ลอยด์

49

กรณีที่ 1: เพียงสองกิจกรรม A และ B:

ที่นี่การไหลของกิจกรรมคือ A-> B เมื่อคลิกปุ่มย้อนกลับจาก B เราจำเป็นต้องปิดแอปพลิเคชันแล้วในขณะที่เริ่มต้นกิจกรรม B จาก A เพียงโทรเสร็จสิ้น () สิ่งนี้จะป้องกันไม่ให้ Android จัดเก็บกิจกรรม A ใน Backstack.eg สำหรับกิจกรรม A หน้าจอ Loding / Splash ของแอปพลิเคชัน

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

กรณีที่ 2: มากกว่าสองกิจกรรม:

หากมีกระแสเช่น A-> B-> C-> D-> B และเมื่อคลิกปุ่มย้อนกลับในกิจกรรม B ในขณะที่มาจากกิจกรรม D. ในกรณีนี้เราควรใช้

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

ที่นี่กิจกรรม B จะเริ่มต้นจากแบ็คสแต็กแทนที่จะเป็นอินสแตนซ์ใหม่เนื่องจากเป็นเจตนา FLAG_ACTIVITY_CLEAR_TOP และ Intent.FAG_ACTIVITY_NEW_TASK จะล้างสแต็กและทำให้เป็นแอปอันดับแรกดังนั้นเมื่อเรากดปุ่มย้อนกลับแอปพลิเคชันทั้งหมดจะถูกยกเลิก


2
สิ่งนี้ใช้ได้สำหรับฉัน ฉันใส่กิจกรรมทั้งหมดในธงเหล่านั้น ในกิจกรรมเหล่านั้นปุ่มย้อนกลับจะทำงานอย่างสมบูรณ์ไปยังกิจกรรมก่อนหน้าและในกิจกรรมหลักที่มีเจตนาเจตนา = เจตนาใหม่ (Intent.ACTION_MAIN); intent.addCategory (Intent.CATEGORY_HOME); intent.addFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); startActivity (เจตนา); เสร็จสิ้น(); แอพทั้งหมดปิดอยู่ยังคงอยู่ในหน่วยความจำ แต่ไม่เปิดใช้งานและหากคุณรีสตาร์ทแอปจะไปที่หน้าจอสาด :)
Rako

นี่ควรเป็นคำตอบที่ดีที่สุด หากใครมีสถานการณ์เหมือนกันกับฉัน: A-> B-> C-> D-> E -> (B) จาก E-> B ควรมีผล: A-> B
Shem Alexis Chavez

39

ด้วยเวอร์ชันที่ใหม่กว่าของ Android> = การใช้ API 16 finishAffinity()

วิธีนี้เหมาะสำหรับ> = API 16

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
  • มันเหมือนกับการเริ่มกิจกรรมใหม่และล้างสแต็กทั้งหมด
  • หรือรีสตาร์ทเป็น MainActivity / FirstActivity

1
นี่เป็นกลอุบายพวกธงใช้การ 4.xx สำหรับฉันและมันใช้ได้อย่างสมบูรณ์! ขอบคุณ
Jonathan Aste

1
นี่น่าจะเป็นคำตอบที่ถูกต้องหากเป้าหมายของคุณคือการทำกิจกรรมทั้งหมดด้านล่างและรวมถึงกิจกรรมปัจจุบันและเริ่มกิจกรรมใหม่ในงานของตนเอง
ToBe

24

ฉันใช้เวลาสองสามชั่วโมงนี้ด้วย ... และยอมรับว่า FLAG_ACTIVITY_CLEAR_TOP ดูเหมือนสิ่งที่คุณต้องการ: ล้างสแต็กทั้งหมดยกเว้นกิจกรรมที่กำลังเปิดตัวดังนั้นปุ่ม Back จะออกจากแอปพลิเคชัน แต่อย่างที่ Mike Repass พูดถึง FLAG_ACTIVITY_CLEAR_TOP จะทำงานได้ก็ต่อเมื่อกิจกรรมที่คุณเปิดตัวมีอยู่แล้วในสแต็ก เมื่อกิจกรรมไม่อยู่ที่นั่นธงจะไม่ทำอะไรเลย

จะทำอย่างไร? วางกิจกรรมที่กำลังเปิดตัวในสแต็กด้วย FLAG_ACTIVITY_NEW_TASK ซึ่งทำให้กิจกรรมนั้นเป็นจุดเริ่มต้นของภารกิจใหม่บนสแต็กประวัติ จากนั้นเพิ่มการตั้งค่าสถานะ FLAG_ACTIVITY_CLEAR_TOP

ตอนนี้เมื่อ FLAG_ACTIVITY_CLEAR_TOP ไปหากิจกรรมใหม่ในสแต็คมันจะอยู่ที่นั่นและถูกดึงขึ้นก่อนที่ทุกอย่างจะถูกล้างออก

นี่คือฟังก์ชันออกจากระบบของฉัน พารามิเตอร์ View เป็นปุ่มที่แนบกับฟังก์ชั่น

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
คุณหมายถึง CLEAR_TASK แทนที่จะเป็น CLEAR_TOP หรือไม่
แอนดี้

14

คุณไม่ควรเปลี่ยนสแต็ค ปุ่มย้อนกลับของ Android ควรทำงานเหมือนในเว็บเบราว์เซอร์

ฉันสามารถคิดวิธีที่จะทำ แต่มันค่อนข้างแฮ็ค

  • สร้างกิจกรรมของคุณsingleTaskโดยเพิ่มลงในAndroidManifest ตัวอย่าง:

    <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"/>
  • ขยายApplicationซึ่งจะถือตรรกะของสถานที่ที่จะไป

ตัวอย่าง:

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);
  }
}

จาก A ถึง B:

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

จาก B ถึง C:

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

ใน C:

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

และจัดการปุ่มย้อนกลับpop()จากสแต็ค

อีกครั้งคุณไม่ควรทำเช่นนี้ :)


ในที่สุดฉันตัดสินใจออกจากกองเหมือนเดิมและบอกผู้ใช้ว่าหน้าจอปัจจุบันไม่ถูกต้อง
Casebash

1
น่าผิดหวังมากที่ Android ไม่อนุญาตให้เราจัดการกิจกรรมด้วยวิธีนี้แล้ว ฉันจะถูกล่อลวงให้ใช้วิธีแก้ปัญหานี้ในแอพ android ของฉันในอนาคต
Cephron

4
เพื่อให้ชัดเจนว่าทำไมไม่ควรใช้สิ่งนี้: เป็นวิธีที่ดีในการสร้างหน่วยความจำรั่ว ณ จุดหนึ่งระบบปฏิบัติการอาจตัดสินใจฆ่ากิจกรรมพื้นหลัง แต่เนื่องจากApplicationใช้อินสแตนซ์ของตนระบบปฏิบัติการจะไม่สามารถเพิ่ม RAM นั้นจากกิจกรรมที่ถูกทำลาย
Vit Khudenko

@Arhimed มีปัญหาอื่นอีกไหม? หน่วยความจำรั่วสามารถแก้ไขได้โดยเก็บเฉพาะการอ้างอิงที่อ่อนแอเท่านั้น
Navin

1
@Navin ใช่การรั่วไหลสามารถหลีกเลี่ยงได้ด้วยการอ้างอิงที่อ่อนแอ แต่ถ้าหลังจาก GC จะไม่มีการอ้างอิงกิจกรรมแบบสดวิธีการทั้งหมดจะไร้ประโยชน์ อีกครั้ง - อย่าทำสิ่งนี้เป็นวิธีที่ผิดสำหรับ Android
Vit Khudenko

12

ทันทีหลังจากที่คุณเริ่มกิจกรรมใหม่ให้ใช้startActivityตรวจสอบให้แน่ใจว่าคุณได้โทรแล้วfinish()เพื่อไม่ให้กิจกรรมปัจจุบันซ้อนกันหลังกิจกรรมใหม่


+1 วิธีแก้ปัญหาที่ดีเพื่อป้องกันกิจกรรมหนึ่งกิจกรรมในบางสถานการณ์ไม่ให้วางลงบนสแต็กประวัติ
marsbear

27
ไม่ทำงานหากคุณมีมากกว่าหนึ่งกิจกรรมในกองซ้อนเสร็จสิ้นจะล้างกิจกรรมก่อนหน้า แต่ไม่ใช่กิจกรรมอื่น ๆ ...
Necronet

5

ลองสิ่งนี้:

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 ขั้นสูง Reuseable:

คุณสามารถตั้งค่าสถานะโดยตรงโดยใช้วิธีการตั้งค่า ใน Kotlin orเป็นทดแทนสำหรับบิต Java |หรือ

intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK

หากคุณวางแผนที่จะใช้สิ่งนี้เป็นประจำให้สร้างฟังก์ชันส่วนขยาย Intent

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

จากนั้นคุณสามารถเรียกใช้ฟังก์ชันนี้โดยตรงก่อนที่จะเริ่มเจตนา

intent.clearStack()

หากคุณต้องการตัวเลือกในการเพิ่มการตั้งค่าสถานะเพิ่มเติมในสถานการณ์อื่น ๆ ให้เพิ่มพารามิเตอร์เสริมลงในฟังก์ชันส่วนขยาย

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


2

ลองรหัสด้านล่าง

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

ถ้าฉันกำลังใช้กิจกรรมนี้อัปเดต api อีกครั้ง แต่ที่มีอยู่ก่อนหน้านี้ statck ทั้งหมดจะถูกล้าง
Harsha

2

สำหรับฉันไม่มีวิธีการข้างต้นไม่ทำงาน

เพียงทำเช่นนี้เพื่อล้างกิจกรรมก่อนหน้านี้ทั้งหมด :

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

-1

บางครั้ง android emulator ของคุณอาจล้มเหลวในการเชื่อมต่อเครื่องมือ eclipse DDMS และขอให้ adb เริ่มด้วยตนเอง ในกรณีดังกล่าวคุณสามารถเริ่มหรือหยุด adb โดยใช้พรอมต์คำสั่ง


1
บางครั้ง android emulator ของคุณอาจล้มเหลวในการเชื่อมต่อเครื่องมือ eclipse DDMS และขอให้ adb เริ่มด้วยตนเอง ในกรณีดังกล่าวคุณสามารถเริ่มหรือหยุด adb โดยใช้พรอมต์คำสั่ง เจตนา i = เจตนาใหม่ (OldActivity.this, NewActivity.class); // ตั้งค่าภารกิจใหม่และล้างค่าสถานะ i.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) startActivity (i);
RajeshkumarG

-2

ฉันพบว่าแฮ็คง่ายเกินไปเพียงแค่เพิ่มองค์ประกอบใหม่ใน AndroidManifestรูปแบบ: -

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

android:noHistoryจะล้างกิจกรรมที่ไม่พึงประสงค์ของคุณจากกอง


2
คำสั่งนี้อาจทำให้เกิดปัญหากับ Android 6.0 ขึ้นไปถ้าคุณขอใบอนุญาตในกิจกรรมนี้
Vitaliy A
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.