วิธีจัดการรหัสเมื่อแอปถูกฆ่าโดยการปัดใน Android


106

หากแอปของฉันกำลังทำงานและฉันกดปุ่มโฮมแอปจะทำงานอยู่เบื้องหลัง ตอนนี้ถ้ากดปุ่มบ้านและฆ่า app โดยรูดจากรายการแอปที่ผ่านมาไม่มีเหตุการณ์ชอบonPause(), onStop()หรือonDestroy()ได้รับเรียกว่าค่อนข้างกระบวนการถูกยกเลิก ดังนั้นหากฉันต้องการให้บริการของฉันหยุดหยุดการแจ้งเตือนและยกเลิกการลงทะเบียนผู้ฟังฉันจะทำได้อย่างไร? ฉันอ่านบทความและบล็อกค่อนข้างน้อย แต่ไม่ได้รับข้อมูลที่เป็นประโยชน์และไม่พบเอกสารใด ๆ เกี่ยวกับเรื่องนี้ ความช่วยเหลือใด ๆ จะได้รับการชื่นชม ขอบคุณล่วงหน้า.


ฉันแก้ไขปัญหาของฉัน ตรวจสอบ
MysticMagicϡ

@MysticMagic ที่ใช้ได้กับบริการแล้วการยกเลิกการแจ้งเตือนและการยกเลิกการลงทะเบียนผู้ฟังล่ะ?
CodeWarrior

คุณสามารถยกเลิกการลงทะเบียนผู้ฟังใน onTaskRemoved ไม่ได้เหรอ? และเช่นเดียวกันสำหรับการยกเลิกการแจ้งเตือน
MysticMagicϡ

@ MysticMagicϡจะเกิดอะไรขึ้นถ้าฉันต้องการทำสิ่งเดียวกันในการอัปเดตแอป?
reji

คำตอบ:


152

ฉันเพิ่งแก้ไขปัญหาประเภทเดียวกันนี้

นี่คือสิ่งที่คุณสามารถทำได้หากเพียงแค่หยุดบริการเมื่อแอปพลิเคชันถูกฆ่าโดยการปัดจากรายการแอปล่าสุด

ภายในไฟล์ Manifest ของคุณให้ตั้งค่าสถานะstopWithTaskเป็นtrueบริการ ชอบ:

<service
    android:name="com.myapp.MyService"
    android:stopWithTask="true" />

แต่อย่างที่คุณบอกว่าคุณต้องการยกเลิกการลงทะเบียนผู้ฟังและหยุดการแจ้งเตือนเป็นต้นฉันขอแนะนำวิธีนี้:

  1. ภายในไฟล์ Manifest ของคุณให้ตั้งค่าสถานะstopWithTaskเป็นfalseบริการ ชอบ:

    <service
        android:name="com.myapp.MyService"
        android:stopWithTask="false" />
  2. ตอนนี้ในของคุณบริการวิธีการแทนที่MyService onTaskRemoved(สิ่งนี้จะเริ่มทำงานก็ต่อเมื่อstopWithTaskตั้งค่าเป็นfalse)

    public void onTaskRemoved(Intent rootIntent) {
    
        //unregister listeners
        //do any other cleanup if required
    
        //stop service
        stopSelf();  
    }

อ้างอิงคำถามของฉันสำหรับรายละเอียดเพิ่มเติมซึ่งมีส่วนอื่น ๆ ของโค้ดด้วย

หวังว่านี่จะช่วยได้


ขอบคุณ. คิดว่าวิธีนี้สามารถใช้เพื่อระบุการฆ่ารูดได้หรือไม่ บริการเปล่าธรรมดา?
Kiran

ใช่ @Kiran คุณลองดูก็ได้ :)
MysticMagicϡ

