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


382

ฉันกำลังพยายามเขียนแอพที่ทำสิ่งที่เฉพาะเจาะจงเมื่อมันถูกนำกลับไปที่ส่วนหน้าหลังจากเวลาผ่านไประยะหนึ่ง มีวิธีการตรวจสอบหรือไม่เมื่อแอพถูกส่งไปที่พื้นหลังหรือถูกนำไปที่ฉากหน้า?


2
อาจเป็นการเพิ่มกรณีการใช้งานให้กับคำถามเพราะดูเหมือนจะไม่ชัดเจนดังนั้นจึงไม่ได้ระบุไว้ในคำตอบที่ให้ แอพอาจเริ่มต้นแอพอื่น (เช่นคลังภาพ) ซึ่งจะยังคงอยู่ในสแต็กเดียวกันและปรากฏเป็นหน้าจอแอปหนึ่งจากนั้นกดปุ่มโฮม ไม่มีวิธีใดที่ใช้วงจรชีวิตของแอพ (หรือแม้แต่การจัดการหน่วยความจำ) ที่สามารถตรวจจับสิ่งนี้ได้ พวกเขาจะทริกเกอร์สถานะพื้นหลังทันทีเมื่อกิจกรรมภายนอกปรากฏขึ้นไม่ใช่เมื่อคุณกดหน้าแรก
Dennis K

นี่คือคำตอบที่คุณกำลังมองหา: stackoverflow.com/a/42679191/2352699
Fred Porciúncula

คำตอบ:


98

onPause()และonResume()วิธีการที่เรียกว่าเมื่อโปรแกรมประยุกต์ที่จะมาถึงพื้นหลังและเข้าไปในเบื้องหน้าอีกครั้ง อย่างไรก็ตามจะมีการเรียกเมื่อแอปพลิเคชั่นเริ่มต้นเป็นครั้งแรกและก่อนที่จะถูกฆ่า คุณสามารถอ่านเพิ่มเติมในกิจกรรม

ไม่มีวิธีการใด ๆ โดยตรงจะได้รับสถานะใบสมัครในขณะที่ในพื้นหลังหรือเบื้องหน้า แต่แม้ฉันต้องเผชิญกับปัญหานี้และพบว่าวิธีการแก้ปัญหาด้วยและonWindowFocusChangedonStop

สำหรับรายละเอียดเพิ่มเติมตรวจสอบที่นี่Android: โซลูชั่นการตรวจสอบเมื่อ app Android ที่ไปที่พื้นหลังและกลับมาเบื้องหน้าโดยไม่ต้อง getRunningTasks หรือ getRunningAppProcesses


173
อย่างไรก็ตามวิธีการนี้ทำให้เกิดผลบวกผิด ๆ ตามที่คนอื่น ๆ ชี้ให้เห็นเพราะวิธีการเหล่านี้จะถูกเรียกเมื่อเปลี่ยนระหว่างกิจกรรมในแอพเดียวกัน
John Lehmann

9
มันแย่กว่านั้น ฉันลองและบางครั้งเรียกว่า onResume ขณะที่โทรศัพท์ถูกล็อค หากคุณเห็นคำจำกัดความของ onResume ในเอกสารคุณจะพบ: โปรดทราบว่า onResume ไม่ใช่ตัวบ่งชี้ที่ดีที่สุดที่กิจกรรมของคุณจะปรากฏต่อผู้ใช้ หน้าต่างระบบเช่นปุ่มกดอาจอยู่ด้านหน้า ใช้ onWindowFocusChanged (บูลีน) เพื่อให้ทราบว่ากิจกรรมของคุณจะปรากฏแก่ผู้ใช้ (ตัวอย่างเช่นเพื่อเล่นเกมต่อ) developer.android.com/reference/android/app/…
J-Rou

2
โซลูชันที่โพสต์ในลิงค์ไม่ได้ใช้ onResume / onPause แทนการรวมกันของ onBackPressed, onStop, onStart และ onWindowsFocusChanged มันใช้งานได้สำหรับฉันและฉันมีลำดับชั้น UI ที่ค่อนข้างซับซ้อน (มีลิ้นชักเครื่องมือดูภาพแบบไดนามิก ฯลฯ )
Martin Marconcini

18
onPause และ onResume เป็นกิจกรรมเฉพาะ ไม่ใช่แอปพลิเคชัน เมื่อวางแอปบนพื้นหลังและดำเนินการต่อแล้วแอปจะดำเนินการต่อเฉพาะกิจกรรมที่เคยทำก่อนที่จะเข้าสู่พื้นหลัง ซึ่งหมายความว่าคุณจะต้องใช้สิ่งที่คุณต้องการในการดำเนินการต่อจากพื้นหลังในกิจกรรมทั้งหมดของแอปพลิเคชันของคุณ ฉันเชื่อว่าคำถามเดิมกำลังมองหาบางสิ่งบางอย่างเช่น "onResume" สำหรับแอปพลิเคชันไม่ใช่กิจกรรม
SysHex

4
ฉันไม่อยากเชื่อว่า API ที่เหมาะสมจะไม่ถูกนำเสนอสำหรับความต้องการทั่วไป ตอนแรกฉันคิดว่า onUserLeaveHint () จะตัดมัน แต่คุณไม่สามารถบอกได้ว่าผู้ใช้ออกจากแอปพลิเคชันหรือไม่
atsakiridis

197

2018: Android รองรับสิ่งนี้ผ่านส่วนประกอบวงจรชีวิต

มีนาคม 2561 อัพเดท : ขณะนี้มีทางออกที่ดีกว่า ดูProcessLifecycleOwner คุณจะต้องใช้ส่วนประกอบสถาปัตยกรรมใหม่ 1.1.0 (ล่าสุดในขณะนี้) แต่เป็นเฉพาะรับการออกแบบมาเพื่อทำสิ่งนี้

มีตัวอย่างง่ายๆที่ให้ไว้ในคำตอบนี้แต่ฉันเขียนตัวอย่างแอพและโพสต์บล็อกเกี่ยวกับมัน

นับตั้งแต่ฉันเขียนบทความนี้ย้อนกลับไปในปี 2014 การแก้ปัญหาที่แตกต่างกันก็เกิดขึ้น บางคนทำงานบางคนคิดว่าจะทำงานแต่มีข้อบกพร่อง (รวมถึงของฉัน!) และเราในฐานะชุมชน (Android) เรียนรู้ที่จะอยู่กับผลที่ตามมาและเขียนวิธีแก้ปัญหาสำหรับกรณีพิเศษ

อย่าสันนิษฐานว่ามีตัวอย่างข้อมูลเพียงรหัสเดียวคือโซลูชันที่คุณกำลังมองหา ดีกว่ายังพยายามที่จะเข้าใจว่ามันทำอะไรและทำไมมันทำ

MemoryBossชั้นก็ไม่เคยนำมาใช้จริงโดยผมเขียนนี่มันเป็นเพียงชิ้นส่วนของรหัสเทียมที่เกิดขึ้นกับการทำงาน

เว้นแต่จะมีเหตุผลที่ถูกต้องสำหรับคุณที่จะไม่ใช้ส่วนประกอบสถาปัตยกรรมใหม่ (และมีบางอย่างโดยเฉพาะถ้าคุณกำหนดเป้าหมาย apis เก่าสุด) จากนั้นไปข้างหน้าและใช้พวกเขา พวกเขาห่างไกลจากความสมบูรณ์แบบ ComponentCallbacks2แต่ก็ยังไม่ได้

อัพเดท / หมายเหตุ (พฤศจิกายน 2558) : ผู้คนแสดงความคิดเห็นสองข้อก่อนอื่นคือ>=ควรใช้แทน==เพราะรัฐเอกสารที่คุณไม่ควรตรวจสอบค่าที่แน่นอน นี้เป็นที่ดีสำหรับกรณีส่วนใหญ่ แต่จำไว้ว่าถ้าคุณเพียงดูแลเกี่ยวกับการทำบางสิ่งบางอย่างเมื่อ app ไปพื้นหลังคุณจะต้องใช้ == และยังรวมกับวิธีอื่น (เช่นการเรียกกลับกิจกรรม Lifecycle) หรือคุณอาจไม่ได้รับผลที่คุณต้องการ ตัวอย่าง (และสิ่งนี้เกิดขึ้นกับฉัน) คือถ้าคุณต้องการล็อคแอปของคุณด้วยหน้าจอรหัสผ่านเมื่อไปที่พื้นหลัง (เช่น 1Password หากคุณคุ้นเคย) คุณอาจล็อคแอปของคุณโดยไม่ตั้งใจถ้าคุณใช้หน่วยความจำเหลือน้อยและมีการทดสอบโดยฉับพลัน>= TRIM_MEMORYเพราะ Android จะเปิดการLOW MEMORYโทร สูงกว่าของคุณ ดังนั้นควรระวังวิธี / สิ่งที่คุณทดสอบ

