ตามพื้นหลังฉันหมายถึงไม่มีกิจกรรมของแอปพลิเคชันที่ผู้ใช้สามารถมองเห็นได้ในตอนนี้?
ตามพื้นหลังฉันหมายถึงไม่มีกิจกรรมของแอปพลิเคชันที่ผู้ใช้สามารถมองเห็นได้ในตอนนี้?
คำตอบ:
มีสองสามวิธีในการตรวจสอบว่าแอปพลิเคชันของคุณทำงานในพื้นหลังหรือไม่ แต่มีเพียงแอปพลิเคชั่นเดียวที่เชื่อถือได้อย่างสมบูรณ์:
ทางออกที่เหมาะสม (หน่วยกิตไปแดน , CommonsWareและNeTeInStEiN )
ติดตามการแสดงผลของแอพลิเคชันของคุณได้ด้วยตัวเองโดยใช้Activity.onPause
, Activity.onResume
วิธีการ เก็บสถานะ "การมองเห็น" ในคลาสอื่น ตัวเลือกที่ดีคือการนำไปใช้ของคุณเองApplication
หรือ a Service
(นอกจากนี้ยังมีรูปแบบต่าง ๆของโซลูชันนี้หากคุณต้องการตรวจสอบการมองเห็นกิจกรรมจากบริการ)
ตัวอย่าง
ใช้Application
คลาสที่กำหนดเอง(สังเกตisActivityVisible()
วิธีสแตติก)
public class MyApplication extends Application {
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private static boolean activityVisible;
}
ลงทะเบียนคลาสแอปพลิเคชันของคุณในAndroidManifest.xml
:
<application
android:name="your.app.package.MyApplication"
android:icon="@drawable/icon"
android:label="@string/app_name" >
เพิ่มonPause
และเพิ่มonResume
ลงActivity
ในทุกโครงการ (คุณอาจสร้างบรรพบุรุษร่วมกันสำหรับกิจกรรมของคุณหากคุณต้องการ แต่ถ้ากิจกรรมของคุณได้ขยายออกไปจากMapActivity
/ ListActivity
ฯลฯ แล้วคุณยังต้องเขียนสิ่งต่อไปนี้ด้วยตนเอง):
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
อัปเดต
ActivityLifecycleCallbacksถูกเพิ่มใน API ระดับ 14 (Android 4.0) คุณสามารถใช้พวกเขาเพื่อติดตามว่าผู้ใช้สามารถมองเห็นกิจกรรมของแอปพลิเคชันของคุณหรือไม่ ตรวจสอบคำตอบของ Cornstalksด้านล่างเพื่อดูรายละเอียด
ฉันใช้ผิดเพื่อแนะนำวิธีแก้ไขปัญหาต่อไปนี้:
คุณสามารถตรวจสอบแอปพลิเคชันเบื้องหน้า / พื้นหลัง
ActivityManager.getRunningAppProcesses()
ซึ่งส่งกลับรายการของเรกRunningAppProcessInfo
คอร์ด เมื่อต้องการตรวจสอบว่าแอปพลิเคชันของคุณอยู่ในRunningAppProcessInfo.importance
ฟิลด์ตรวจสอบเบื้องหน้าเพื่อดูว่ามีความเท่าเทียมกันRunningAppProcessInfo.IMPORTANCE_FOREGROUND
ในขณะที่RunningAppProcessInfo.processName
เท่ากับชื่อแพคเกจแอปพลิเคชันของคุณนอกจากนี้ถ้าคุณโทร
ActivityManager.getRunningAppProcesses()
จากเธรด UI แอปพลิเคชันของคุณมันจะให้ความสำคัญIMPORTANCE_FOREGROUND
กับงานของคุณไม่ว่าจะอยู่ในเบื้องหน้าหรือไม่ก็ตาม เรียกว่าในเธรดพื้นหลัง (ตัวอย่างเช่นผ่านAsyncTask
) และมันจะกลับผลลัพธ์ที่ถูกต้อง
ในขณะที่วิธีนี้อาจใช้งานได้ (และใช้งานได้จริงในเวลาส่วนใหญ่) ฉันขอแนะนำอย่างยิ่งให้ไม่ใช้งาน และนี่คือเหตุผล ดังที่ Dianne Hackborn เขียนว่า :
API เหล่านี้ไม่ได้มีไว้สำหรับแอปพลิเคชันในการอิงการไหลของ UI แต่จะทำสิ่งต่าง ๆ เช่นแสดงแอพที่กำลังรันหรือตัวจัดการงานหรือผู้ใช้
ใช่มีรายการเก็บไว้ในหน่วยความจำสำหรับสิ่งเหล่านี้ อย่างไรก็ตามจะถูกปิดในกระบวนการอื่นจัดการโดยเธรดที่แยกจากคุณและไม่ใช่สิ่งที่คุณสามารถเชื่อถือได้ (a) การเห็นในเวลาเพื่อทำการตัดสินใจที่ถูกต้องหรือ (b) มีภาพที่สอดคล้องกันในเวลาที่คุณกลับมา บวกกับการตัดสินใจเกี่ยวกับกิจกรรม "ถัดไป" ที่จะไปทำ ณ จุดที่สวิตช์เกิดขึ้นเสมอและจะไม่เกิดขึ้นจนกว่าจะถึงจุดที่แน่นอน (ซึ่งสถานะกิจกรรมถูกล็อคลงเพื่อทำการสวิตช์) รู้จริงแน่นอนว่าสิ่งต่อไปจะเป็นอย่างไร
และการดำเนินการและพฤติกรรมระดับโลกที่นี่ไม่ได้รับประกันว่าจะยังคงเหมือนเดิมในอนาคต
ฉันหวังว่าฉันจะอ่านมันก่อนที่ฉันจะโพสต์คำตอบเกี่ยวกับ SO แต่หวังว่ามันจะไม่สายเกินไปที่จะยอมรับข้อผิดพลาดของฉัน
อีกวิธีหนึ่งที่ไม่ถูกต้องห้องสมุด
Droid-Fu ที่กล่าวถึงในหนึ่งในคำตอบที่ใช้ActivityManager.getRunningTasks
สำหรับisApplicationBroughtToBackground
วิธีการของมัน ดูความคิดเห็นของ Dianne ด้านบนและไม่ใช้วิธีนั้น
OnStop
isActivityVisible
คำตอบ user1269737 เป็นที่เหมาะสม (Google / Android ได้รับการอนุมัติ) วิธีการที่จะทำเช่นนี้ ไปอ่านคำตอบของพวกเขาและให้ +1
ฉันจะทิ้งคำตอบดั้งเดิมไว้ที่นี่เพื่อเห็นแก่ลูกหลาน นี่คือการกลับมาที่ดีที่สุดในปี 2012 แต่ตอนนี้ Android ได้รับการสนับสนุนที่เหมาะสมสำหรับเรื่องนี้
คีย์กำลังใช้งานActivityLifecycleCallbacks
(โปรดทราบว่าต้องใช้ Android API ระดับ 14 (Android 4.0)) เพียงตรวจสอบว่าจำนวนกิจกรรมที่หยุดแล้วเท่ากับจำนวนกิจกรรมที่เริ่ม หากพวกเขาเท่ากันใบสมัครของคุณจะถูก background หากมีกิจกรรมเริ่มต้นมากขึ้นแอปพลิเคชันของคุณจะยังคงปรากฏให้เห็น หากมีการดำเนินการต่อมากกว่ากิจกรรมที่หยุดชั่วคราวแอปพลิเคชันของคุณไม่เพียงมองเห็นได้ แต่ยังอยู่ในเบื้องหน้า มี 3 สถานะหลักที่กิจกรรมของคุณสามารถเข้าได้คือ: มองเห็นได้และในเบื้องหน้ามองเห็นได้ แต่ไม่อยู่ในเบื้องหน้าและไม่สามารถมองเห็นได้และไม่ปรากฏในเบื้องหน้า (เช่นในพื้นหลัง)
สิ่งที่ดีมากเกี่ยวกับวิธีการนี้ก็คือว่ามันไม่ได้มีประเด็นที่ไม่ตรงกันgetRunningTasks()
ไม่ แต่คุณยังไม่ได้มีการปรับเปลี่ยนทุกActivity
ในใบสมัครของคุณไปยังชุด / สิ่งที่ไม่มีการตั้งค่าใน/onResumed()
onPaused()
เป็นโค้ดเพียงไม่กี่บรรทัดที่มีอยู่ในตัวและทำงานได้ตลอดทั้งแอปพลิเคชันของคุณ นอกจากนี้ยังไม่จำเป็นต้องใช้การอนุญาตแบบขี้ขลาด
MyLifecycleHandler.java:
public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
// I use four separate variables here. You can, of course, just use two and
// increment/decrement them instead of using four and incrementing them all.
private int resumed;
private int paused;
private int started;
private int stopped;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
++resumed;
}
@Override
public void onActivityPaused(Activity activity) {
++paused;
android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityStarted(Activity activity) {
++started;
}
@Override
public void onActivityStopped(Activity activity) {
++stopped;
android.util.Log.w("test", "application is visible: " + (started > stopped));
}
// If you want a static function you can use to check if your application is
// foreground/background, you can use the following:
/*
// Replace the four variables above with these four
private static int resumed;
private static int paused;
private static int started;
private static int stopped;
// And these two public static functions
public static boolean isApplicationVisible() {
return started > stopped;
}
public static boolean isApplicationInForeground() {
return resumed > paused;
}
*/
}
MyApplication.java:
// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
@Override
public void onCreate() {
// Simply add the handler, and that's it! No need to add any code
// to every activity. Everything is contained in MyLifecycleHandler
// with just a few lines of code. Now *that's* nice.
registerActivityLifecycleCallbacks(new MyLifecycleHandler());
}
}
@Mewzer ได้ถามคำถามที่ดีเกี่ยวกับวิธีการนี้ที่ฉันต้องการตอบกลับในคำตอบนี้สำหรับทุกคน:
onStop()
ไม่ได้ถูกเรียกในสถานการณ์ที่หน่วยความจำเหลือน้อย นั่นเป็นปัญหาใช่ไหม
ไม่เอกสารสำหรับการonStop()
พูด:
โปรดทราบว่าวิธีนี้อาจไม่เคยถูกเรียกใช้ในสถานการณ์ที่หน่วยความจำเหลือน้อยซึ่งระบบมีหน่วยความจำไม่เพียงพอที่จะทำให้กระบวนการของกิจกรรมของคุณทำงานหลังจากที่มีการเรียกใช้เมธอด onPause ()
กุญแจสำคัญในที่นี้คือ "ทำให้กระบวนการของกิจกรรมของคุณทำงานต่อไป ... " ถ้าถึงสถานการณ์ความทรงจำไม่ถึงขั้นตอนนี้กระบวนการของคุณก็จะถูกทำลาย (ไม่ใช่แค่กิจกรรมของคุณ) ซึ่งหมายความว่าวิธีการตรวจสอบ backgrounded-ness นี้ยังคงใช้ได้เนื่องจากก) คุณไม่สามารถตรวจสอบพื้นหลังได้หากกระบวนการของคุณถูกฆ่าและ b) หากกระบวนการของคุณเริ่มต้นอีกครั้ง (เนื่องจากมีการสร้างกิจกรรมใหม่) สมาชิก ตัวแปร (ไม่ว่าจะคงที่หรือไม่) เพื่อจะถูกรีเซ็ตMyLifecycleHandler
0
สิ่งนี้ใช้ได้กับการเปลี่ยนแปลงการกำหนดค่าหรือไม่
โดยค่าเริ่มต้นไม่มี คุณต้องกำหนดอย่างชัดเจนconfigChanges=orientation|screensize
( |
ด้วยสิ่งอื่นที่คุณต้องการ) ในไฟล์รายการและจัดการการเปลี่ยนแปลงการกำหนดค่ามิฉะนั้นกิจกรรมของคุณจะถูกทำลายและสร้างใหม่ onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume
หากคุณไม่ได้ตั้งค่านี้วิธีการกิจกรรมของคุณจะถูกเรียกในลำดับนี้: อย่างที่คุณเห็นไม่มีการเหลื่อมกัน (โดยปกติกิจกรรมสองอย่างซ้อนทับกันสั้นมากเมื่อสลับระหว่างสองซึ่งเป็นวิธีการตรวจจับการแบ็คกราวน์พื้นหลังนี้) เพื่อให้ได้สิ่งนี้คุณต้องตั้งค่าconfigChanges
เพื่อไม่ให้กิจกรรมของคุณถูกทำลาย โชคดีที่ฉันต้องตั้งค่าconfigChanges
มีอยู่แล้วในโครงการทั้งหมดของฉันเพราะมันไม่เป็นที่พึงปรารถนาสำหรับกิจกรรมทั้งหมดของฉันที่จะถูกทำลายบนหน้าจอหมุน / ปรับขนาดดังนั้นฉันไม่เคยพบว่าสิ่งนี้เป็นปัญหา (ขอบคุณ dpimka สำหรับการรีเฟรชความทรงจำของฉันและแก้ไขฉัน!)
หมายเหตุหนึ่ง:
เมื่อฉันพูดว่า "พื้นหลัง" ที่นี่ในคำตอบนี้ฉันหมายถึง "แอพของคุณจะไม่ปรากฏอีกต่อไป" กิจกรรม Android สามารถมองเห็นได้ แต่ไม่อยู่ในเบื้องหน้า (ตัวอย่างเช่นหากมีการซ้อนทับการแจ้งเตือนที่โปร่งใส) นั่นเป็นเหตุผลที่ฉันได้อัปเดตคำตอบนี้เพื่อสะท้อนว่า
มันเป็นสิ่งสำคัญที่จะรู้ว่า Android มีช่วงเวลาที่ถูกทอดทิ้งแปลกเมื่อมีการเปลี่ยนกิจกรรมที่ไม่มีอะไรในเบื้องหน้า ด้วยเหตุผลนี้หากคุณตรวจสอบว่าแอปพลิเคชันของคุณอยู่ในเบื้องหน้าเมื่อสลับระหว่างกิจกรรม (ในแอปเดียวกัน) คุณจะได้รับแจ้งว่าคุณไม่ได้อยู่ในเบื้องหน้า (แม้ว่าแอปของคุณจะยังเป็นแอปที่ใช้งานอยู่ )
คุณสามารถตรวจสอบถ้า app ของคุณอยู่ในเบื้องหน้าในของคุณActivity
's onPause()
วิธีการหลังจากที่ super.onPause()
แค่จำรัฐที่ไม่ถูกต้องที่ฉันเพิ่งพูดถึง
คุณสามารถตรวจสอบว่าแอปของคุณจะมองเห็นได้ (คือถ้ามันไม่ได้อยู่ในพื้นหลัง) ในของคุณActivity
's onStop()
วิธีการหลังจากที่ super.onStop()
onStop()
super.onStop()
ไม่ต้องตรวจสอบสำหรับ backgrounding onPause()
ใน
GOOGLE SOLUTION - ไม่ใช่แฮ็คเหมือนโซลูชันก่อนหน้า ใช้ProcessLifecycleOwner
Kotlin:
class ArchLifecycleApp : Application(), LifecycleObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
//App in background
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForegrounded() {
// App in foreground
}
}
Java:
public class ArchLifecycleApp extends Application implements LifecycleObserver {
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onAppBackgrounded() {
//App in background
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppForegrounded() {
// App in foreground
}
}
ใน app.gradle
dependencies {
...
implementation "android.arch.lifecycle:extensions:1.1.0"
//New Android X dependency is this -
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
}
allprojects {
repositories {
...
google()
jcenter()
maven { url 'https://maven.google.com' }
}
}
คุณสามารถอ่านเพิ่มเติมเกี่ยวกับส่วนประกอบของวงจรชีวิตที่เกี่ยวข้องได้ที่นี่ - https://developer.android.com/topic/lไลบรารี/architecture /lifecycle
companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }
จากนั้นคุณจะได้สถานะพื้นหน้าด้วยArchLifecycleApp.isForeground()
The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes.
สิ่งนี้ไม่ทำงานกับmultiple processes
แอพมี api บางอย่างที่เราสามารถทำได้อย่างงดงามหรือไม่?
เริ่มต้นไลบรารีการสนับสนุนเวอร์ชัน 26 คุณสามารถใช้ProcessLifecycleOwnerเพียงเพิ่มไปยังการพึ่งพาของคุณเช่นที่อธิบายไว้ที่นี่ตัวอย่างเช่น:
dependencies {
def lifecycle_version = "1.1.1"
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// alternatively - Lifecycles only (no ViewModel or LiveData).
// Support library depends on this lightweight import
implementation "android.arch.lifecycle:runtime:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}
และจากนั้นก็สอบถามProcessLifecycleOwner
เมื่อใดก็ตามที่คุณต้องการสำหรับสถานะแอปตัวอย่าง:
//Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;
//Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
ตั้งแต่ Android API 16 มีวิธีง่าย ๆ ในการตรวจสอบว่าแอปอยู่เบื้องหน้าหรือไม่ มันอาจจะไม่สามารถจะเข้าใจผิดได้ แต่ไม่มีวิธีการใน Android จะเข้าใจผิดได้ วิธีนี้ดีพอที่จะใช้เมื่อบริการของคุณได้รับการอัปเดตจากเซิร์ฟเวอร์และต้องตัดสินใจว่าจะแสดงการแจ้งเตือนหรือไม่ (เพราะถ้า UI อยู่เบื้องหน้าผู้ใช้จะสังเกตเห็นการอัปเดตโดยไม่มีการแจ้งเตือน)
RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
JobService
ตรวจสอบว่าบริการกำลังทำงานในพื้นหลัง
คำตอบของไอดอลนั้นเกิดข้อผิดพลาดและมีความซับซ้อนมากขึ้นแม้ว่าจะยกเลิกการตรวจสอบแอปพลิเคชัน Android ในเบื้องหน้าหรือไม่? และที่นี่การกำหนดแอปพลิเคชันเบื้องหน้าปัจจุบันจากงานหรือบริการเบื้องหลัง
มีวิธีที่ง่ายกว่านี้มากขึ้น:
บนBaseActivity ที่กิจกรรมทั้งหมดขยาย:
protected static boolean isVisible = false;
@Override
public void onResume()
{
super.onResume();
setVisible(true);
}
@Override
public void onPause()
{
super.onPause();
setVisible(false);
}
เมื่อใดก็ตามที่คุณต้องตรวจสอบถ้ามีกิจกรรมแอพลิเคชันของคุณอยู่ในเบื้องหน้าเพียงแค่ตรวจสอบ isVisible()
;
เพื่อทำความเข้าใจวิธีการนี้ให้ตรวจสอบคำตอบของวงจรชีวิตของกิจกรรมแบบเคียงข้างกัน: วงจรชีวิตของกิจกรรมแบบเคียงข้างกัน
Idolon's answer is error prone
- น่าเสียดายที่ฉันต้องเห็นด้วยกับคุณ จากความคิดเห็นของ Dianne Hackborn ใน Google Groups ฉันได้อัปเดตคำตอบของฉันแล้ว โปรดตรวจสอบรายละเอียด
onPause
, onStop
, หรือonResume
เหตุการณ์ที่เรียกว่า แล้วคุณจะทำอย่างไรถ้าไม่มีเหตุการณ์เหล่านี้ถูกไล่ออก?!
ฉันลองใช้วิธีแก้ปัญหาที่แนะนำซึ่งใช้Application.ActivityLifecycleCallbacksและอื่น ๆ อีกมากมาย แต่ไม่ได้ผลตามที่คาดไว้ ขอบคุณSargeฉันจึงได้วิธีแก้ปัญหาที่ง่ายและตรงไปตรงมาซึ่งฉันอธิบายไว้ด้านล่าง
พวกเขาที่สำคัญของการแก้ปัญหาคือความเป็นจริงของการทำความเข้าใจว่าถ้าเรามี activitya และ ActivityB และเราเรียก ActivityB จาก activitya (และไม่โทร
ActivityA.finish
) แล้ว ActivityB ของonStart()
จะถูกเรียกว่าก่อนที่จะonStop()
activitya
นั่นเป็นความแตกต่างที่สำคัญระหว่างonStop()
และonPause()
ไม่มีใครพูดถึงในบทความที่ฉันอ่าน
ดังนั้นตามพฤติกรรมวงจรชีวิตของกิจกรรมนี้คุณสามารถนับจำนวนครั้งonStart()
และonPause()
เรียกใช้ในโปรแกรมของคุณได้หลายครั้ง โปรดทราบว่าสำหรับแต่ละ Activity
โปรแกรมของคุณคุณต้องแทนที่onStart()
และonStop()
เพื่อเพิ่ม / ลดตัวแปรสแตติกที่ใช้สำหรับการนับ ด้านล่างเป็นรหัสที่ใช้ตรรกะนี้ โปรดทราบว่าฉันกำลังใช้คลาสที่ขยายApplication
ดังนั้นอย่าลืมประกาศManifest.xml
ภายใน Application tag: android:name=".Utilities"
ถึงแม้ว่ามันจะสามารถนำไปใช้งานได้โดยใช้คลาสที่กำหนดเองง่าย ๆ เช่นกัน
public class Utilities extends Application
{
private static int stateCounter;
public void onCreate()
{
super.onCreate();
stateCounter = 0;
}
/**
* @return true if application is on background
* */
public static boolean isApplicationOnBackground()
{
return stateCounter == 0;
}
//to be called on each Activity onStart()
public static void activityStarted()
{
stateCounter++;
}
//to be called on each Activity onStop()
public static void activityStopped()
{
stateCounter--;
}
}
ตอนนี้ในแต่ละกิจกรรมของโปรแกรมของเราเราควรแทนที่onStart()
และonStop()
และเพิ่ม / ลดลงตามที่แสดงด้านล่าง:
@Override
public void onStart()
{
super.onStart();
Utilities.activityStarted();
}
@Override
public void onStop()
{
Utilities.activityStopped();
if(Utilities.isApplicationOnBackground())
{
//you should want to check here if your application is on background
}
super.onStop();
}
ด้วยตรรกะนี้มี 2 กรณีที่เป็นไปได้:
stateCounter = 0
: จำนวนการหยุดเท่ากับจำนวนกิจกรรมที่เริ่มต้นซึ่งหมายความว่าแอปพลิเคชันทำงานบนพื้นหลังstateCounter > 0
: จำนวนการเริ่มต้นมีขนาดใหญ่กว่าจำนวนการหยุดซึ่งหมายความว่าแอปพลิเคชันทำงานบนพื้นหน้าประกาศ: stateCounter < 0
จะหมายถึงว่ามีกิจกรรมที่หยุดมากกว่าแทนที่จะเริ่มซึ่งเป็นไปไม่ได้ หากคุณพบกรณีนี้หมายความว่าคุณไม่ได้เพิ่ม / ลดจำนวนตัวนับเท่าที่ควร
คุณพร้อมที่จะไป คุณควรตรวจสอบว่าใบสมัครของคุณอยู่ในพื้นหลังด้านในonStop()
หรือไม่
if(Utilities.isApplicationOnBackground()) …
Utilities
เพราะมิฉะนั้นเฉพาะกิจกรรมที่เฉพาะเจาะจงจะตอบสนองต่อเหตุการณ์
ไม่มีทางที่คุณติดตามด้วยตัวคุณเองเพื่อกำหนดว่ากิจกรรมใด ๆ ของคุณสามารถมองเห็นได้หรือไม่ บางทีคุณควรพิจารณาถามคำถาม StackOverflow ใหม่โดยอธิบายว่าคุณพยายามทำอะไรจากประสบการณ์ของผู้ใช้ดังนั้นเราอาจให้แนวคิดการใช้งานทางเลือกแก่คุณ
Service
จะถูกดำเนินการโดย ถ้าเป็นเช่นนั้นให้กิจกรรมของคุณแจ้งบริการตามที่ปรากฏและหายไป หากService
กำหนดว่าไม่มีกิจกรรมที่มองเห็นได้และจะยังคงมีวิธีนั้นอยู่เป็นระยะเวลาหนึ่งให้หยุดการถ่ายโอนข้อมูลที่จุดหยุดตรรกะถัดไป ใช่นี่จะต้องใช้รหัสสำหรับแต่ละกิจกรรมของคุณ แต่ตอนนี้นั่นเป็น AFAIK ที่หลีกเลี่ยงไม่ได้
MyActivityClass
สืบทอดจากและการใช้วิธีวงจรชีวิตและทำให้กิจกรรมทั้งหมดของคุณได้รับมรดกจากActivity
MyActivityClass
นี้จะไม่ทำงานPreferenceActivity
หรือMapActivity
แม้ว่า (ดูคำถามนี้ )
คุณสามารถใช้ComponentCallbacks2เพื่อตรวจสอบว่าแอปอยู่ในพื้นหลังหรือไม่ BTW การติดต่อกลับนี้มีเฉพาะใน API ระดับ 14 (Ice Cream Sandwich) และสูงกว่า
คุณจะได้รับวิธีการ:
public abstract void onTrimMemory (int level)
หากระดับComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
นั้นเป็นแอปที่อยู่ในพื้นหลัง
คุณสามารถนำอินเตอร์เฟสนี้ไปใช้กับactivity
, service
และอื่น ๆ ได้
public class MainActivity extends AppCompatActivity 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) {
// app is in background
}
}
}
การสร้างคำตอบบน @Cornstalks เพื่อรวมคุณสมบัติที่มีประโยชน์สองอย่าง
คุณสมบัติพิเศษ:
App.java
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
}
}
AppLifecycleHandler.java
public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
private int resumed;
private int started;
private final String DebugName = "AppLifecycleHandler";
private boolean isVisible = false;
private boolean isInForeground = false;
private static AppLifecycleHandler instance;
public static AppLifecycleHandler getInstance() {
if (instance == null) {
instance = new AppLifecycleHandler();
}
return instance;
}
private AppLifecycleHandler() {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
++resumed;
android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
@Override
public void onActivityPaused(Activity activity) {
--resumed;
android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityStarted(Activity activity) {
++started;
android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
@Override
public void onActivityStopped(Activity activity) {
--started;
android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
private void setVisible(boolean visible) {
if (isVisible == visible) {
// no change
return;
}
// visibility changed
isVisible = visible;
android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);
// take some action on change of visibility
}
private void setForeground(boolean inForeground) {
if (isInForeground == inForeground) {
// no change
return;
}
// in foreground changed
isInForeground = inForeground;
android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);
// take some action on change of in foreground
}
public static boolean isApplicationVisible() {
return AppLifecycleHandler.getInstance().started > 0;
}
public static boolean isApplicationInForeground() {
return AppLifecycleHandler.getInstance().resumed > 0;
}
}
ทางออกที่ดีที่สุดที่ฉันใช้กับตัวจับเวลา
คุณเริ่มจับเวลาใน onPause () และยกเลิกตัวจับเวลาเดียวกันใน onResume () มี 1 อินสแตนซ์ของตัวจับเวลา (โดยปกติจะกำหนดไว้ในคลาสแอปพลิเคชัน) ตัวจับเวลาถูกตั้งค่าให้เรียกใช้ Runnable หลังจาก 2 วินาที (หรือช่วงเวลาใดที่คุณคิดว่าเหมาะสม) เมื่อตัวจับเวลาเริ่มทำงานคุณตั้งค่าสถานะที่ทำเครื่องหมายแอปพลิเคชันว่าเป็นพื้นหลัง
ในเมธอด onResume () ก่อนที่คุณจะยกเลิกตัวจับเวลาคุณสามารถสอบถามการตั้งค่าพื้นหลังเพื่อดำเนินการเริ่มต้นใด ๆ (เช่นเริ่มการดาวน์โหลดหรือเปิดใช้งานบริการตำแหน่ง)
โซลูชันนี้ช่วยให้คุณมีกิจกรรมหลายอย่างใน back stack และไม่ต้องการการอนุญาตใด ๆ
โซลูชันนี้ทำงานได้ดีหากคุณใช้ Event Bus เช่นกันเนื่องจากตัวจับเวลาของคุณสามารถไล่เหตุการณ์และส่วนต่าง ๆ ของแอพของคุณสามารถตอบสนองได้
หากคุณเปิดใช้งานการตั้งค่าของนักพัฒนา "อย่าเก็บการกระทำ" - ตรวจสอบเฉพาะกิจกรรมที่สร้างไว้ไม่เพียงพอ คุณต้องตรวจสอบisSaveInstanceStateด้วย วิธีการที่กำหนดเองของฉันคือisApplicationRunning () การตรวจสอบคือแอพ android กำลังทำงาน:
นี่คือรหัสการทำงานของฉัน:
public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
private int created;
private boolean isSaveInstanceState;
private static AppLifecycleService instance;
private final static String TAG = AppLifecycleService.class.getName();
public static AppLifecycleService getInstance() {
if (instance == null) {
instance = new AppLifecycleService();
}
return instance;
}
public static boolean isApplicationRunning() {
boolean isApplicationRunning = true;
if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
isApplicationRunning = false;
}
return isApplicationRunning;
}
public static boolean isSaveInstanceState() {
return AppLifecycleService.getInstance().isSaveInstanceState;
}
public static int getCountCreatedActvities() {
return AppLifecycleService.getInstance().created;
}
private AppLifecycleService() {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
this.isSaveInstanceState = true;
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
++created;
}
@Override
public void onActivityDestroyed(Activity activity) {
--created;
}
@Override
public void onActivityResumed(Activity activity) { }
@Override
public void onActivityPaused(Activity activity) { }
@Override
public void onActivityStarted(Activity activity) { }
@Override
public void onActivityStopped(Activity activity) { }
}
ทางออกที่ถูกต้องเพียงข้อเดียว:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.mainActivity = this;
super.onCreate(savedInstanceState);
...
}
public class MyApp extends Application implements LifecycleObserver {
public static MainActivity mainActivity = null;
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onAppBackgrounded() {
// app in background
if (mainActivity != null) {
...
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void onAppForegrounded() {
// app in foreground
if (mainActivity != null) {
...
}
}
}
หากต้องการ piggyback เกี่ยวกับสิ่งที่ CommonsWare และ Key ได้กล่าวไว้คุณอาจขยายคลาสแอพพลิเคชั่นและให้กิจกรรมทั้งหมดของคุณเรียกว่าในเมธอด onPause / onResume สิ่งนี้จะช่วยให้คุณทราบว่ากิจกรรมใด (IES) ที่มองเห็นได้ แต่สิ่งนี้อาจจัดการได้ดีกว่า
คุณอธิบายรายละเอียดเกี่ยวกับสิ่งที่คุณมีในใจได้ไหม? เมื่อคุณพูดว่าทำงานในพื้นหลังคุณหมายถึงเพียงแค่ให้แอปพลิเคชันของคุณยังคงอยู่ในหน่วยความจำแม้ว่าจะไม่ได้อยู่บนหน้าจอในปัจจุบัน? คุณเคยใช้บริการเป็นวิธีที่ถาวรกว่าในการจัดการแอพของคุณเมื่อไม่ได้โฟกัสหรือไม่?
Application
ไม่ได้หรือonPause()
onResume()
ฉันใช้งาน ActivityLifecycleCallbacks ของฉันเอง ฉันกำลังใช้ SherlockActivity แต่สำหรับคลาส Activity ปกติอาจใช้ได้
ก่อนอื่นฉันจะสร้างส่วนต่อประสานที่มีวิธีการทั้งหมดเพื่อติดตามวงจรชีวิตของกิจกรรม:
public interface ActivityLifecycleCallbacks{
public void onActivityStopped(Activity activity);
public void onActivityStarted(Activity activity);
public void onActivitySaveInstanceState(Activity activity, Bundle outState);
public void onActivityResumed(Activity activity);
public void onActivityPaused(Activity activity);
public void onActivityDestroyed(Activity activity);
public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}
ประการที่สองฉันใช้อินเทอร์เฟซนี้ในคลาสของแอปพลิเคชันของฉัน:
public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onActivityStopped(Activity activity) {
Log.i("Tracking Activity Stopped", activity.getLocalClassName());
}
@Override
public void onActivityStarted(Activity activity) {
Log.i("Tracking Activity Started", activity.getLocalClassName());
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
}
@Override
public void onActivityResumed(Activity activity) {
Log.i("Tracking Activity Resumed", activity.getLocalClassName());
}
@Override
public void onActivityPaused(Activity activity) {
Log.i("Tracking Activity Paused", activity.getLocalClassName());
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.i("Tracking Activity Created", activity.getLocalClassName());
}
}
ประการที่สามฉันสร้างชั้นเรียนที่ยื่นออกมาจาก SherlockActivity:
public class MySherlockActivity extends SherlockActivity {
protected MyApplication nMyApplication;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
nMyApplication = (MyApplication) getApplication();
nMyApplication.onActivityCreated(this, savedInstanceState);
}
protected void onResume() {
// TODO Auto-generated method stub
nMyApplication.onActivityResumed(this);
super.onResume();
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
nMyApplication.onActivityPaused(this);
super.onPause();
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
nMyApplication.onActivityDestroyed(this);
super.onDestroy();
}
@Override
protected void onStart() {
nMyApplication.onActivityStarted(this);
super.onStart();
}
@Override
protected void onStop() {
nMyApplication.onActivityStopped(this);
super.onStop();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
nMyApplication.onActivitySaveInstanceState(this, outState);
super.onSaveInstanceState(outState);
}
}
ประการที่สี่คลาสทั้งหมดที่ขยายจาก SherlockActivity ฉันแทนที่ MySherlockActivity:
public class MainActivity extends MySherlockActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
ตอนนี้ใน logcat คุณจะเห็นบันทึกที่ตั้งโปรแกรมไว้ในส่วนต่อประสานที่เกิดขึ้นใน MyApplication
กิจกรรมจะหยุดชั่วคราวเมื่อไดอะล็อกมาเหนือดังนั้นโซลูชันที่แนะนำทั้งหมดจึงเป็นครึ่งโซลูชั่น คุณต้องสร้าง hooks สำหรับกล่องโต้ตอบด้วย
เนื่องจากยังไม่ได้กล่าวถึงฉันจะแนะนำให้ผู้อ่านสำรวจProcessLifecycleOwnerพร้อมใช้งานผ่านองค์ประกอบของสถาปัตยกรรม Android
ระบบแยกความแตกต่างระหว่างแอปพื้นหน้าและพื้นหลัง (คำจำกัดความของพื้นหลังเพื่อจุดประสงค์ในการ จำกัด บริการแตกต่างจากคำจำกัดความที่ใช้โดยการจัดการหน่วยความจำแอปอาจอยู่ในพื้นหลังซึ่งเกี่ยวข้องกับการจัดการหน่วยความจำแต่ในส่วนหน้า ถือว่าอยู่เบื้องหน้าหากสิ่งใดสิ่งหนึ่งต่อไปนี้เป็นจริง:
หากไม่มีเงื่อนไขเหล่านี้เป็นจริงแอปนั้นจะถือว่าอยู่ในพื้นหลัง
วิธีแก้ปัญหาอื่นสำหรับโพสต์เก่านี้ (สำหรับผู้ที่อาจช่วยได้):
<application android:name=".BaseApplication" ... >
public class BaseApplication extends Application {
private class Status {
public boolean isVisible = true;
public boolean isFocused = true;
}
private Map<Activity, Status> activities;
@Override
public void onCreate() {
activities = new HashMap<Activity, Status>();
super.onCreate();
}
private boolean hasVisibleActivity() {
for (Status status : activities.values())
if (status.isVisible)
return true;
return false;
}
private boolean hasFocusedActivity() {
for (Status status : activities.values())
if (status.isFocused)
return true;
return false;
}
public void onActivityCreate(Activity activity, boolean isStarting) {
if (isStarting && activities.isEmpty())
onApplicationStart();
activities.put(activity, new Status());
}
public void onActivityStart(Activity activity) {
if (!hasVisibleActivity() && !hasFocusedActivity())
onApplicationForeground();
activities.get(activity).isVisible = true;
}
public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
activities.get(activity).isFocused = hasFocus;
}
public void onActivityStop(Activity activity, boolean isFinishing) {
activities.get(activity).isVisible = false;
if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
onApplicationBackground();
}
public void onActivityDestroy(Activity activity, boolean isFinishing) {
activities.remove(activity);
if(isFinishing && activities.isEmpty())
onApplicationStop();
}
private void onApplicationStart() {Log.i(null, "Start");}
private void onApplicationBackground() {Log.i(null, "Background");}
private void onApplicationForeground() {Log.i(null, "Foreground");}
private void onApplicationStop() {Log.i(null, "Stop");}
}
public class MyActivity extends BaseActivity {...}
public class BaseActivity extends Activity {
private BaseApplication application;
@Override
protected void onCreate(Bundle state) {
application = (BaseApplication) getApplication();
application.onActivityCreate(this, state == null);
super.onCreate(state);
}
@Override
protected void onStart() {
application.onActivityStart(this);
super.onStart();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
application.onActivityWindowFocusChanged(this, hasFocus);
super.onWindowFocusChanged(hasFocus);
}
@Override
protected void onStop() {
application.onActivityStop(this, isFinishing());
super.onStop();
}
@Override
protected void onDestroy() {
application.onActivityDestroy(this, isFinishing());
super.onDestroy();
}
}
ดูความคิดเห็นในฟังก์ชั่น onActivityDestroyed
ทำงานกับ SDK เป้าหมายรุ่น 14>:
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;
public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
public static int active = 0;
@Override
public void onActivityStopped(Activity activity) {
Log.i("Tracking Activity Stopped", activity.getLocalClassName());
active--;
}
@Override
public void onActivityStarted(Activity activity) {
Log.i("Tracking Activity Started", activity.getLocalClassName());
active++;
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
}
@Override
public void onActivityResumed(Activity activity) {
Log.i("Tracking Activity Resumed", activity.getLocalClassName());
active++;
}
@Override
public void onActivityPaused(Activity activity) {
Log.i("Tracking Activity Paused", activity.getLocalClassName());
active--;
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
active--;
// if active var here ever becomes zero, the app is closed or in background
if(active == 0){
...
}
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.i("Tracking Activity Created", activity.getLocalClassName());
active++;
}
}
คุณควรใช้การตั้งค่าที่ใช้ร่วมกันเพื่อจัดเก็บทรัพย์สินและดำเนินการโดยใช้บริการที่มีผลผูกพันจากกิจกรรมของคุณ หากคุณใช้การผูกเท่านั้น (นั่นคือไม่เคยใช้ startService) บริการของคุณจะทำงานก็ต่อเมื่อคุณผูกไว้ (ผูก onResume และ unbind onPause) ที่จะทำให้มันทำงานในเบื้องหน้าเท่านั้นและหากคุณต้องการทำงานต่อ พื้นหลังคุณสามารถใช้บริการเริ่มหยุดปกติ
ฉันคิดว่าคำถามนี้ควรชัดเจนกว่านี้ เมื่อไหร่? ที่ไหน? สถานการณ์เฉพาะของคุณคืออะไรที่คุณต้องการรู้ไหมหากแอปของคุณอยู่ในพื้นหลัง
ฉันเพิ่งแนะนำวิธีแก้ปัญหาของฉันในแบบของฉัน
ฉันทำสิ่งนี้โดยใช้ฟิลด์ "สำคัญ" ของRunningAppProcessInfo
คลาสในทุกกิจกรรมของonStop
แอปของฉันซึ่งสามารถทำได้โดยการBaseActivity
จัดกิจกรรมอื่น ๆ เพื่อขยายซึ่งใช้onStop
วิธีการตรวจสอบคุณค่าของ "ความสำคัญ" นี่คือรหัส:
public static boolean isAppRunning(Context context) {
ActivityManager activityManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appProcesses = activityManager
.getRunningAppProcesses();
for (RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.processName.equals(context.getPackageName())) {
if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
return true;
}
}
}
return false;
}
ฉันขอแนะนำให้อ่านหน้านี้: http://developer.android.com/reference/android/app/Activity.html
กล่าวโดยย่อกิจกรรมของคุณจะไม่ปรากฏอีกต่อไปหลังจากonStop()
ถูกเรียก
onStop
; ระหว่างonPause
และonStop
ก็มองเห็นแต่ไม่ได้อยู่เบื้องหน้า
onStop()
ถูกเรียกอีกต่อไปซึ่งสอดคล้องกับสิ่งที่คุณเขียน
onPause
เรียกว่า: การแก้ไขล่าสุดได้แก้ไขคุณ
สิ่งที่เกี่ยวกับการใช้ getApplicationState (). isInForeground ()?
ในความคิดของฉันคำตอบมากมายแนะนำให้โหลดโค้ดจำนวนมากและทำให้เกิดความซับซ้อนและอ่านไม่ออก
เมื่อมีคนถามในดังนั้นวิธีการสื่อสารระหว่างService
และActivity
ผมมักจะให้คำแนะนำการใช้LocalBroadcastManager
ทำไม?
ก็ด้วยการอ้างถึงเอกสาร:
คุณรู้ว่าข้อมูลที่คุณกำลังออกอากาศจะไม่ปล่อยให้แอปของคุณดังนั้นไม่จำเป็นต้องกังวลเกี่ยวกับการรั่วไหลของข้อมูลส่วนตัว
แอปพลิเคชันอื่นไม่สามารถส่งการออกอากาศเหล่านี้ไปยังแอปของคุณได้ดังนั้นคุณไม่จำเป็นต้องกังวลเกี่ยวกับช่องโหว่ด้านความปลอดภัยที่สามารถใช้ประโยชน์ได้
มันมีประสิทธิภาพมากกว่าการส่งการออกอากาศทั่วโลกผ่านระบบ
ไม่อยู่ในเอกสาร:
Activity
, Application
...ลักษณะ
ดังนั้นคุณต้องการตรวจสอบว่ามีสิ่งใดActivity
อยู่ในเบื้องหน้าหรือไม่ คุณมักจะทำเช่นนั้นในService
หรือApplication
ชั้นเรียน
ซึ่งหมายความว่าActivity
วัตถุของคุณกลายเป็นผู้ส่งสัญญาณ (ฉันเปิด / ปิด) ของคุณService
ในมืออื่น ๆ ที่จะกลายเป็นReceiver
ที่จะกลายเป็น
มีสองช่วงเวลาที่คุณอยู่Activity
จะบอกคุณว่ามันจะไปในเบื้องหน้าหรือในพื้นหลัง (ใช่เพียงสอง ... ไม่ใช่ 6)
เมื่อสิ่งนั้นActivity
เข้าสู่เบื้องหน้าonResume()
เมธอดจะถูกทริกเกอร์ (เรียกอีกอย่างว่าหลังจากonCreate()
)
เมื่อActivity
ไปในด้านหลังonPause()
เรียกว่า
นี่คือช่วงเวลาที่คุณActivity
ควรส่งสัญญาณไปยังคุณService
เพื่ออธิบายสถานะของมัน
ในกรณีที่มีหลายค่าActivity
ให้จดจำเครื่องหมายActivity
ลงในพื้นหลังก่อนจากนั้นอีกอันหนึ่งจะเข้าสู่เบื้องหน้า
ดังนั้นสถานการณ์จะเป็น: *
Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON
Service
/ Application
ก็จะให้ฟังสำหรับสัญญาณเหล่านั้นและดำเนินการตาม
รหัส (TLDR)
คุณService
ต้องดำเนินการBroadcastReceiver
เพื่อฟังสัญญาณ
this.localBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// received data if Activity is on / off
}
}
public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL")
ลงทะเบียนReceiver
ในService::onCreate()
@Override
protected void onCreate() {
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}
ยกเลิกการลงทะเบียน Service::onDestroy()
@Override
protected void onDestroy() {
// I'm dead, no need to listen to anything anymore.
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}
ตอนนี้คุณActivity
ต้องสื่อสารสถานะของพวกเขา
ใน Activity::onResume()
Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
ใน Activity::onPause()
Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
สถานการณ์ที่ธรรมดามาก
ผู้พัฒนา: ฉันต้องการที่จะส่งข้อมูลจากฉันและปรับปรุง
Service
Activity
ฉันจะตรวจสอบว่าActivity
อยู่ในเบื้องหน้าได้อย่างไร
มักจะไม่จำเป็นต้องตรวจสอบว่าActivity
อยู่ในเบื้องหน้าหรือไม่ เพียงแค่ส่งข้อมูลผ่านทางจากคุณLocalBroadcastManager
Service
หากเปิดActivity
อยู่ระบบจะตอบกลับและดำเนินการ
สำหรับสถานการณ์ทั่วไปมากนี้Service
จะกลายเป็นผู้ส่งและนำไปปฏิบัติActivity
BroadcastReceiver
ดังนั้นสร้างในของคุณReceiver
Activity
ลงทะเบียนในและยกเลิกการลงทะเบียนในonResume()
ไม่จำเป็นต้องใช้วิธีวงจรชีวิตอื่น ๆonPause()
ไม่มีความจำเป็นต้องใช้วิธีการวงจรชีวิตอื่น
กำหนดReceiver
พฤติกรรมในonReceive()
(อัปเดต ListView ทำสิ่งนี้ทำเช่นนั้น ... )
วิธีนี้Activity
จะฟังก็ต่อเมื่ออยู่ในเบื้องหน้าและจะไม่มีอะไรเกิดขึ้นหากอยู่ด้านหลังหรือถูกทำลาย
ในกรณีที่มีหลายตัวเลือกActivity
ใดActivity
จะเปิดขึ้นจะตอบสนอง (ถ้าพวกเขายังใช้Receiver
)
หากทุกคนอยู่ในพื้นหลังไม่มีใครตอบสนองและสัญญาณก็จะหายไป
ส่งข้อมูลจากService
ทางIntent
(ดูรหัสด้านบน) โดยระบุรหัสสัญญาณ
fun isAppInForeground(): Boolean {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager ?: return false
val appProcesses = activityManager.runningAppProcesses ?: return false
val packageName = packageName
for (appProcess in appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
return true
}
}
return false
}
ไม่มีคำตอบใดที่เหมาะสำหรับกรณีเฉพาะหากคุณทราบว่ากิจกรรม specfic อยู่ใน forground และหากคุณเป็น SDK ที่ไม่สามารถเข้าถึงแอปพลิเคชันได้โดยตรง สำหรับฉันฉันอยู่ในเธรดพื้นหลังที่เพิ่งได้รับการแจ้งเตือนแบบพุชสำหรับข้อความแชทใหม่และต้องการแสดงการแจ้งเตือนของระบบหากหน้าจอแชทไม่ได้อยู่ในเบื้องหน้า
การใช้สิ่งActivityLifecycleCallbacks
ที่ได้รับการแนะนำในคำตอบอื่น ๆ ฉันได้สร้างคลาส util ขนาดเล็กที่ใช้ตรรกะว่าMyActivity
อยู่ในพื้นหน้าหรือไม่
class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {
private var isMyActivityInForeground = false
init {
(context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}
fun isMyActivityForeground() = isMyActivityInForeground
override fun onActivityPaused(activity: Activity?) {
if (activity is MyActivity) {
isMyActivityInForeground = false
}
}
override fun onActivityResumed(activity: Activity?) {
if (activity is MyActivity) {
isMyActivityInForeground = true
}
}
}
ในกิจกรรมของฉันบนรีโมตและ onPause ฉันเขียนบูลีน isVisible ให้กับ SharedPrefences
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
Editor editor = sharedPrefs.edit();
editor.putBoolean("visible", false);
editor.commit();
และอ่านที่อื่นเมื่อจำเป็นผ่าน
// Show a Toast Notification if App is not visible (ie in background. Not running, etc)
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
if(!sharedPrefs.getBoolean("visible", true)){...}
อาจจะไม่หรูหรา แต่ก็ใช้งานได้สำหรับฉัน ...
มันอาจจะสายเกินไปที่จะตอบ แต่ถ้ามีใครบางคนมาเยี่ยมชมแล้วนี่คือคำตอบที่ฉันแนะนำเหตุผลที่แอปต้องการทราบว่าสถานะอยู่ในพื้นหลังหรือมาเบื้องหน้าอาจมีหลายคน เพื่อแสดงขนมปังปิ้งและการแจ้งเตือนเมื่อผู้ใช้อยู่ใน BG 2. เพื่อทำงานบางอย่างสำหรับผู้ใช้ครั้งแรกที่มาจาก BG เช่นแบบสำรวจความคิดเห็นวาดใหม่ ฯลฯ
การแก้ปัญหาโดย Idolon และอื่น ๆ จะดูแลส่วนแรก แต่ไม่ได้สำหรับส่วนที่สอง หากมีหลายกิจกรรมในแอปของคุณและผู้ใช้กำลังสลับระหว่างกิจกรรมเหล่านั้นตามเวลาที่คุณอยู่ในกิจกรรมที่สองการตั้งค่าสถานะที่มองเห็นได้จะเป็นเท็จ ดังนั้นจึงไม่สามารถใช้งานได้อย่างแน่นอน
ฉันทำสิ่งที่ CommonsWare แนะนำ "ถ้าบริการกำหนดว่าไม่มีกิจกรรมที่มองเห็นได้และยังคงเป็นเช่นนั้นตามระยะเวลาให้หยุดการถ่ายโอนข้อมูลที่จุดหยุดตรรกะถัดไป"
บรรทัดตัวหนามีความสำคัญและสามารถใช้เพื่อให้ได้ไอเท็มที่สอง ดังนั้นสิ่งที่ฉันทำคือเมื่อฉันได้รับ onActivityPaused () อย่าเปลี่ยนการมองเห็นเป็นเท็จโดยตรงแทนที่จะมีการจับเวลา 3 วินาที (นั่นคือสูงสุดที่กิจกรรมถัดไปควรจะเปิดตัว) และถ้าไม่มี onActivityResumed ( ) โทรใน 3 วินาทีถัดไปเปลี่ยนเป็นเท็จ ในทำนองเดียวกัน onActivityResumed () หากมีตัวจับเวลาฉันจะยกเลิกมัน ในการสรุปผลที่มองเห็นได้คือ isAppInBackground
ขออภัยไม่สามารถคัดลอกรหัส ...
ฉันอยากจะแนะนำให้คุณใช้วิธีอื่นในการทำเช่นนี้
ฉันเดาว่าคุณต้องการที่จะแสดงหน้าจอเริ่มต้นขึ้นในขณะที่โปรแกรมกำลังเริ่มต้นถ้ามันทำงานอยู่ในแบ็กเอนด์แล้วอย่าแสดงมัน
แอปพลิเคชันของคุณสามารถเขียนเวลาปัจจุบันไปยังไฟล์ที่ต้องการได้อย่างต่อเนื่อง ขณะที่แอปพลิเคชันของคุณเริ่มต้นให้ตรวจสอบการประทับเวลาล่าสุดหาก current_time-last_time> ช่วงเวลาที่คุณระบุสำหรับการเขียนเวลาล่าสุดหมายความว่าแอปพลิเคชันของคุณหยุดทำงานไม่ว่าระบบหรือผู้ใช้จะถูกฆ่า