1
@ MysticMagicϡสวัสดีคำถามหนึ่งฉันลองใช้วิธีแก้ปัญหาของคุณ แต่ดูเหมือนว่าการดำเนินการที่ยาวนานเช่นการส่งข้อมูลไปยังเซิร์ฟเวอร์การวิเคราะห์ไปยัง Google ฯลฯ บางครั้งก็ไม่สามารถดำเนินการได้ตลอดเวลา เป็นไปได้ไหมว่ากระบวนการถูกฆ่าก่อนที่วิธีนี้จะมีโอกาสทำให้ตัวเองเสร็จสมบูรณ์? คุณได้ลองใช้วิธีนี้กับอุปกรณ์ Huawei แล้วหรือยัง? ดูเหมือนว่า onTaskRemoved ไม่เคยถูกเรียกบนอุปกรณ์เหล่านั้น ความคิดใด ๆ ทำไม?
Alon Minski

2
@ MysticMagicϡโมฆะสาธารณะ onTaskRemoved (.. ) วิธีที่ไม่เรียกใน Android O. ทำงานได้ดีในระบบปฏิบัติการอื่น แต่พบปัญหาใน Android O.
Jay Patel

2
รหัสนี้ใช้ไม่ได้กับ Android O เนื่องจากข้อ จำกัด ของบริการพื้นหลัง มีวิธีจัดการสิ่งนี้ในอุปกรณ์ Android ทั้งหมดหรือไม่?
Aanal Mehta

34

พบวิธีหนึ่งในการทำเช่นนี้

ให้บริการแบบนี้

public class OnClearFromRecentService extends Service {

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d("ClearFromRecentService", "Service Started");
    return START_NOT_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    Log.d("ClearFromRecentService", "Service Destroyed");
}

@Override
public void onTaskRemoved(Intent rootIntent) {
    Log.e("ClearFromRecentService", "END");
    //Code here
    stopSelf();
}

}

2) ลงทะเบียนบริการนี้ใน manifest.xml

<service android:name="com.example.OnClearFromRecentService" android:stopWithTask="false" />

3) จากนั้นเริ่มบริการนี้ในกิจกรรมสแปลชของคุณ

startService(new Intent(getBaseContext(), OnClearFromRecentService.class));

และตอนนี้เมื่อใดก็ตามที่คุณจะล้างแอปของคุณจาก Android ล่าสุดวิธีนี้ onTaskRemoved () จะดำเนินการ


1
คัดลอกและวางแบบ
ตัวต่อตัว

17

ฉันแก้ไขปัญหาที่คล้ายกัน หากคุณต้องการหลังจากปัดจากงานล่าสุดและในการเปิดใช้งานครั้งต่อไปเพื่อให้ทำงานได้อย่างถูกต้องให้ทำตามขั้นตอนด้านล่าง: -

1) บันทึกรหัสกระบวนการในการตั้งค่าที่ใช้ร่วมกัน:

SharedPreferencesUtils.getInstance().putInt(SharedPreferencesUtils.APP_PROCESS_ID, android.os.Process.myPid());

2) เมื่อเปิดแอปพลิเคชันจากตัวเรียกใช้งานหลังจากเคลียร์งานล่าสุดแล้วให้ทำ:

int previousProcessID = mSharedPreferencesUtils.getInt(SharedPreferencesUtils.APP_PROCESS_ID);

int currentProcessID = android.os.Process.myPid();

if ((previousProcessID == currentProcessID)) {
    // This ensures application not killed yet either by clearing recent or anyway
} else {
    // This ensures application killed either by clearing recent or by anyother means
}

ยืนยัน - ใช้บน Android 8 โดยไม่มีบริการ onTaskRemoved () - ทำได้ดีมาก
Gal Rom

4
โซลูชันนี้จะตรวจพบว่าแอปถูกรีสตาร์ท แต่คุณไม่สามารถตรวจจับช่วงเวลาที่แน่นอนเมื่อแอปถูกฆ่า
Vadim Kotov

6