นอกจากนี้บางคนถามถึงวิธีการตรวจจับเมื่อคุณกลับมา

วิธีที่ง่ายที่สุดที่ฉันคิดได้อธิบายไว้ด้านล่าง แต่เนื่องจากบางคนไม่คุ้นเคยกับมันฉันจึงเพิ่มโค้ดหลอกบางส่วนที่นี่ สมมติว่าคุณมีYourApplicationและMemoryBossคลาสในของคุณclass BaseActivity extends Activity(คุณจะต้องสร้างถ้าคุณไม่มี)

@Override
protected void onStart() {
    super.onStart();

    if (mApplication.wasInBackground()) {
        // HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
        mApplication.setWasInBackground(false);
    }
}

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

และนั่นคือทั้งหมดที่ รหัสในบล็อก if จะถูกเรียกใช้งานเพียงครั้งเดียวแม้ว่าคุณจะไปที่กิจกรรมอื่น ๆ รายงานใหม่ (ที่ยังextends BaseActivity) จะรายงานwasInBackgroundคือfalseมันจะไม่รันรหัสจนกว่าonMemoryTrimmedจะถูกเรียกและตั้งค่าสถานะเป็นจริงอีกครั้ง .

หวังว่าจะช่วย

อัปเดต / หมายเหตุ (เมษายน 2558) : ก่อนที่คุณจะไปคัดลอกและวางบนรหัสนี้โปรดทราบว่าฉันได้พบสองสามกรณีที่อาจไม่น่าเชื่อถือ 100% และต้องใช้ร่วมกับวิธีอื่นเพื่อให้ได้ผลลัพธ์ที่ดีที่สุด มีสองกรณีที่เป็นที่รู้จักonTrimMemoryเรียกกลับไม่ได้รับการรับประกันว่าจะดำเนินการ:

  1. หากโทรศัพท์ของคุณล็อคหน้าจอในขณะที่แอปของคุณมองเห็นได้ (พูดว่าอุปกรณ์ของคุณล็อคหลังจาก nn นาที) การโทรกลับนี้จะไม่ถูกเรียก (หรือไม่เสมอไป) เพราะ lockscreen อยู่ด้านบน แต่แอปของคุณยังคง

  2. หากอุปกรณ์ของคุณมีหน่วยความจำค่อนข้างต่ำ (และอยู่ภายใต้ความกดดันหน่วยความจำ) ระบบปฏิบัติการดูเหมือนจะไม่สนใจการโทรนี้และตรงไปยังระดับที่สำคัญยิ่งกว่า

ตอนนี้ขึ้นอยู่กับความสำคัญสำหรับคุณที่จะรู้ว่าเมื่อแอปของคุณไปที่พื้นหลังคุณอาจหรืออาจไม่จำเป็นต้องขยายโซลูชันนี้พร้อมกับติดตามวงจรชีวิตของกิจกรรมและอะไรก็ตาม

เพียงจำไว้ข้างต้นและมีทีมงาน QA ที่ดี;)

สิ้นสุดการอัพเดท

มันอาจจะสาย แต่มีวิธีที่เชื่อถือได้ในIce Cream Sandwich (API 14) และด้านบน

ปรากฎว่าเมื่อแอปของคุณไม่มี UI ที่มองเห็นได้อีกการโทรกลับจะถูกเรียกใช้ การเรียกกลับซึ่งคุณสามารถนำไปใช้ในคลาสที่กำหนดเองเรียกว่าComponentCallbacks2 (ใช่มีสองอัน) การโทรกลับนี้มีให้บริการเท่านั้นใน API ระดับ 14 (Ice Cream Sandwich) และสูงกว่า

โดยทั่วไปคุณจะได้รับการเรียกวิธีการ:

public abstract void onTrimMemory (int level)

ระดับคือ 20 ขึ้นไปโดยเฉพาะ

public static final int TRIM_MEMORY_UI_HIDDEN

ฉันได้ทำการทดสอบและใช้งานได้ดีเสมอเพราะระดับ 20 เป็นเพียง "ข้อเสนอแนะ" ที่คุณอาจต้องการปล่อยทรัพยากรบางอย่างเนื่องจากแอปของคุณไม่ปรากฏให้เห็นอีกต่อไป

หากต้องการอ้างอิงเอกสารอย่างเป็นทางการ:

ระดับสำหรับ onTrimMemory (int): กระบวนการแสดงส่วนต่อประสานผู้ใช้และไม่ทำเช่นนั้นอีกต่อไป การจัดสรร UI ขนาดใหญ่ควรได้รับการปล่อยตัวในเวลานี้เพื่อให้หน่วยความจำมีการจัดการที่ดีขึ้น

แน่นอนคุณควรใช้สิ่งนี้เพื่อทำสิ่งที่พูด (ล้างหน่วยความจำที่ไม่ได้ใช้ในบางเวลาล้างคอลเลกชันบางอย่างที่ไม่ได้ใช้งาน ฯลฯ ) ความเป็นไปได้ไม่มีที่สิ้นสุด (ดูเอกสารอย่างเป็นทางการสำหรับคนอื่นระดับวิกฤติ )

แต่สิ่งที่น่าสนใจคือระบบปฏิบัติการกำลังบอกคุณ: เฮ้แอปของคุณไปที่พื้นหลัง!

ซึ่งเป็นสิ่งที่คุณต้องการรู้ตั้งแต่แรก

คุณจะตัดสินอย่างไรเมื่อคุณกลับมา?

ง่ายมากฉันแน่ใจว่าคุณมี "BaseActivity" เพื่อให้คุณสามารถใช้ onResume () ของคุณเพื่อตั้งค่าสถานะความเป็นจริงที่คุณกลับมา เพราะมีเพียงครั้งเดียวที่คุณจะบอกว่าคุณไม่ได้กลับมาเมื่อคุณได้รับสายจริงonTrimMemoryวิธีการดังกล่าว

มันได้ผล. คุณไม่ได้รับผลบวกผิด ๆ หากกิจกรรมกลับมาทำงานต่อคุณกลับมาแล้ว 100% หากผู้ใช้ไปที่ด้านหลังอีกครั้งคุณจะได้รับonTrimMemory()สายอีกครั้ง

คุณต้องประกาศกิจกรรมของคุณ (หรือดีกว่านั้นเป็นคลาสที่กำหนดเอง)

วิธีที่ง่ายที่สุดในการรับประกันว่าคุณจะได้รับสิ่งนี้เสมอคือการสร้างคลาสแบบง่าย ๆ ดังนี้:

public class MemoryBoss implements ComponentCallbacks2 {
    @Override
    public void onConfigurationChanged(final Configuration newConfig) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // We're in the Background
        }
        // you might as well implement some memory cleanup here and be a nice Android dev.
    }
}

ในการใช้สิ่งนี้ในการติดตั้งแอปพลิเคชันของคุณ ( คุณมีใช่มั้ย? ) ทำสิ่งต่อไปนี้:

MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
   super.onCreate();
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      mMemoryBoss = new MemoryBoss();
      registerComponentCallbacks(mMemoryBoss);
   } 
}

หากคุณสร้างInterfaceคุณสามารถเพิ่มสิ่งelseนั้นifและนำไปใช้ComponentCallbacks(โดยไม่ต้องใช้ 2) ในสิ่งใดก็ตามด้านล่าง API 14 การโทรกลับนั้นมีเพียงonLowMemory()วิธีการและไม่ได้รับการเรียกเมื่อคุณไปที่พื้นหลังแต่คุณควรใช้มันเพื่อตัดหน่วยความจำ .

ตอนนี้เปิดแอพแล้วกด home onTrimMemory(final int level)ควรเรียกวิธีการของคุณ(คำใบ้: เพิ่มการบันทึก)

ขั้นตอนสุดท้ายคือการยกเลิกการลงทะเบียนจากการโทรกลับ สถานที่ที่ดีที่สุดน่าจะเป็นonTerminate()วิธีการใช้แอพของคุณแต่วิธีการนั้นไม่ได้รับการเรียกใช้บนอุปกรณ์จริง:

/**
 * This method is for use in emulated process environments.  It will
 * never be called on a production Android device, where processes are
 * removed by simply killing them; no user code (including this callback)
 * is executed when doing so.
 */

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

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

unregisterComponentCallbacks(mMemoryBoss);

และนั่นคือมัน