เมื่อคุณกดที่บ้าน - onPauseและonStopกิจกรรมของคุณกำลังถูกเรียกดังนั้นในเวลานี้คุณต้องประหยัดและล้างข้อมูลทั้งหมดเนื่องจากแพลตฟอร์ม Android ไม่รับประกันอีกต่อไปว่าonDestroyจะมีการเรียกใช้วิธีวงจรชีวิตหรือวิธีอื่น ๆ ดังนั้นกระบวนการนี้อาจถูกฆ่าได้ ไม่มีการแจ้งเตือนใด ๆ


12
ขอบคุณสำหรับข้อเสนอแนะ แต่ฉันไม่ต้องการที่จะฆ่าการแจ้งเตือนบริการและผู้ฟังของฉันonPause()และonstop()พวกเขาจะต้องถูกฆ่าเมื่อผู้ใช้ออกจากแอปเท่านั้นเช่นออกจากระบบ
CodeWarrior

1

คุณต้องบันทึกข้อมูลของคุณเมื่อonPause()มีการเรียก ดูแผนภาพวงจรชีวิตนี้: นักพัฒนา Android

คุณจะเห็นว่าแอปสามารถฆ่าตายหลังจากหรือonPause()onStop()

จัดการข้อมูลของคุณที่นั่นและกู้คืนในonRestart()\ onCreate().

โชคดี!


1

คุณไม่สามารถจัดการกับการปัดได้เนื่องจากระบบเพียงแค่ลบกระบวนการของคุณออกจากหน่วยความจำโดยไม่ต้องโทรกลับใด ๆ

ฉันตรวจสอบแล้วว่าก่อนที่ผู้ใช้จะเรียกหน้าจอ "แอปล่าสุด" จะมีการเรียก onPause () เสมอ ดังนั้นคุณต้องบันทึกข้อมูลทั้งหมดในเมธอด onPause โดยไม่ตรวจสอบ isFinishing ()

ในการตรวจสอบปุ่มย้อนกลับให้ใช้เมธอด onBackPressed


1
ฉันเชื่อว่านี่เป็นวิธีเดียวที่ปลอดภัยสำหรับ Android ทุกเวอร์ชัน
Sergio

@Sergio ใช่มันเป็นวิธีที่ปลอดภัยที่สุดและฉันทดสอบกับแอพพลิเคชั่นขนาดใหญ่
Ahmad

1

ViewModel.onCleared () จะมีประโยชน์หากเป้าหมายคือการปล่อยทรัพยากรบางอย่าง (อาจเป็นระบบที่ทำงานอยู่ที่อื่นบนเครือข่าย) เมื่อผู้ใช้ดำเนินการออกจากการแปลกใจด้วยการปัดแทนที่จะกดปุ่ม "หยุด" หรือปุ่ม [นี่คือวิธีที่ฉันมาถึงคำถามนี้ในตอนแรก]

แอปพลิเคชันไม่ได้รับการแจ้งเตือนและ Activity.onDestroy () ถูกเรียกให้มีการเปลี่ยนแปลงการกำหนดค่าเช่นการเปลี่ยนแปลงการวางแนวคำตอบจึงไม่มี แต่ ViewModel.onCleared จะถูกเรียกเมื่อแอปพลิเคชันถูกปัดออกไป (เช่นเดียวกับเมื่อผู้ใช้กลับออกจากกิจกรรม) หากรีซอร์สที่คุณต้องการใช้เชื่อมโยงกับมากกว่าหนึ่งกิจกรรมในสแต็กคุณสามารถเพิ่มจำนวนอ้างอิงหรือกลไกอื่น ๆ เพื่อตัดสินใจว่า ViewModel.onClear ควรปล่อยรีซอร์สหรือไม่

นี่เป็นอีกหนึ่งเหตุผลที่ดีในการใช้ ViewModel

- บ๊อบ


1

ฉันไม่รู้จริงๆว่าทำไมวิธีการข้างต้นถึงใช้ไม่ได้กับกรณีของฉันแม้ว่าฉันจะตั้งค่าandroid:stopWithTask="false"ที่onTaskRemoved()ไม่ได้เรียกก็ตาม