เมื่อตรวจสอบสิ่งนี้จากบริการดูเหมือนว่าจะทำงานเฉพาะเมื่อกดปุ่มโฮม การกดปุ่มย้อนกลับไม่ได้เป็นการใช้งาน KitKat
เรียนรู้ OpenGL ES

บริการไม่มี UI ดังนั้นจึงอาจเกี่ยวข้องกับสิ่งนั้น ทำการตรวจสอบในกิจกรรมฐานของคุณไม่ใช่ในบริการ คุณต้องการที่จะรู้ว่าเมื่อ UI ของคุณถูกซ่อนอยู่ (และอาจจะบอกบริการดังนั้นมันจะไปข้างหน้า)
Martin Marconcini

1
มันไม่ทำงานเมื่อคุณปิดโทรศัพท์ มันไม่ได้ถูกเรียก
Juangcg

2
การใช้ ComponentCallbacks2.onTrimMemory () (ร่วมกับ ActivityLifecycleCallbacks) เป็นทางออกเดียวที่เชื่อถือได้ที่ฉันพบมาขอบคุณ Martin! สำหรับผู้ที่สนใจดูคำตอบของฉันให้
rickul

3
ฉันใช้วิธีนี้มาตั้งแต่ปีที่แล้วและมันก็น่าเชื่อถือสำหรับฉันเสมอ เป็นเรื่องดีที่คนอื่นจะใช้ด้วย ฉันแค่ใช้level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENสิ่งที่หลีกเลี่ยงปัญหาในการอัปเดตของคุณจุดที่ 2 เกี่ยวกับจุดที่ 1 มันไม่ใช่เรื่องที่น่ากังวลสำหรับฉันเพราะแอพไม่ได้ไปที่พื้นหลังจริงๆ
sorianiv

175

นี่คือวิธีที่ฉันจัดการเพื่อแก้ไขปัญหานี้ มันทำงานบนสมมติฐานที่ว่าการใช้การอ้างอิงเวลาระหว่างการเปลี่ยนกิจกรรมมักจะให้หลักฐานที่เพียงพอว่าแอปได้รับ "แบ็กกราวน์" หรือไม่

อันดับแรกฉันใช้อินสแตนซ์ android.app.Application (เรียกว่า MyApplication) ซึ่งมีตัวจับเวลา TimerTask ค่าคงที่เพื่อแสดงจำนวนมิลลิวินาทีสูงสุดที่การเปลี่ยนจากกิจกรรมหนึ่งไปอีกกิจกรรมหนึ่งอาจทำได้อย่างสมเหตุสมผล (ฉันไป ด้วยค่า 2s) และบูลีนเพื่อระบุว่าแอปนั้น "อยู่ในพื้นหลัง" หรือไม่:

public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
    ...

แอปพลิเคชันยังมีวิธีการสองวิธีสำหรับการเริ่มและหยุดตัวจับเวลา / งาน:

public void startActivityTransitionTimer() {
    this.mActivityTransitionTimer = new Timer();
    this.mActivityTransitionTimerTask = new TimerTask() {
        public void run() {
            MyApplication.this.wasInBackground = true;
        }
    };

    this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
                                           MAX_ACTIVITY_TRANSITION_TIME_MS);
}

public void stopActivityTransitionTimer() {
    if (this.mActivityTransitionTimerTask != null) {
        this.mActivityTransitionTimerTask.cancel();
    }

    if (this.mActivityTransitionTimer != null) {
        this.mActivityTransitionTimer.cancel();
    }

    this.wasInBackground = false;
}

ส่วนสุดท้ายของโซลูชันนี้คือการเพิ่มการเรียกไปยังแต่ละวิธีเหล่านี้จากเหตุการณ์ onResume () และ onPause () ของกิจกรรมทั้งหมดหรือโดยเฉพาะอย่างยิ่งในกิจกรรมพื้นฐานที่กิจกรรมคอนกรีตของคุณสืบทอด:

@Override
public void onResume()
{
    super.onResume();

    MyApplication myApp = (MyApplication)this.getApplication();
    if (myApp.wasInBackground)
    {
        //Do specific came-here-from-background code
    }

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}

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

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


4
สวัสดี d60402 คำตอบของคุณมีประโยชน์จริงๆ .. ขอบคุณมากสำหรับการตอบกลับนี้ ... การแจ้งเตือนเล็กน้อย .. MyApplication ควรกล่าวถึงในแท็กแอปพลิเคชันไฟล์ Manifest เช่น android: name = "MyApplication" มิฉะนั้นแอปขัดข้อง ... เพียงเพื่อช่วย ใครบางคนเช่นฉัน
praveenb

2
เครื่องหมายของโปรแกรมเมอร์ที่ยอดเยี่ยมทางออกที่ง่ายสำหรับหนึ่งในปัญหาที่ซับซ้อนที่สุดที่ฉันเคยประสบมา
Aashish Bhatnagar

2
ทางออกที่ยอดเยี่ยม! ขอบคุณ หากใครได้รับข้อผิดพลาด "ClassCastException" คุณอาจพลาดการเพิ่มลงในแท็กแอปพลิเคชันภายใน Manifest.xml ของคุณ <application android: name = "your.package.MyApplication"
Wahib Ul Haq

27
นี่เป็นการใช้งานที่ดีและเรียบง่าย อย่างไรก็ตามฉันเชื่อว่าสิ่งนี้ควรถูกนำไปใช้ใน onStart / onStop มากกว่า onPause / onResume onPause จะถูกเรียกแม้ว่าฉันจะเริ่มต้นกล่องโต้ตอบซึ่งครอบคลุมบางส่วนของกิจกรรม และการปิดกล่องโต้ตอบจะโทรหาจริงทำให้ดูเหมือนว่าแอพเพิ่งมาเบื้องหน้า
Shubhayu

7
ฉันหวังว่าจะใช้รูปแบบของโซลูชันนี้ ประเด็นเกี่ยวกับบทสนทนาที่ระบุข้างต้นเป็นปัญหาสำหรับฉันดังนั้นฉันจึงลองใช้ @ Shubhayu ข้อเสนอแนะ (onStart / onStop) อย่างไรก็ตามก็ไม่ได้ช่วยอะไรเพราะเมื่อไปที่ A-> B จะมีการเรียกใช้กิจกรรม onStart () ของกิจกรรม B ก่อน onStop () ของกิจกรรม A
เทรเวอร์

150

แก้ไข:องค์ประกอบสถาปัตยกรรมใหม่นำเสนอสิ่งที่มีแนวโน้ม: ProcessLifecycleOwnerดูคำตอบของ @ vokilam


ทางออกที่แท้จริงตามการพูดคุยของ Google I / O :

class YourApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(AppLifecycleTracker())
  }

}


class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {

  private var numStarted = 0

  override fun onActivityStarted(activity: Activity?) {
    if (numStarted == 0) {
      // app went to foreground
    }
    numStarted++
  }

  override fun onActivityStopped(activity: Activity?) {
    numStarted--
    if (numStarted == 0) {
      // app went to background
    }
  }

}

ใช่. ฉันรู้ว่ามันยากที่จะเชื่อว่าโซลูชันง่าย ๆ นี้ทำงานได้เนื่องจากเรามีโซลูชันแปลก ๆ มากมายที่นี่

แต่มีความหวัง


3
มันทำงานได้อย่างสมบูรณ์แบบ! ฉันได้ลองวิธีแก้ปัญหาแปลก ๆ ที่มีข้อบกพร่องมากมายแล้วขอบคุณมาก! ฉันหามาได้ซักพักแล้ว
Eggakin Baconwalker

7
มันใช้งานได้สำหรับหลาย ๆ กิจกรรม แต่สำหรับหนึ่งตัวเมื่อเปิดเครื่องจะระบุว่ากิจกรรมทั้งหมดหายไปหรือเป็น
แบ็

2
@Shyri คุณถูกต้อง แต่นั่นเป็นส่วนหนึ่งของโซลูชันนี้ดังนั้นคุณจึงต้องกังวล หาก firebase อาศัยสิ่งนี้ฉันคิดว่าแอปธรรมดาของฉันก็สามารถทำได้เช่นกัน :) คำตอบที่ดี BTW
ElliotM

3
@deadfish ตรวจสอบลิงก์ไปยัง I / O ที่ให้ไว้ในส่วนบนของคำตอบ คุณสามารถตรวจสอบช่องว่างเวลาระหว่างหยุดกิจกรรมและเริ่มตรวจสอบว่าคุณไปที่พื้นหลังจริงหรือไม่ นี่เป็นทางออกที่ยอดเยี่ยมจริงๆ
Alex Berdnikov

2
มีวิธีแก้ปัญหา Java หรือไม่? นี่คือ kotlin
Giacomo Bartoli

116

ProcessLifecycleOwner ดูเหมือนว่าจะเป็นทางออกที่มีแนวโน้มด้วย

ProcessLifecycleOwner จะส่งON_START, ON_RESUMEเหตุการณ์ที่เกิดขึ้นเป็นย้ายกิจกรรมแรกผ่านเหตุการณ์เหล่านี้ ON_PAUSE, ON_STOPกิจกรรมจะถูกส่งไปพร้อมกับความล่าช้าหลังจากกิจกรรมครั้งสุดท้ายผ่านไปแล้ว ความล่าช้านี้นานพอที่จะรับประกันว่าProcessLifecycleOwnerจะไม่ส่งกิจกรรมใด ๆ หากกิจกรรมถูกทำลายและสร้างใหม่เนื่องจากการเปลี่ยนแปลงการกำหนดค่า

การใช้งานสามารถทำได้ง่ายเพียงแค่

class AppLifecycleListener : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onMoveToForeground() { // app moved to foreground
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onMoveToBackground() { // app moved to background
    }
}

// register observer
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())

700msตามรหัสที่มาล่าช้ามูลค่าปัจจุบันคือ

การใช้คุณสมบัตินี้ต้องการdependencies:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"

10
โปรดทราบว่าคุณต้องเพิ่มการพึ่งพาวงจรชีวิตimplementation "android.arch.lifecycle:extensions:1.0.0"และannotationProcessor "android.arch.lifecycle:compiler:1.0.0"จากที่เก็บของ Google (เช่นgoogle())
Sir Codesalot

1
มันใช้งานได้ดีมากสำหรับฉันขอบคุณ ฉันต้องใช้ api 'android.arch.lifecycle: ส่วนขยาย: 1.1.0' แทนการใช้งานเนื่องจากข้อผิดพลาดที่ระบุการพึ่งพา Android มีเวอร์ชันที่แตกต่างกันสำหรับคอมไพล์และรันไทม์คลาสพา ธ
FSUWX2011

นี่เป็นโซลูชันที่ยอดเยี่ยมเพราะทำงานได้ในโมดูลโดยไม่จำเป็นต้องมีการอ้างอิงกิจกรรม!
สูงสุด

สิ่งนี้ไม่ทำงานเมื่อแอพขัดข้อง มีวิธีใดบ้างที่จะทำให้แอพพลิเคชั่นขัดข้องด้วยวิธีนี้
4357

ทางออกที่ดี บันทึกวันของฉัน
ซันนี่

69

จากคำตอบของMartín Marconcinis (ขอบคุณ!) ในที่สุดฉันก็พบวิธีแก้ปัญหาที่เชื่อถือได้

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
    private static boolean isInBackground = false;

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityStarted(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){
            Log.d(TAG, "app went to foreground");
            isInBackground = false;
        }
    }

    @Override
    public void onActivityPaused(Activity activity) {
    }

    @Override
    public void onActivityStopped(Activity activity) {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(int i) {
        if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
            Log.d(TAG, "app went to background");
            isInBackground = true;
        }
    }
}

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

public class MyApp extends android.app.Application {

    @Override
    public void onCreate() {
        super.onCreate();

        ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
        registerActivityLifecycleCallbacks(handler);
        registerComponentCallbacks(handler);

    }

}

คุณสามารถแสดงให้เห็นว่าคุณใช้สิ่งนี้ในแอพได้อย่างไรฉันจะเรียกมันจากคลาสแอพหรือที่อื่น ๆ
JPM

นี่เป็นคำขอบคุณที่สมบูรณ์แบบ !! ใช้งานได้ดีในการทดสอบจนถึงตอนนี้
aherrick

ตัวอย่างนี้ถ้าไม่สมบูรณ์ registerActivityLifecycleCallbacks คืออะไร
Noman

เป็นวิธีการในคลาส android.app.Application
rickul

1
ทำได้ดี +1 ไปที่ด้านบนเพราะมันสมบูรณ์แบบอย่ามองคำตอบอื่น ๆ นี่เป็นคำตอบที่ @reno แต่มีตัวอย่างจริง
Stoycho Andreev

63

เราใช้วิธีนี้ มันดูง่ายเกินไปที่จะทำงาน แต่มันผ่านการทดสอบอย่างดีในแอพของเราและในความเป็นจริงแล้วมันทำงานได้ดีอย่างน่าประหลาดใจในทุกกรณีรวมถึงการไปที่หน้าจอหลักด้วยปุ่ม "home" โดยปุ่ม "return" หรือหลังจากล็อกหน้าจอ ให้มันลอง.

แนวคิดคือเมื่ออยู่เบื้องหน้า Android จะเริ่มกิจกรรมใหม่เสมอก่อนที่จะหยุดกิจกรรมก่อนหน้านี้ ไม่รับประกัน แต่นั่นเป็นวิธีการทำงาน BTW, วุ่นวายดูเหมือนจะใช้ตรรกะเดียวกัน (เพียงเดาฉันไม่ได้ตรวจสอบว่า แต่ตะขอในเหตุการณ์เดียวกัน)

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

    @Override
    protected void onStart() {
        super.onStart();       
        sessionDepth++;
        if(sessionDepth == 1){
        //app came to foreground;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (sessionDepth > 0)
            sessionDepth--;
        if (sessionDepth == 0) {
            // app went to background
        }
    }

}

แก้ไข: ตามความคิดเห็นเรายังย้ายไปที่ onStart () ในรหัสรุ่นที่ใหม่กว่า นอกจากนี้ฉันกำลังเพิ่มการโทรสุด ๆ ซึ่งหายไปจากโพสต์เริ่มต้นของฉันเพราะนี่เป็นแนวคิดมากกว่าโค้ดที่ใช้งานได้


2
นี่เป็นคำตอบที่น่าเชื่อถือที่สุดแม้ว่าฉันจะใช้ onStart แทน onResume
เกร็กเอนนิส

คุณควรเพิ่มการเรียกไปยัง super.onResume () และ super.onStop () ในเมธอด overriden มิฉะนั้นจะมีการโยน android.app.SuperNotCalledException
Jan Laussmann

1
สำหรับฉันมันไม่ทำงาน ... หรืออย่างน้อยก็ไฟเหตุการณ์เมื่อคุณหมุนอุปกรณ์ด้วย (ซึ่งเป็น imho บวกปลอม)
Noya

ทางออกที่ง่ายและมีประสิทธิภาพมาก! แต่ฉันไม่แน่ใจว่ามันทำงานกับกิจกรรมที่โปร่งใสบางส่วนซึ่งทำให้บางส่วนของกิจกรรมก่อนหน้านี้ปรากฏ จากเอกสาร, onStop is called when the activity is no longer visible to the user.
Nicolas Buquet

3
จะเกิดอะไรขึ้นหากผู้ใช้เปลี่ยนการวางแนวในกิจกรรมแรก มันจะรายงานว่าแอปทำงานเป็นพื้นหลังซึ่งไม่เป็นความจริง คุณจัดการสถานการณ์นี้ได้อย่างไร
Nimrod Dayan

54

หากแอปของคุณประกอบด้วยหลายกิจกรรมและ / หรือกิจกรรมที่ซ้อนกันเช่นวิดเจ็ตแถบแท็บการแทนที่ onPause () และ onResume () จะไม่ทำงาน เช่นเมื่อเริ่มกิจกรรมใหม่กิจกรรมปัจจุบันจะหยุดชั่วคราวก่อนที่กิจกรรมใหม่จะถูกสร้างขึ้น เช่นเดียวกับเมื่อเสร็จสิ้น (โดยใช้ปุ่ม "ย้อนกลับ") กิจกรรม

ฉันพบสองวิธีที่ดูเหมือนจะทำงานได้ตามที่ต้องการ

คนแรกต้องได้รับอนุญาต GET_TASKS และประกอบด้วยวิธีการง่าย ๆ ที่ตรวจสอบว่ากิจกรรมที่ทำงานอยู่บนอุปกรณ์เป็นของแอปพลิเคชันหรือไม่โดยการเปรียบเทียบชื่อแพคเกจ:

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }

    return false;
}

วิธีนี้พบในกรอบการทำงานของ Droid-Fu (ปัจจุบันเรียกว่าระบบจุดระเบิด)

วิธีที่สองที่ฉันใช้เองไม่ต้องได้รับอนุญาตจาก GET_TASKS ซึ่งดี แต่มันซับซ้อนกว่าเล็กน้อยที่จะใช้