AndroidViewModelวิธีการที่ดีอีกก็จะใช้ สิ่งนี้ใช้ได้กับกรณีที่ผู้ใช้ออกจากการกดปุ่มย้อนกลับ

เพียงผูกViewModelคลาสไว้กับคุณMainActivityจากนั้นทำการonCleared()โทรกลับงานของคุณ

ตัวอย่าง:

public class MainViewModel extends AndroidViewModel {
    public MainViewModel(@NonNull Application application) {
        super(application);
    }

    @Override
    protected void onCleared() {
        // Do your task here
        Log.e("MainViewModel", "OnCleared mainViewModel");
        super.onCleared();
    }
}

จากนั้นผูกไว้กับ MainActivity ของคุณ:

MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);

~ โวลา!


อาจใช้ไม่ได้กับวิธีการบริการเนื่องจาก Android O ขึ้นไปตามที่ผู้แสดงความคิดเห็นอื่น ๆ กล่าวไว้ ดูเหมือนจะเป็นทางออกที่น่าสนใจในการใช้โมเดลมุมมองสำหรับสิ่งนี้ สิ่งนี้ยังคงถูกทริกเกอร์หรือไม่หากแอปพลิเคชันถูกบังคับให้ฆ่า (ปัดบนหน้าจอตัวสลับแอป) แก้ไข: คำตอบของ Bob ชี้ให้เห็นว่ามันใช้ได้ผลในกรณีเหล่านี้
William Reed

ใช่มันใช้งานได้นั่นคือกรณีที่ฉันพบ
droidhs

0

สิ่งนี้ใช้ได้กับฉันบน Android 6,7,8,9

ให้บริการเดียวดังนี้:

 public class OnClearFromRecentService extends Service {

 @Override public IBinder onBind(Intent intent) {
     return null; }

 @Override public int onStartCommand(Intent intent, int flags, int
 startId) {
     Log.d("ClearFromRecentService", "Service Started");
     return START_NOT_STICKY; }

 @Override public void onDestroy() {
     super.onDestroy();
     Log.d("ClearFromRecentService", "Service Destroyed"); }

 @Override public void onTaskRemoved(Intent rootIntent) {
     Log.e("ClearFromRecentService", "END");
     //Code here
     stopSelf(); } }

2) ลงทะเบียนบริการนี้ในmanifest.xml:

<service android:name="com.example.OnClearFromRecentService"
 android:stopWithTask="false" />

3) จากนั้นเริ่มบริการนี้ในกิจกรรมสแปลชของคุณ

 startService(new Intent(getBaseContext(),
 OnClearFromRecentService.class));

1
แล้วทำไมบริการของคุณถึงยังคงทำงานบน Android 8 ขึ้นไป คุณทราบดีว่ามีข้อ จำกัด ในการโพสต์บริการเริ่มต้น Android 8 ใช่ไหม?
rahil008

0

ดังที่ Bob Cram กล่าวไว้ในคำตอบของเขาเมธอด onCleared () ของ View Modelคือคำตอบ ใช้ได้ทั้งสองกรณี:

  1. เมื่อผู้ใช้ลบแอพโดยปัดแอพออกจากพื้นหลัง
  2. เมื่อผู้ใช้ล้างแอปทั้งหมดโดยใช้ปุ่มล้างรายการ

onTaskRemoved () ของบริการจะทำงานเมื่อผู้ใช้ปัดแอปจากพื้นหลัง แต่จะไม่ทำงานเมื่อล้างแอปโดยใช้ปุ่มฆ่าทั้งหมด

แต่วิธี onCleared () ของ viewModel ใช้งานได้ทั้งสองกรณี คุณสามารถใช้ if เพื่อหยุดกระบวนการที่กำลังดำเนินอยู่หรือล้างงานใด ๆ ในเซิร์ฟเวอร์ระยะไกล

 override fun onCleared() {
        super.onCleared()
        Log.d(TAG , "App Killed")
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.