ในคลาส MainApplication ของคุณคุณมีตัวแปรที่ติดตามจำนวนกิจกรรมการทำงานในแอปพลิเคชันของคุณ ใน onResume () สำหรับแต่ละกิจกรรมคุณเพิ่มตัวแปรและใน onPause () คุณลดมันลง

เมื่อจำนวนกิจกรรมที่รันถึง 0 แอปพลิเคชันจะถูกใส่ลงในพื้นหลังหากเงื่อนไขต่อไปนี้เป็นจริง:

  • กิจกรรมที่หยุดชั่วคราวไม่เสร็จสิ้น (ใช้ปุ่ม "ย้อนกลับ") สามารถทำได้โดยใช้วิธีกิจกรรม activity.isFinishing ()
  • กิจกรรมใหม่ (ชื่อแพ็กเกจเดียวกัน) ไม่เริ่มขึ้น คุณสามารถแทนที่เมธอด startActivity () เพื่อตั้งค่าตัวแปรที่ระบุสิ่งนี้และตั้งค่าใหม่ใน onPostResume () ซึ่งเป็นวิธีสุดท้ายที่จะเรียกใช้เมื่อกิจกรรมถูกสร้าง / ดำเนินการต่อ

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


18
Google อาจปฏิเสธแอปที่ใช้ ActivityManager.getRunningTasks () เอกสารระบุว่ามันเป็นจุดประสงค์ของศัตรู foe เท่านั้น developer.android.com/reference/android/app/…
Sky Kelsey


1
ฉันพบว่าฉันต้องใช้การผสมผสานของวิธีการเหล่านี้ onUserLeaveHint () ถูกเรียกเมื่อเปิดตัวกิจกรรมใน 14 `โมฆะสาธารณะ @Override onUserLeaveHint () {inBackground = isApplicationBroughtToBackground (); } `
รายการเรือ

7
ผู้ใช้จะไม่พอใจเกินไปเกี่ยวกับการใช้สิทธิ์ที่มีประสิทธิภาพ android.permission.GET_TASKS
MSquare

6
getRunningTasks เลิกใช้แล้วใน API ระดับ 21
Noya

33

สร้างชั้นApplicationที่ขยาย จากนั้นเราสามารถใช้วิธีการแทนที่, onTrimMemory().

ในการตรวจสอบว่าแอปพลิเคชันไปที่พื้นหลังหรือไม่เราจะใช้:

 @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
            // Get called every-time when application went to background.
        } 
        else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
        }
    }

1
สำหรับFragmentActivityคุณอาจต้องการเพิ่มlevel == ComponentCallbacks2.TRIM_MEMORY_COMPLETEด้วย
Srujan Simha

2
ขอบคุณมากสำหรับการชี้ไปที่วิธีนี้ฉันต้องแสดง Pin Dialog เมื่อใดก็ตามที่ผู้ใช้ดำเนินกิจกรรมต่อสำหรับพื้นหลังใช้วิธีนี้เพื่อเขียนค่า pref และตรวจสอบค่านี้บน baseActivity
Sam

18

พิจารณาใช้ onUserLeaveHint สิ่งนี้จะถูกเรียกเมื่อแอปของคุณเข้าสู่พื้นหลัง onPause จะมีมุมเคสที่ต้องจัดการเนื่องจากสามารถถูกเรียกด้วยเหตุผลอื่น ตัวอย่างเช่นหากผู้ใช้เปิดกิจกรรมอื่นในแอปของคุณเช่นหน้าการตั้งค่าวิธีการ onPause ของกิจกรรมหลักของคุณจะถูกเรียกแม้ว่าพวกเขาจะยังคงอยู่ในแอปของคุณ การติดตามสิ่งที่กำลังเกิดขึ้นจะนำไปสู่ข้อผิดพลาดเมื่อคุณสามารถใช้การติดต่อกลับ onUserLeaveHint แทนซึ่งทำสิ่งที่คุณขอ

เมื่อเรียกใช้ UserLeaveHint คุณสามารถตั้งค่าบูลีน inBackground ให้เป็นจริง เมื่อเรียกว่า onResume ให้ถือว่าคุณกลับมาที่หน้าก่อนหากตั้งค่าสถานะ inBackground นี่เป็นเพราะ onResume จะถูกเรียกใช้ในกิจกรรมหลักของคุณหากผู้ใช้อยู่ในเมนูการตั้งค่าของคุณและไม่เคยออกจากแอป

โปรดจำไว้ว่าหากผู้ใช้กดปุ่มหน้าหลักในขณะที่อยู่ในหน้าจอการตั้งค่าของคุณ onUserLeaveHint จะถูกเรียกในกิจกรรมการตั้งค่าของคุณและเมื่อพวกเขากลับมาที่รีบูตจะถูกเรียกในกิจกรรมการตั้งค่าของคุณ หากคุณมีรหัสตรวจจับนี้ในกิจกรรมหลักของคุณคุณจะพลาดกรณีการใช้งานนี้ หากต้องการให้รหัสนี้ในกิจกรรมทั้งหมดของคุณโดยไม่ต้องทำรหัสซ้ำให้มีคลาสกิจกรรมที่เป็นนามธรรมซึ่งขยายกิจกรรมและใส่รหัสทั่วไปลงไป จากนั้นแต่ละกิจกรรมที่คุณสามารถขยายกิจกรรมนามธรรมนี้ได้

ตัวอย่างเช่น:

public abstract AbstractActivity extends Activity {
    private static boolean inBackground = false;

    @Override
    public void onResume() {
        if (inBackground) {
            // You just came from the background
            inBackground = false;
        }
        else {
            // You just returned from another activity within your own app
        }
    }

    @Override
    public void onUserLeaveHint() {
        inBackground = true;
    }
}

public abstract MainActivity extends AbstractActivity {
    ...
}

public abstract SettingsActivity extends AbstractActivity {
    ...
}

19
onUserLeaveHint จะถูกเรียกเมื่อนำทางไปยังกิจกรรมอื่น
Jonas Stawski

3
onUserLeaveHint จะไม่ถูกเรียกเมื่อมีการโทรเข้ามาและกิจกรรมการโทรจะเปิดใช้งานดังนั้นจึงมีกรณีขอบเช่นกัน - อาจมีกรณีอื่นเช่นกันเนื่องจากคุณสามารถเพิ่มการตั้งค่าสถานะเพื่อเจตนาที่จะระงับการโทร onUserLeaveHint developer.android.com/reference/android/content/…
Groxx

1
นอกจากนี้ onResume ทำงานได้ไม่ดี ฉันลองและบางครั้งเรียกว่า onResume ขณะที่โทรศัพท์ถูกล็อค หากคุณเห็นคำจำกัดความของ onResume ในเอกสารคุณจะพบ: โปรดทราบว่า onResume ไม่ใช่ตัวบ่งชี้ที่ดีที่สุดที่กิจกรรมของคุณจะปรากฏต่อผู้ใช้ หน้าต่างระบบเช่นปุ่มกดอาจอยู่ด้านหน้า ใช้ onWindowFocusChanged (บูลีน) เพื่อให้ทราบว่ากิจกรรมของคุณจะปรากฏแก่ผู้ใช้ (ตัวอย่างเช่นเพื่อเล่นเกมต่อ) developer.android.com/reference/android/app/…
J-Rou

วิธีนี้ไม่ช่วยในการตัดสินใจเบื้องหน้า / พื้นหลังหากมีหลายกิจกรรมPlz
Raj Trivedi

14

ActivityLifecycleCallbacksอาจเป็นที่สนใจ แต่ก็ยังไม่ได้บันทึกไว้อย่างดี

แม้ว่าถ้าคุณเรียกregisterActivityLifecycleCallbacks () คุณควรจะได้รับ callback เมื่อกิจกรรมถูกสร้างทำลาย ฯลฯ คุณสามารถเรียกgetComponentName () สำหรับกิจกรรมได้


11
ตั้งแต่ api ระดับ 14 = \
imort

ดูเหมือนว่าคนนี้จะสะอาดและใช้งานได้สำหรับฉัน ขอบคุณ
duanbo1983

สิ่งนี้แตกต่างจากคำตอบที่ยอมรับทั้งคู่พึ่งพาวงจรชีวิตของกิจกรรมเดียวกันใช่หรือไม่
ไซตามะ

13

android.arch.lifecycleแพคเกจให้เรียนและอินเตอร์เฟซที่ช่วยให้คุณสร้างส่วนประกอบวงจรทราบ

แอปพลิเคชันของคุณควรใช้อินเทอร์เฟซ LifecycleObserver:

public class MyApplication extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onAppBackgrounded() {
        Log.d("MyApp", "App in background");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onAppForegrounded() {
        Log.d("MyApp", "App in foreground");
    }
}

ในการทำเช่นนั้นคุณต้องเพิ่มการขึ้นต่อกันนี้ในไฟล์ build.gradle ของคุณ:

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
}

ตามที่ Google แนะนำคุณควรย่อเล็กสุดรหัสที่ประมวลผลในวิธีการดำเนินชีวิต:

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

คุณสามารถอ่านเพิ่มเติมได้ที่นี่: https://developer.android.com/topic/l ไลบรารี/architecture/lifecycle


และเพิ่มลงในรายการที่ต้องการ: <application android: name = ". AnotherApp">
Dan Alboteanu

9

ในแอปพลิเคชันของคุณเพิ่มการติดต่อกลับและตรวจสอบกิจกรรมรูทในลักษณะดังนี้:

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivityStarted(Activity activity) {
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
        }

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
                Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
                loadDefaults();
            }
        }
    });
}

ฉันจะพิจารณาใช้วิธีการใช้งานนี้ การเปลี่ยนจากกิจกรรมหนึ่งไปอีกกิจกรรมหนึ่งใช้เวลาเพียงไม่กี่มิลลิวินาที ขึ้นอยู่กับเวลาที่กิจกรรมสุดท้ายที่หายไปถือได้ว่าเป็นการเข้าสู่ระบบของผู้ใช้อีกครั้งด้วยกลยุทธ์เฉพาะ
drindt

6

ฉันได้สร้างโครงการบน Github app-foreground-background-Listen

สร้าง BaseActivity สำหรับกิจกรรมทั้งหมดในแอปพลิเคชันของคุณ

public class BaseActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    public static boolean isAppInFg = false;
    public static boolean isScrInFg = false;
    public static boolean isChangeScrFg = false;

    @Override
    protected void onStart() {
        if (!isAppInFg) {
            isAppInFg = true;
            isChangeScrFg = false;
            onAppStart();
        }
        else {
            isChangeScrFg = true;
        }
        isScrInFg = true;

        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();

        if (!isScrInFg || !isChangeScrFg) {
            isAppInFg = false;
            onAppPause();
        }
        isScrInFg = false;
    }

    public void onAppStart() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in foreground",    Toast.LENGTH_LONG).show();

        // Your code
    }

    public void onAppPause() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in background",  Toast.LENGTH_LONG).show();

        // Your code
    }
}

ตอนนี้ใช้ BaseActivity เป็นคลาสซุปเปอร์ของกิจกรรมทั้งหมดของคุณเช่น MainActivity ขยาย BaseActivity และ onAppStart จะถูกเรียกเมื่อคุณเริ่มโปรแกรมประยุกต์ของคุณและ onAppPause () จะถูกเรียกเมื่อแอปพลิเคชันไปที่พื้นหลังจากหน้าจอใด ๆ


@kiran boghra: มีผลบวกปลอม ๆ ในการแก้ปัญหาของคุณหรือไม่?
Harish Vishwakarma

คำตอบที่สมบูรณ์แบบฟังก์ชั่น onStart () และ onStop () สามารถใช้ในกรณีนี้ ซึ่งจะบอกคุณเกี่ยวกับแอปของคุณ
Pir Fahim Shah

6

กระบวนการนี้ค่อนข้างง่ายด้วยProcessLifecycleOwner

เพิ่มการพึ่งพาเหล่านี้

implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"

ในKotlin :

class ForegroundBackgroundListener : LifecycleObserver {


    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun startSomething() {
        Log.v("ProcessLog", "APP IS ON FOREGROUND")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stopSomething() {
        Log.v("ProcessLog", "APP IS IN BACKGROUND")
    }
}

จากนั้นในกิจกรรมพื้นฐานของคุณ:

override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get()
                .lifecycle
                .addObserver(
                        ForegroundBackgroundListener()
                                .also { appObserver = it })
    }

ดูบทความของฉันในหัวข้อนี้: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48


5

คุณสามารถใช้ProcessLifecycleOwnerแนบผู้สังเกตการณ์วงจรชีวิต

  public class ForegroundLifecycleObserver implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onAppCreated() {
        Timber.d("onAppCreated() called");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppStarted() {
        Timber.d("onAppStarted() called");
    }

    @OnLifecycleEvent(Event.ON_RESUME)
    public void onAppResumed() {
        Timber.d("onAppResumed() called");
    }

    @OnLifecycleEvent(Event.ON_PAUSE)
    public void onAppPaused() {
        Timber.d("onAppPaused() called");
    }

    @OnLifecycleEvent(Event.ON_STOP)
    public void onAppStopped() {
        Timber.d("onAppStopped() called");
    }
}

จากนั้นในonCreate()คลาสแอปพลิเคชันของคุณคุณเรียกสิ่งนี้ว่า:

ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());

ด้วยสิ่งนี้คุณจะสามารถจับภาพกิจกรรมON_PAUSEและON_STOPแอปพลิเคชันของคุณที่เกิดขึ้นเมื่อมันอยู่ในพื้นหลัง


4

ไม่มีวิธีวงจรชีวิตที่ตรงไปตรงมาที่จะบอกคุณเมื่อแอปพลิเคชันทั้งหมดไปที่พื้นหลัง / เบื้องหน้า

ฉันทำสิ่งนี้ด้วยวิธีที่ง่าย ทำตามคำแนะนำด้านล่างเพื่อตรวจสอบพื้นหลังแอปพลิเคชัน / เบื้องหน้าเฟส

ด้วยวิธีแก้ปัญหาเพียงเล็กน้อยก็เป็นไปได้ ที่นี่ActivityLifecycleCallbacksมาช่วยเหลือ ให้ฉันเดินผ่านทีละขั้นตอน

  1. ขั้นแรกให้สร้างคลาสที่ขยายandroid.app.Applicationและใช้ส่วนต่อประสานActivityLifecycleCallbacks ใน Application.onCreate () ให้ลงทะเบียนการติดต่อกลับ

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
  2. การลงทะเบียน“App” <application android:name=".App"ชั้นในเป็นที่ประจักษ์ด้านล่าง

  3. จะมีกิจกรรมอย่างน้อยหนึ่งรายการในสถานะเริ่มต้นเมื่อแอปอยู่ในเบื้องหน้าและจะไม่มีกิจกรรมในสถานะเริ่มต้นเมื่อแอปอยู่ในพื้นหลัง

    ประกาศตัวแปร 2 ตัวดังต่อไปนี้ในคลาส“ แอป”

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;

    activityReferencesจะเก็บจำนวนกิจกรรมในสถานะเริ่มต้น isActivityChangingConfigurationsเป็นแฟล็กเพื่อระบุว่ากิจกรรมปัจจุบันกำลังดำเนินการเปลี่ยนแปลงการกำหนดค่าเช่นสวิตช์การวางแนวหรือไม่

  4. การใช้รหัสต่อไปนี้คุณสามารถตรวจสอบได้ว่าแอปมาก่อนหน้าหรือไม่

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
  5. นี่คือวิธีการตรวจสอบว่าแอปทำงานเป็นพื้นหลังหรือไม่

    @Override
    public void onActivityStopped(Activity activity) {
        isActivityChangingConfigurations = activity.isChangingConfigurations();
        if (--activityReferences == 0 && !isActivityChangingConfigurations) {
            // App enters background
        }
    }

มันทำงานอย่างไร:

นี่เป็นเคล็ดลับเล็ก ๆ น้อย ๆ ที่ทำกับวิธีการที่เรียกว่า Lifecycle ตามลำดับ ให้ฉันแนะนำสถานการณ์

สมมติว่าผู้ใช้เปิดแอพและเรียกใช้งานกิจกรรมตัวเรียกใช้งาน A การโทรระยะเวลาจะเป็น

A.onCreate ()

A.onStart () (++ activityReferences == 1) (แอปเข้าสู่เบื้องหน้า)

A.onResume ()

กิจกรรม A เริ่มกิจกรรม B

A.onPause ()

B.onCreate ()

B.onStart () (++ activityReferences == 2)

B.onResume ()

A.onStop () (--activityReferences == 1)

จากนั้นผู้ใช้นำทางกลับจากกิจกรรม B

B.onPause ()

A.onStart () (++ activityReferences == 2)

A.onResume ()

B.onStop () (--activityReferences == 1)

B.onDestroy ()

จากนั้นผู้ใช้กดปุ่มโฮม

A.onPause ()

A.onStop () (--activityReferences == 0) (แอปเข้าสู่พื้นหลัง)

ในกรณีที่ผู้ใช้กดปุ่มโฮมจากกิจกรรม B แทนปุ่มย้อนกลับก็จะยังคงเหมือนเดิมและกิจกรรมการอ้างอิงจะเป็น 0เช่นเดิม ดังนั้นเราสามารถตรวจจับได้ว่าเป็นแอปที่เข้าสู่พื้นหลัง

ดังนั้นบทบาทของisActivityChangingConfigurationsอะไร ในสถานการณ์ข้างต้นสมมติว่ากิจกรรม B เปลี่ยนการวางแนว ลำดับการติดต่อกลับจะเป็น

B.onPause ()

B.onStop () (--activityReferences == 0) (แอปเข้าสู่พื้นหลัง ??)

B.onDestroy ()

B.onCreate ()

B.onStart () (++ activityReferences == 1) (แอปเข้าสู่ Foreground ??)

B.onResume ()

นั่นเป็นเหตุผลที่เรามีการตรวจสอบเพิ่มเติมisActivityChangingConfigurationsเพื่อหลีกเลี่ยงสถานการณ์เมื่อกิจกรรมผ่านการเปลี่ยนแปลงการกำหนดค่า


3

ฉันพบวิธีที่ดีในการตรวจสอบแอปพลิเคชันไม่ว่าจะใส่เบื้องหน้าหรือพื้นหลัง นี่คือฉันรหัส หวังว่านี่จะช่วยคุณได้

/**
 * Custom Application which can detect application state of whether it enter
 * background or enter foreground.
 *
 * @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/android-solution-to-detect-when-android.html
 */
 public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {

public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;

private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;

private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;

@Override
public void onCreate() {
    super.onCreate();
    mCurrentState = STATE_UNKNOWN;
    registerActivityLifecycleCallbacks(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    // mCurrentState = STATE_CREATED;
}

@Override
public void onActivityStarted(Activity activity) {
    if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
        if (mStateFlag == FLAG_STATE_BACKGROUND) {
            applicationWillEnterForeground();
            mStateFlag = FLAG_STATE_FOREGROUND;
        }
    }
    mCurrentState = STATE_STARTED;

}

@Override
public void onActivityResumed(Activity activity) {
    mCurrentState = STATE_RESUMED;

}

@Override
public void onActivityPaused(Activity activity) {
    mCurrentState = STATE_PAUSED;

}

@Override
public void onActivityStopped(Activity activity) {
    mCurrentState = STATE_STOPPED;

}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(Activity activity) {
    mCurrentState = STATE_DESTROYED;
}

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidEnterBackground();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidDestroyed();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }
}

/**
 * The method be called when the application been destroyed. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidDestroyed();

/**
 * The method be called when the application enter background. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidEnterBackground();

/**
 * The method be called when the application enter foreground.
 */
protected abstract void applicationWillEnterForeground();

}


3

คุณสามารถใช้ได้:

โมฆะป้องกันบน onRestart ()

แตกต่างระหว่างการเริ่มใหม่และการเริ่มใหม่

ป้อนคำอธิบายรูปภาพที่นี่


3

แก้ไข 2: สิ่งที่ฉันเขียนด้านล่างจะไม่ทำงาน Google ปฏิเสธแอปที่รวมการโทรไปที่ ActivityManager.getRunningTasks () จากเอกสารประกอบจะเห็นได้ว่า API นี้มีวัตถุประสงค์เพื่อการดีบักและการพัฒนาเท่านั้น ฉันจะอัปเดตโพสต์นี้ทันทีที่ฉันมีเวลาอัปเดตโครงการ GitHub ด้านล่างด้วยรูปแบบใหม่ที่ใช้ตัวจับเวลาและเกือบดี

แก้ไข 1: ฉันได้เขียนบทความในบล็อกและสร้างที่เก็บ GitHub อย่างง่ายเพื่อให้ง่าย

คำตอบที่ได้รับการยอมรับและติดอันดับสูงสุดนั้นไม่ใช่วิธีที่ดีที่สุด การใช้งานคำตอบที่ได้รับคะแนนสูงสุดของ isApplicationBroughtToBackground () ไม่ได้จัดการกับสถานการณ์ที่กิจกรรมหลักของแอปพลิเคชันยอมให้กิจกรรมที่กำหนดไว้ในแอปพลิเคชันเดียวกัน แต่มีแพคเกจ Java ที่แตกต่างกัน ฉันมากับวิธีการทำเช่นนี้ที่จะทำงานในกรณีที่

เรียกสิ่งนี้ใน onPause () และมันจะบอกคุณว่าแอปพลิเคชันของคุณกำลังเข้าสู่พื้นหลังเพราะแอปพลิเคชันอื่นเริ่มต้นขึ้นหรือผู้ใช้กดปุ่มโฮม

public static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}

FYI การเรียกสิ่งนี้ใน onStart () แทนจะหลีกเลี่ยงการถูกเรียกเมื่อไดอะล็อกแบบง่าย ๆ ถูกส่งออกมาตัวอย่างเช่นสัญญาณเตือนกำลังจะดับ
Sky Kelsey

2

คำตอบที่ถูกต้องที่นี่

สร้างคลาสด้วยชื่อ MyApp เหมือนด้านล่าง:

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private Context context;
    public void setContext(Context context)
    {
        this.context = context;
    }

    private boolean isInBackground = false;

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {


            isInBackground = true;
            Log.d("status = ","we are out");
        }
    }


    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){

            isInBackground = false;
            Log.d("status = ","we are in");
        }

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }
}

จากนั้นทุกที่ที่คุณต้องการ (เปิดตัวกิจกรรมแรกที่ดีกว่าในแอป) เพิ่มรหัสด้านล่าง:

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);

ทำ! ตอนนี้เมื่อแอปอยู่ในพื้นหลังเราจะได้รับการบันทึก status : we are out และเมื่อเราเข้าไปในแอพเราจะได้รับการบันทึกstatus : we are out


1

โซลูชันของฉันได้รับแรงบันดาลใจจากคำตอบของ @ d60402 และยังขึ้นอยู่กับหน้าต่างเวลา แต่ไม่ได้ใช้Timer:

public abstract class BaseActivity extends ActionBarActivity {

  protected boolean wasInBackground = false;

  @Override
  protected void onStart() {
    super.onStart();
    wasInBackground = getApp().isInBackground;
    getApp().isInBackground = false;
    getApp().lastForegroundTransition = System.currentTimeMillis();
  }

  @Override
  protected void onStop() {
    super.onStop();
    if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
      getApp().isInBackground = true;
  }

  protected SingletonApplication getApp(){
    return (SingletonApplication)getApplication();
  }
}

โดยที่SingletonApplicationเป็นส่วนขยายของApplicationคลาส:

public class SingletonApplication extends Application {
  public boolean isInBackground = false;
  public long lastForegroundTransition = 0;
}

1

ฉันใช้สิ่งนี้กับ Google Analytics EasyTracker และใช้งานได้ อาจขยายออกไปเพื่อทำสิ่งที่คุณต้องการโดยใช้จำนวนเต็มอย่างง่าย

public class MainApplication extends Application {

    int isAppBackgrounded = 0;

    @Override
    public void onCreate() {
        super.onCreate();
        appBackgroundedDetector();
    }

    private void appBackgroundedDetector() {
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityStarted(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStart(activity);
            }

            @Override
            public void onActivityResumed(Activity activity) {
                isAppBackgrounded++;
                if (isAppBackgrounded > 0) {
                    // Do something here
                }
            }

            @Override
            public void onActivityPaused(Activity activity) {
                isAppBackgrounded--;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStop(activity);
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}

1

ฉันรู้ว่ามันสายไปนิดหน่อย แต่ฉันคิดว่าคำตอบทั้งหมดเหล่านี้มีปัญหาบางอย่างในขณะที่ฉันชอบที่ด้านล่างและมันใช้งานได้ดี

สร้างกิจกรรมวงจรชีวิตการเรียกกลับเช่นนี้:

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    Activity lastActivity;
    @Override
    public void onActivityResumed(Activity activity) {
        //if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when  app has been killed or started for the first time
        if (activity != null && activity == lastActivity) 
        {
            Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
        }

        lastActivity = activity;
    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

และเพียงลงทะเบียนในชั้นแอปพลิเคชันของคุณดังนี้:

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}

สิ่งนี้ถูกเรียกตลอดเวลาในแต่ละกิจกรรม ฉันจะใช้สิ่งนี้ได้อย่างไรตัวอย่างเช่นฉันต้องการตรวจสอบสถานะออนไลน์ของผู้ใช้
Maksim Kniazev

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

ถ้าคุณหมายถึงการเชื่อมต่ออินเทอร์เน็ตฉันคิดว่ามันจะดีกว่าที่จะตรวจสอบว่าเมื่อคุณต้องการมัน ถ้าคุณต้องการโทรหา api ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตก่อนโทร
Amir Ziarati

1

สิ่งนี้ดูเหมือนจะเป็นหนึ่งในคำถามที่ซับซ้อนที่สุดใน Android ตั้งแต่ (ขณะที่เขียนนี้) Android ไม่มี iOS ที่เทียบเท่าapplicationDidEnterBackground()หรือapplicationWillEnterForeground()โทรกลับ ฉันใช้ห้องสมุด AppStateที่ถูกใส่กันโดย@jenzz

[AppState คือ] ห้องสมุด Android ที่ใช้งานง่ายและมีปฏิกิริยาตอบสนองตาม RxJava ที่ตรวจสอบการเปลี่ยนแปลงสถานะแอป มันแจ้งเตือนสมาชิกทุกครั้งที่แอพเข้าสู่พื้นหลังและกลับเข้าสู่เบื้องหน้า

ปรากฏว่านี่เป็นสิ่งที่ฉันต้องการโดยเฉพาะอย่างยิ่งเนื่องจากแอปของฉันมีกิจกรรมหลายอย่างดังนั้นเพียงแค่ตรวจสอบonStart()หรือonStop()กิจกรรมที่ไม่ได้ลดลง

ครั้งแรกที่ฉันเพิ่มการพึ่งพาเหล่านี้เพื่อ gradle:

dependencies {
    compile 'com.jenzz.appstate:appstate:3.0.1'
    compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

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

//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of android.app.Application
appState.subscribe(new Consumer<AppState>() {
    @Override
    public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
        switch (appState) {
            case FOREGROUND:
                Log.i("info","App entered foreground");
                break;
            case BACKGROUND:
                Log.i("info","App entered background");
                break;
        }
    }
});

คุณอาจต้องยกเลิกการเป็นสมาชิกเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ ข้อมูลเพิ่มเติมอีกครั้งใน หน้า GitHub


1

นี่เป็นเวอร์ชันที่แก้ไขแล้วของคำตอบของ @ d60402: https://stackoverflow.com/a/15573121/4747587

ทำทุกอย่างที่กล่าวถึงที่นั่น แต่แทนที่จะมีBase Activityและทำสิ่งนั้นในฐานะผู้ปกครองสำหรับทุกกิจกรรมและการเอาชนะonResume()และonPauseทำสิ่งต่อไปนี้:

ในคลาสแอปพลิเคชันของคุณเพิ่มบรรทัด:

registerActivityLifecycleCallbacks (แอปพลิเคชัน ActivityLifecycleCallbacks โทรกลับ);

นี้callbackมีทั้งหมดวิธีวงจรกิจกรรมและตอนนี้คุณสามารถแทนที่และonActivityResumed()onActivityPaused()

ดูที่ส่วนสำคัญนี้: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b


1

คุณสามารถทำสิ่งนี้ได้อย่างง่ายดายด้วยความช่วยเหลือActivityLifecycleCallbacksและComponentCallbacks2บางอย่างเช่นด้านล่าง

สร้างคลาสที่AppLifeCycleHandlerใช้อินเตอร์เฟซดังกล่าวข้างต้น

package com.sample.app;

import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.os.Bundle;

/**
 * Created by Naveen on 17/04/18
 */
public class AppLifeCycleHandler
    implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

  AppLifeCycleCallback appLifeCycleCallback;

  boolean appInForeground;

  public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
    this.appLifeCycleCallback = appLifeCycleCallback;
  }

  @Override
  public void onActivityResumed(Activity activity) {
    if (!appInForeground) {
      appInForeground = true;
      appLifeCycleCallback.onAppForeground();
    }
  }

  @Override
  public void onTrimMemory(int i) {
    if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
      appInForeground = false;
      appLifeCycleCallback.onAppBackground();
    }
  }

  @Override
  public void onActivityCreated(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityStarted(Activity activity) {

  }

  @Override
  public void onActivityPaused(Activity activity) {

  }

  @Override
  public void onActivityStopped(Activity activity) {

  }

  @Override
  public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityDestroyed(Activity activity) {

  }

  @Override
  public void onConfigurationChanged(Configuration configuration) {

  }

  @Override
  public void onLowMemory() {

  }

  interface AppLifeCycleCallback {

    void onAppBackground();

    void onAppForeground();
  }
}

ในชั้นเรียนของคุณที่ขยายApplicationการใช้งานAppLifeCycleCallbackเพื่อรับการเรียกกลับเมื่อแอพสลับระหว่างพื้นหน้าและพื้นหลัง สิ่งที่ชอบด้านล่าง

public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{

    @Override
    public void onCreate() {
        super.onCreate();
        AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
        registerActivityLifecycleCallbacks(appLifeCycleHandler);
        registerComponentCallbacks(appLifeCycleHandler);
    }

    @Override
    public void onAppBackground() {
        Log.d("LifecycleEvent", "onAppBackground");
    }

    @Override
    public void onAppForeground() {
        Log.d("LifecycleEvent", "onAppForeground");
    }
}

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

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


1

เนื่องจากฉันไม่พบวิธีการใด ๆ ซึ่งจัดการการหมุนโดยไม่ตรวจสอบการประทับเวลาฉันจึงคิดว่าฉันจะแบ่งปันวิธีที่เราทำเช่นนี้ในแอปของเราด้วย นอกเหนือจากคำตอบนี้เท่านั้นhttps://stackoverflow.com/a/42679191/5119746ก็คือเราต้องคำนึงถึงเรื่องนี้ด้วย

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

   // Members

   private var mAppIsInBackground = false
   private var mCurrentOrientation: Int? = null
   private var mOrientationWasChanged = false
   private var mResumed = 0
   private var mPaused = 0

จากนั้นสำหรับการโทรกลับเรามีประวัติย่อก่อน:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

         // !!! App came from background !!! Insert code

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

และเมื่อกิจกรรมหยุด:

   override fun onActivityStopped(activity: Activity?) {

       if (mResumed == mPaused && !mOrientationWasChanged) {

       // !!! App moved to background !!! Insert code

        mAppIsInBackground = true
    }

จากนั้นที่นี่มาเพิ่มเติม: การตรวจสอบการเปลี่ยนแปลงการวางแนว:

   override fun onConfigurationChanged(newConfig: Configuration) {

       if (newConfig.orientation != mCurrentOrientation) {
           mCurrentOrientation = newConfig.orientation
           mOrientationWasChanged = true
       }
       super.onConfigurationChanged(newConfig)
   }

แค่นั้นแหละ. หวังว่าจะช่วยให้ใครบางคน :)


1

เราสามารถขยายโซลูชันนี้โดยใช้LiveData:

class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {

    private var lifecycleListener: LifecycleObserver? = null

    override fun onActive() {
        super.onActive()
        lifecycleListener = AppLifecycleListener().also {
            ProcessLifecycleOwner.get().lifecycle.addObserver(it)
        }
    }

    override fun onInactive() {
        super.onInactive()
        lifecycleListener?.let {
            this.lifecycleListener = null
            ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
        }
    }

    internal inner class AppLifecycleListener : LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onMoveToForeground() {
            value = State.FOREGROUND
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onMoveToBackground() {
            value = State.BACKGROUND
        }
    }

    enum class State {
        FOREGROUND, BACKGROUND
    }
}

ตอนนี้เราสามารถสมัครสมาชิก LiveData นี้และติดตามเหตุการณ์ที่จำเป็น ตัวอย่างเช่น:

appForegroundStateLiveData.observeForever { state ->
    when(state) {
        AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
        AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
    }
}

0

คำตอบเหล่านี้ดูเหมือนจะไม่ถูกต้อง วิธีการเหล่านี้จะถูกเรียกเมื่อกิจกรรมอื่นเริ่มต้นและสิ้นสุด สิ่งที่คุณสามารถทำได้คือรักษาธงทั่วโลก (ใช่, globals ไม่ดี :) และตั้งค่านี้เป็นจริงทุกครั้งที่คุณเริ่มกิจกรรมใหม่ ตั้งค่าเป็นเท็จใน onCreate ของแต่ละกิจกรรม จากนั้นใน onPause คุณตรวจสอบการตั้งค่าสถานะนี้ หากเป็นเท็จแอปของคุณกำลังเข้าสู่พื้นหลังหรือถูกฆ่า


ฉันไม่ได้พูดเกี่ยวกับฐานข้อมูล ... คุณหมายถึงอะไร
Joris Weimar

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