Kiểm tra nếu một ứng dụng Android đang chạy trong nền


329

Theo nền tảng, ý tôi là không có hoạt động nào của ứng dụng hiện đang hiển thị cho người dùng?



7
Tôi đang bối rối ở đây .. tại sao Android không thể cung cấp ghi đè đơn giản cho lớp ứng dụng cho việc này? Có quá khó để biết điều này ở cấp độ nền tảng? @Override void void onApplicationSentToBackground () {}
Chuck D

2
@ChuckD - điều đó sẽ có ý nghĩa, đó là điều mà SDK Android dường như muốn tránh làm đôi khi. : /
Đánh dấu


1
iOS có điều này trong các spades, không chắc tại sao Google lại làm điều này khó khăn đến vậy. Đó là một nhu cầu rõ ràng.
Jerry Destremps

Câu trả lời:


388

Có một số cách để phát hiện xem ứng dụng của bạn có chạy nền hay không, nhưng chỉ một trong số đó là hoàn toàn đáng tin cậy:

  1. Các giải pháp phù hợp (các khoản tín dụng đi đến Dan , CommonsWareNeTeInStEiN )
    Theo dõi tầm nhìn của ứng dụng của bạn bằng cách sử dụng bản thân Activity.onPause, Activity.onResumephương pháp này. Lưu trữ trạng thái "khả năng hiển thị" trong một số lớp khác. Các lựa chọn tốt là việc bạn tự thực hiện Applicationhoặc Service(cũng có một vài biến thể của giải pháp này nếu bạn muốn kiểm tra mức độ hiển thị của hoạt động từ dịch vụ).
     
    Ví dụ
    Triển khai Applicationlớp tùy chỉnh (lưu ý isActivityVisible()phương thức tĩnh):

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

    Đăng ký lớp ứng dụng của bạn tại AndroidManifest.xml:

    <application
        android:name="your.app.package.MyApplication"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >

    Thêm onPauseonResumecho mọi người Activitytrong dự án (bạn có thể tạo tổ tiên chung cho Hoạt động của mình nếu bạn muốn, nhưng nếu hoạt động của bạn đã được mở rộng từ MapActivity/ ListActivityv.v. bạn vẫn cần phải viết bằng tay sau):

    @Override
    protected void onResume() {
      super.onResume();
      MyApplication.activityResumed();
    }
    
    @Override
    protected void onPause() {
      super.onPause();
      MyApplication.activityPaused();
    }

     
    Cập nhật
    ActivityLifecyclCallbacks đã được thêm vào trong API cấp 14 (Android 4.0). Bạn có thể sử dụng chúng để theo dõi xem một hoạt động của ứng dụng của bạn hiện đang hiển thị cho người dùng. Kiểm tra câu trả lời của Cornstalks bên dưới để biết chi tiết.

  2. Một sai
    tôi đã sử dụng để đề xuất giải pháp sau đây:

    Bạn có thể phát hiện ứng dụng tiền cảnh / nền hiện đang ActivityManager.getRunningAppProcesses()trả về danh sách các RunningAppProcessInfobản ghi. Để xác định xem ứng dụng của bạn là trên tờ séc foreground RunningAppProcessInfo.importancelĩnh vực bình đẳng đến RunningAppProcessInfo.IMPORTANCE_FOREGROUNDkhiRunningAppProcessInfo.processName bằng tên gói ứng dụng của bạn.

    Ngoài ra nếu bạn gọi ActivityManager.getRunningAppProcesses()từ luồng UI ứng dụng của bạn, nó sẽ trả lại tầm quan trọng IMPORTANCE_FOREGROUNDcho nhiệm vụ của bạn cho dù nó có thực sự ở phía trước hay không. Gọi nó trong luồng nền (ví dụ thông qua AsyncTask) và nó sẽ trả về kết quả chính xác.

    Mặc dù giải pháp này có thể hoạt động (và nó thực sự hoạt động hầu hết thời gian), tôi thực sự khuyên bạn không nên sử dụng nó. Và đây là lý do. Như Dianne Hackborn đã viết :

    Các API này không có cho các ứng dụng dựa trên luồng UI của chúng, nhưng để thực hiện những việc như hiển thị cho người dùng các ứng dụng đang chạy hoặc trình quản lý tác vụ, v.v.

    Có một danh sách lưu trong bộ nhớ cho những điều này. Tuy nhiên, nó bị tắt trong một quy trình khác, được quản lý bởi các luồng chạy riêng biệt với bạn và không phải là thứ bạn có thể tin tưởng (a) kịp thời đưa ra quyết định chính xác hoặc (b) có một bức tranh nhất quán khi bạn quay lại. Cộng với quyết định về hoạt động "tiếp theo" sẽ luôn được thực hiện tại thời điểm xảy ra chuyển đổi và phải đến thời điểm chính xác đó (nơi trạng thái hoạt động bị khóa nhanh để thực hiện chuyển đổi) thực sự biết chắc chắn điều tiếp theo sẽ là gì.

    Và việc thực hiện và hành vi toàn cầu ở đây không được đảm bảo sẽ giữ nguyên trong tương lai.

    Tôi ước tôi đã đọc được điều này trước khi tôi đăng câu trả lời lên SO, nhưng hy vọng vẫn chưa quá muộn để thừa nhận lỗi của mình.

  3. Một giải pháp sai khác Thư viện
    Droid-Fu được đề cập trong một trong những câu trả lời sử dụng ActivityManager.getRunningTaskscho isApplicationBroughtToBackgroundphương pháp của nó . Xem bình luận của Dianne ở trên và không sử dụng phương pháp đó.


4
Để biết nếu bạn nhấn nút home hoặc một số ứng dụng khác đã đạt được trọng tâm: 1) thực hiện giải pháp tốt . 2) Trong OnStopyêu cầu isActivityVisible.
Brais Gabin

28
Thật không may, giải pháp 'chính xác' của bạn không hiệu quả với tôi. Xem xét bạn chu kỳ thông qua các hoạt động trong ứng dụng của bạn. Điều xảy ra sau đó là cờ 'inForeground' của bạn diễn ra như sau: Đúng, Sai (Giữa hoạt động thứ nhất và hoạt động thứ hai là onResume) sau đó lại đúng, v.v. Sau đó, bạn sẽ cần một độ trễ nào đó.
Radu

14
Giải pháp này không hoạt động nếu bạn không thể trực tiếp kiểm soát tất cả các hoạt động. Chẳng hạn, nếu bạn có Hoạt động từ sdk của bên thứ 3 hoặc thậm chí khởi chạy ý định ACTION_VIEW.
dùng123321

66
Android là một xác tàu đáng sợ như vậy. Không ai nghĩ rằng ai đó có thể muốn duy trì dữ liệu cấp ứng dụng? Hãy cho tôi nghỉ ngơi

8
Có vẻ như câu trả lời thực sự cho câu hỏi này là "Bạn không thể kiểm tra nó đúng cách". Giải pháp được gọi là 'chính xác' là một cách giải quyết tốt nhất, vì vậy, sử dụng ActivityLifecycleCallbacks. Bạn vẫn cần xem xét chuyển đổi giữa các hoạt động sẽ được đăng ký là "không ở phía trước". Nó làm tôi suy nghĩ rằng bạn không thể kiểm tra một thứ đơn giản như thế ...
serine

263

KHÔNG SỬ DỤNG TRẢ LỜI NÀY

Câu trả lời của user1269737 là cách thích hợp (được Google / Android phê duyệt) để làm điều này . Đi đọc câu trả lời của họ và cho họ +1.

Tôi sẽ để lại câu trả lời ban đầu của tôi ở đây vì lợi ích của hậu thế. Đây là phiên bản tốt nhất có sẵn trong năm 2012, nhưng bây giờ Android đã hỗ trợ thích hợp cho việc này.

Câu trả lời gốc

Chìa khóa đang sử dụng ActivityLifecycleCallbacks (lưu ý rằng điều này yêu cầu API Android cấp 14 (Android 4.0)). Chỉ cần kiểm tra xem số lượng hoạt động dừng lại có bằng số lượng hoạt động bắt đầu không. Nếu chúng bằng nhau, ứng dụng của bạn đang được làm nền. Nếu có nhiều hoạt động bắt đầu, ứng dụng của bạn vẫn hiển thị. Nếu có nhiều hoạt động trở lại hơn các hoạt động bị tạm dừng, ứng dụng của bạn không chỉ hiển thị mà còn ở phía trước. Có 3 trạng thái chính mà hoạt động của bạn có thể ở, sau đó: hiển thị và ở phía trước, hiển thị nhưng không ở phía trước và không hiển thị và không ở phía trước (tức là trong nền).

Điều thực sự thú vị về phương pháp này là nó không có vấn đề không đồng bộ getRunningTasks(), nhưng bạn cũng không phải sửa đổi mọi thứ Activitytrong ứng dụng của mình để đặt / hủy đặt cái gì đó trong onResumed()/ onPaused(). Đó chỉ là một vài dòng mã độc lập và nó hoạt động trong toàn bộ ứng dụng của bạn. Thêm vào đó, không có quyền yêu cầu sôi nổi.

MyLifecyclHandler.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 đã hỏi một số câu hỏi hay về phương pháp này mà tôi muốn trả lời trong câu trả lời này cho mọi người:

onStop()không được gọi trong các tình huống bộ nhớ thấp; đó có phải là một vấn đề ở đây không?

Không. Các tài liệu để onStop()nói:

Lưu ý rằng phương thức này có thể không bao giờ được gọi, trong các tình huống bộ nhớ thấp trong đó hệ thống không có đủ bộ nhớ để duy trì quá trình hoạt động của bạn sau khi phương thức onPause () của nó được gọi.

Chìa khóa ở đây là "giữ cho quá trình hoạt động của bạn hoạt động ..." Nếu đạt đến tình trạng bộ nhớ thấp này, quá trình của bạn thực sự bị giết (không chỉ hoạt động của bạn). Điều này có nghĩa là phương pháp kiểm tra nền này vẫn còn hiệu lực vì a) dù sao bạn cũng không thể kiểm tra nền nếu quy trình của bạn bị giết và b) nếu quy trình của bạn bắt đầu lại (vì một hoạt động mới được tạo), thành viên các biến (cho dù tĩnh hay không) MyLifecycleHandlersẽ được đặt lại thành 0.

Điều này có làm việc để thay đổi cấu hình?

Theo mặc định, không. Bạn phải đặt rõ ràng configChanges=orientation|screensize( |với bất kỳ thứ gì bạn muốn) trong tệp kê khai của bạn và xử lý các thay đổi cấu hình, nếu không hoạt động của bạn sẽ bị hủy và được tạo lại. Nếu bạn không đặt điều này, các phương thức của hoạt động của bạn sẽ được gọi theo thứ tự này : onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. Như bạn có thể thấy, không có sự trùng lặp (thông thường, hai hoạt động chồng chéo rất ngắn khi chuyển đổi giữa hai, đó là cách phương thức phát hiện nền này hoạt động). Để giải quyết vấn đề này, bạn phải thiết lập configChangesđể hoạt động của bạn không bị phá hủy. May mắn thay, tôi đã phải thiết lậpconfigChangesđã có trong tất cả các dự án của tôi vì không mong muốn toàn bộ hoạt động của tôi bị phá hủy khi xoay / thay đổi kích thước màn hình, vì vậy tôi chưa bao giờ thấy điều này có vấn đề. (cảm ơn dpimka vì đã làm mới trí nhớ của tôi về điều này và sửa lỗi cho tôi!)

Một lưu ý:

Khi tôi nói "nền" ở đây trong câu trả lời này, tôi có nghĩa là "ứng dụng của bạn không còn hiển thị nữa". Các hoạt động của Android có thể hiển thị nhưng không ở phía trước (ví dụ: nếu có lớp phủ thông báo trong suốt). Đó là lý do tại sao tôi đã cập nhật câu trả lời này để phản ánh điều đó.

Điều quan trọng cần biết là Android có một khoảnh khắc lấp lửng kỳ lạ khi chuyển đổi các hoạt động mà không có gì ở phía trước . Vì lý do này, nếu bạn kiểm tra xem ứng dụng của bạn có ở phía trước hay không khi chuyển đổi giữa các hoạt động (trong cùng một ứng dụng), bạn sẽ được thông báo rằng bạn không ở trong nền trước (mặc dù ứng dụng của bạn vẫn là ứng dụng đang hoạt động và hiển thị ).

Bạn có thể kiểm tra xem ứng dụng của bạn ở phía trước trong của bạn Activity's onPause()phương pháp sau super.onPause() . Chỉ cần nhớ trạng thái limbo kỳ lạ mà tôi vừa nói.

Bạn có thể kiểm tra xem ứng dụng của bạn được hiển thị (tức là nếu nó không ở chế độ nền) trong của bạn Activity's onStop()phương pháp sau super.onStop() .


1
Điều này có vẻ thú vị - nhưng điều gì xảy ra trong các tình huống bộ nhớ thấp? Không đảm bảo rằng onStop () sẽ được gọi. Chúng ta có thể gặp phải trường hợp onStop () không được gọi và bộ đếm dừng không được tăng lên - điều này có nghĩa là kiểm tra nền không còn đáng tin cậy? Hay điều này sẽ không bao giờ xảy ra?
Mewzer

1
Ngoài ra, điều này sẽ bỏ qua thay đổi cấu hình? Hoặc ứng dụng sẽ được coi là nền nếu hoạt động được tạo lại do thay đổi cấu hình (ví dụ: Thay đổi hướng)?. Xin lỗi, cho các câu hỏi nhưng tôi nghĩ rằng bạn đang ở một cái gì đó và quan tâm đến việc nếu nó hoạt động trong các trường hợp cạnh.
Mewzer

1
@Mewzer: Tôi sẽ trả lời như một bình luận, nhưng sẽ mất một chút gõ để có những câu trả lời này, vì vậy hãy kiểm tra lại sau vài phút và tôi sẽ chỉnh sửa câu trả lời của mình.
Cornstalks

1
@Mewzer: Bạn nên tìm câu trả lời của bạn bây giờ. Hãy cho tôi biết nếu có bất kỳ câu hỏi khác!
Bắp ngô

2
@Mewzer: Tôi vừa thêm một lưu ý mà bạn có thể quan tâm. Cụ thể, hãy kiểm tra nền onStop()sau super.onStop(). Không kiểm tra nền onPause().
Bắp ngô

186

GIẢI PHÁP GOOGLE - không phải là hack, như các giải pháp trước đây. Sử dụng ProcessLifecyclOwner

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

trong 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' }
    }
}

Bạn có thể đọc thêm về các thành phần kiến ​​trúc liên quan đến Vòng đời tại đây - https://developer.android.com/topic/l library / arch architecture / lifecycle


10
Đây chắc chắn là câu trả lời chính xác! Nó hoạt động như một bùa mê: D
JaviOverflow

2
Điều này hoạt động hoàn hảo, tôi cũng đã sửa đổi một chút để tôi có thể truy cập trạng thái nền trước / nền dễ dàng hơn bên ngoài lớp này: companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }sau đó bạn có thể có trạng thái nền trước vớiArchLifecycleApp.isForeground()
Jose Jet

2
Ôi trời, điều này tốt hơn nhiều so với câu trả lời cũ của tôi. Có +1 từ tôi. Tôi cập nhật câu trả lời của tôi để chỉ mọi người cho bạn.
Bắp ngô

2
Mặc dù đây là một câu trả lời đúng, không cần thực hiện các cuộc gọi lại, bạn chỉ có thể truy vấn ProcessLifecycleOwner bất cứ khi nào bạn muốn. Kiểm tra stackoverflow.com/a/52678290/6600000
Keivan Esbati

2
Như doc nói The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. , điều này không hoạt động cho multiple processescác ứng dụng, có một số api chúng ta có thể đạt được một cách thanh lịch?
acntwww

23

Bắt đầu thư viện hỗ trợ phiên bản 26, bạn có thể sử dụng ProcessLifecycleOwner , chỉ cần thêm nó vào phần phụ thuộc của bạn như được mô tả ở đây , ví dụ:

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
}

Và sau đó chỉ cần truy vấn ProcessLifecycleOwnerbất cứ khi nào bạn muốn cho trạng thái ứng dụng, ví dụ:

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

2
Cảm ơn bạn, đây là cách tốt nhất và dễ nhất phù hợp với bất kỳ phần nào trong mã của bạn đặc biệt khi sử dụng fcm.
MiHae Kheel

nếu ứng dụng bị đóng hoàn toàn thì phương thức đầu tiên sẽ trả về cái gì?
Evgeniy Mishustin

@EvgeniyMishustin phụ thuộc vào trạng thái hiện tại của ứng dụng, nhưng bạn thường sẽ thấy CREATED rồi DESTROYED và sau đó, bạn sẽ không nhận được bất kỳ sự kiện mới nào.
Keivan Esbati

Vậy đâu là câu lệnh "IF" để xem ứng dụng IF ở chế độ nền (tiền cảnh) ???
ekashking

@ekashking chỉ cần đặt toàn bộ câu lệnh trong mệnh đề if. Ví dụ: if (ProcessLifecycleOwner.get (). GetLifecycle (). GetCienState (). IsAtLeast (Lifecycle.State.STARTED)) => Ứng dụng nằm ở tiền cảnh
Keivan Esbati

20

Vì API Android 16, có một cách đơn giản để kiểm tra xem ứng dụng có ở phía trước không. Nó có thể không phải là hoàn hảo, nhưng không có phương pháp nào trên Android là hoàn hảo. Phương pháp này đủ tốt để sử dụng khi dịch vụ của bạn nhận được cập nhật từ máy chủ và phải quyết định có hiển thị thông báo hay không (vì nếu UI là tiền cảnh, người dùng sẽ thông báo cập nhật mà không cần thông báo).

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;

Mã này có nên nằm trong lớp Service hay lớp khác không, ví dụ lớp Application? Cảm ơn rất nhiều.
Woppi

.. bất cứ nơi nào bạn muốn sử dụng nó như là dòng cuối cùng chỉ là một boolean bạn sẽ kiểm tra lại.
AO_

Đây là phương pháp tương tự SDK Android AWS cho thông báo đẩy.
spakmad

Xin lưu ý rằng "Định nghĩa nền cho mục đích giới hạn dịch vụ khác với định nghĩa được sử dụng bởi quản lý bộ nhớ; một ứng dụng có thể nằm trong nền liên quan đến quản lý bộ nhớ, nhưng ở phía trước là liên quan đến khả năng khởi chạy dịch vụ của nó.) " developer.android.com/about/versions/oreo/background.html (
ARLabs

Cảm ơn bạn, điều này đã làm việc! Tôi đã có thể sử dụng mã này JobServiceđể phát hiện dịch vụ đang chạy ẩn.
Michael Osofsky

17

Câu trả lời của Idolon dễ bị lỗi và phức tạp hơn nhiều mặc dù có lặp lại ở đây kiểm tra ứng dụng Android có ở phía trước hay không? và ở đây Xác định ứng dụng tiền cảnh hiện tại từ một tác vụ hoặc dịch vụ nền

Có một cách tiếp cận đơn giản hơn nhiều:

Trên BaseActivity mà tất cả các Hoạt động mở rộng:

protected static boolean isVisible = false;

 @Override
 public void onResume()
 {
     super.onResume();
     setVisible(true);
 }


 @Override
 public void onPause()
 {
     super.onPause();
     setVisible(false);
 }

Bất cứ khi nào bạn cần kiểm tra xem có bất kỳ hoạt động ứng dụng nào của bạn ở phía trước hay không, chỉ cần kiểm tra isVisible() ;

Để hiểu cách tiếp cận này, hãy kiểm tra câu trả lời này của vòng đời hoạt động song song: Vòng đời hoạt động song song


3
Idolon's answer is error prone- tiếc là tôi phải đồng ý với bạn. Dựa trên nhận xét của Dianne Hackborn trong Nhóm Google tôi đã cập nhật câu trả lời của mình. Kiểm tra nó xin vui lòng cho các chi tiết.
Thần tượng

2
Đây cũng không phải là một giải pháp hoàn hảo. Một kịch bản là nếu người dùng kéo xuống bảng thông báo, sau đó không phải là onPause, onStop, cũng không phải là onResumesự kiện được gọi. Vậy bạn sẽ làm gì sau đó nếu không có sự kiện nào trong số này bị sa thải?!

Điều này dẫn tôi đến câu hỏi này: stackoverflow.com/questions/33657102/ từ
Ruchir Baronia

Đáng buồn thay, nhưng mã này hoạt động sai khi một hoạt động được bắt đầu khi màn hình tắt. Trong trường hợp này, onResume và onPause được gọi là Making isVisible = false.
CoolMind

@CoolMind Bạn có thể giải thích trường hợp sử dụng mà bạn sẽ khởi chạy một hoạt động trong khi ở chế độ nền không?
neteinstein

11

Tôi đã thử giải pháp được đề xuất sử dụng Application.ActivityLifecyclCallbacks và nhiều giải pháp khác, nhưng chúng không hoạt động như mong đợi. Nhờ có Sarge , tôi đã đưa ra một giải pháp khá dễ dàng và đơn giản mà tôi đang mô tả dưới đây.

Chìa khóa của giải pháp là thực tế hiểu rằng nếu chúng ta có ActivityA và ActivityB và chúng ta gọi ActivityB từ ActivityA (và không gọi ActivityA.finish), thì ActivityB onStart()sẽ được gọi trước ActivityA onStop().

Đó cũng là điểm khác biệt chính giữa onStop()onPause()không ai đề cập đến trong các bài báo tôi đọc.

Vì vậy, dựa trên hành vi Vòng đời của Hoạt động này, bạn chỉ cần đếm số lần thực hiện onStart()onPause()được gọi trong chương trình của mình. Lưu ý rằng đối với mỗi Activity chương trình của bạn, bạn phải ghi đè onStart()onStop(), để tăng / giảm biến tĩnh được sử dụng để đếm. Dưới đây là mã thực hiện logic này. Lưu ý rằng tôi đang sử dụng một lớp mở rộng Application, vì vậy đừng quên khai báo Manifest.xmlbên trong thẻ Ứng dụng: android:name=".Utilities"mặc dù nó cũng có thể được thực hiện bằng một lớp tùy chỉnh đơn giản.

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

Bây giờ trên mỗi Hoạt động của chương trình của chúng tôi, chúng ta nên ghi đè onStart()onStop()và tăng / giảm như hình dưới đây:

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

Với logic này, có 2 trường hợp có thể xảy ra:

  1. stateCounter = 0 : Số lượng dừng bằng với số lượng Hoạt động bắt đầu, có nghĩa là ứng dụng đang chạy trên nền.
  2. stateCounter > 0 : Số lượng bắt đầu lớn hơn số lượng dừng, có nghĩa là ứng dụng đang chạy trên nền trước.

Lưu ý: stateCounter < 0sẽ có nghĩa là có nhiều Hoạt động bị dừng hơn là bắt đầu, điều này là không thể. Nếu bạn gặp trường hợp này, thì điều đó có nghĩa là bạn không tăng / giảm bộ đếm như bạn nên.

Bạn đã sẵn sàng để đi. Bạn nên kiểm tra xem ứng dụng của bạn có ở trên nền không onStop().


Tôi sẽ chuyển if(Utilities.isApplicationOnBackground()) …đến Utilities. Bởi vì nếu không thì chỉ một hoạt động cụ thể sẽ phản ứng với sự kiện.
Tên hiển thị

10

Không có cách nào, bạn tự mình theo dõi nó, để xác định xem có bất kỳ hoạt động nào của bạn có thể nhìn thấy hay không. Có lẽ bạn nên xem xét việc đặt câu hỏi StackOverflow mới, giải thích những gì bạn đang cố gắng đạt được từ trải nghiệm người dùng, vì vậy chúng tôi có thể cung cấp cho bạn các ý tưởng triển khai thay thế.


2
Trong Android, chúng tôi có một cài đặt gọi là "Dữ liệu nền". Cài đặt này bật bất kỳ kết nối dữ liệu nền nào khi ứng dụng đang chạy ẩn. Tôi muốn triển khai chuyển đổi "Dữ liệu nền" cho ứng dụng của mình, vì vậy khi không có hoạt động nào của tôi hiển thị cho người dùng, tôi muốn dịch vụ của mình ngừng thực hiện bất kỳ chuyển dữ liệu nào, nhưng thời điểm một trong các hoạt động của tôi tiếp tục, tôi muốn tiếp tục truyền dữ liệu
cppdev

1
@cppdev: Hy vọng, việc "truyền dữ liệu" đang được tiến hành bởi a Service. Nếu vậy, yêu cầu các hoạt động của bạn thông báo cho dịch vụ khi chúng xuất hiện và biến mất. Nếu Servicexác định rằng không có hoạt động nào hiển thị và vẫn duy trì như vậy trong một khoảng thời gian, hãy dừng truyền dữ liệu tại điểm dừng logic tiếp theo. Có, điều này sẽ yêu cầu mã cho từng hoạt động của bạn, nhưng ngay bây giờ, đó là điều không thể tránh khỏi AFAIK.
CommonsWare

1
Nếu bạn muốn tránh sao chép-dán mã chung giữa tất cả các hoạt động của mình, bạn có thể tạo một lớp MyActivityClasskế thừa từ Activityvà thực hiện các phương thức vòng đời và làm cho tất cả các hoạt động của bạn được kế thừa từ đó MyActivityClass. Điều này sẽ không hoạt động PreferenceActivityhoặc MapActivitymặc dù (xem câu hỏi này )
Guillaume Brunerie

@CommonsWare tôi đã thử với OnPause () OnResume () rằng nó có hoạt động hay không nhưng nếu ứng dụng của tôi không xem trong màn hình xem nó có chạy ở chế độ nền hay không, hãy kiểm tra xem nó có hoạt động hay không
Manoj

@CommonsWare tôi đã thử với OnPause () OnResume () rằng nó có hoạt động hay không nhưng nếu ứng dụng của tôi không xem trong màn hình xem nó có chạy ở chế độ nền hay không, hãy kiểm tra xem nó có hoạt động hay không
Manoj

5

Bạn có thể sử dụng ElementCallbacks2 để phát hiện xem ứng dụng có ở chế độ nền không. BTW cuộc gọi lại này chỉ khả dụng trong API Cấp 14 (Ice Cream Sandwich) trở lên.

Bạn sẽ nhận được một cuộc gọi đến phương thức:

public abstract void onTrimMemory (int level)

nếu cấp độ ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENthì ứng dụng ở chế độ nền.

Bạn có thể thực hiện giao diện này để một activity, servicevv

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

1
Đã thử câu trả lời của bạn nhưng không đáng tin cậy. cuộc gọi lại onTrimMemory sẽ không được kích hoạt khi màn hình bị khóa cũng như khi nhấn nút "nguồn" để khóa màn hình. Nó cũng sẽ không luôn trả về TRIM_MEMORY_UI_HIDDEN nếu ứng dụng của bạn hiển thị và bạn mở một ứng dụng khác thông qua thông báo trên thanh trạng thái. Giải pháp đáng tin cậy duy nhất là triển khai ActivityLifecyclCallbacks và điều chỉnh nó theo trường hợp sử dụng.
vận tốc

4

Xây dựng trên câu trả lời @Cornstalks để bao gồm một vài tính năng hữu ích.

Các tính năng bổ sung:

  • đã giới thiệu mẫu singleton để bạn có thể thực hiện điều này ở bất cứ đâu trong ứng dụng: AppLifecycleHandler.isApplicationVisible () và AppLifecycleHandler.isApplicationInForeground ()
  • thêm xử lý các sự kiện trùng lặp (xem bình luận // thực hiện một số hành động thay đổi mức độ hiển thị và // thực hiện một số hành động thay đổi trong nền trước)

App.java

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
    }
}

AppLifecyclHandler.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;
    }
}

3

Giải pháp tốt nhất tôi đã đưa ra với việc sử dụng bộ tính giờ.

Bạn đã bắt đầu bộ hẹn giờ trong onPause () và hủy cùng bộ hẹn giờ trong onResume (), có 1 phiên bản của Timer (thường được xác định trong lớp Ứng dụng). Bản thân bộ hẹn giờ được đặt để chạy Runnable sau 2 giây (hoặc bất kỳ khoảng thời gian nào bạn nghĩ là phù hợp), khi bộ hẹn giờ kích hoạt bạn đặt cờ đánh dấu ứng dụng ở chế độ nền.

Trong phương thức onResume () trước khi bạn hủy bộ hẹn giờ, bạn có thể truy vấn cờ nền để thực hiện bất kỳ thao tác khởi động nào (ví dụ: bắt đầu tải xuống hoặc bật dịch vụ định vị).

Giải pháp này cho phép bạn có một số hoạt động trên ngăn xếp phía sau và không yêu cầu bất kỳ quyền nào để thực hiện.

Giải pháp này hoạt động tốt nếu bạn cũng sử dụng xe buýt sự kiện, vì bộ đếm thời gian của bạn chỉ đơn giản có thể kích hoạt một sự kiện và các phần khác nhau trong ứng dụng của bạn có thể đáp ứng tương ứng.


Tôi bắt đầu nghĩ rằng đây là giải pháp tốt nhất (dù không may)
dhaag23

Đúng, đây là giải pháp tốt nhất mà tôi đã quản lý. Tôi cần dừng quét bluetooth khi ứng dụng không được báo trước, nhưng không thể sử dụng tạm dừng hoặc dừng hoặc hủy vì tôi không muốn liên tục dừng và bắt đầu khi người dùng điều hướng xung quanh ứng dụng.
CaptRespect

3

Nếu bạn bật cài đặt dành cho nhà phát triển "Đừng giữ hành động" - chỉ kiểm tra số lượng kích hoạt đã tạo là không đủ. Bạn cũng phải kiểm tra isSaveInstanceState . My phương pháp tùy chỉnh isApplicationRunning () kiểm tra là ứng dụng Android đang chạy:

Đây là mã công việc của tôi:

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

}

3

Chỉ có một giải pháp đúng:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyApp.mainActivity = this;
        super.onCreate(savedInstanceState);
        ...
    }

MyApp.java:

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) {
            ...
        }
    }

}

Tôi không thấy cách giải pháp này có thể cho tôi câu trả lời cho một câu hỏi đơn giản trong câu lệnh IF về hoạt động của tôi (hoặc đoạn) cho dù ứng dụng của tôi ở trên nền hay nền trước. Tuyên bố "NẾU" ở đâu ???
ekashking

2

Để cõng những gì CommonsWare và Key đã nói, có lẽ bạn có thể mở rộng lớp Ứng dụng và có tất cả các hoạt động của bạn gọi đó là phương thức onPause / onResume của chúng. Điều này sẽ cho phép bạn biết (các) Hoạt động nào có thể nhìn thấy, nhưng điều này có thể được xử lý tốt hơn.

Bạn có thể giải thích chính xác những gì bạn có trong tâm trí? Khi bạn nói chạy trong nền, bạn có nghĩa là chỉ cần ứng dụng của bạn vẫn còn trong bộ nhớ mặc dù hiện tại nó không có trên màn hình? Bạn đã xem xét việc sử dụng Dịch vụ như một cách liên tục hơn để quản lý ứng dụng của mình khi ứng dụng không tập trung chưa?


Trong Android, chúng tôi có một cài đặt gọi là "Dữ liệu nền". Cài đặt này bật bất kỳ kết nối dữ liệu nền nào khi ứng dụng đang chạy ẩn. Tôi muốn triển khai chuyển đổi "Dữ liệu nền" cho ứng dụng của mình, vì vậy khi không có hoạt động nào của tôi hiển thị cho người dùng, tôi muốn dịch vụ của mình ngừng thực hiện bất kỳ chuyển dữ liệu nào, nhưng thời điểm một trong các hoạt động của tôi tiếp tục, tôi muốn tiếp tục truyền dữ liệu
cppdev

1
Applicationkhông có onPause()hoặc onResume().
CommonsWare

1
@CommonsWare Bạn nói đúng, tôi đã đề cập đến từng Hoạt động riêng lẻ liên hệ với Ứng dụng khi tạm dừng / tiếp tục. Về cơ bản, đây là ý tưởng bạn vừa chia sẻ trên bình luận cho câu trả lời của mình, mặc dù bạn đã sử dụng Dịch vụ mà tôi tưởng tượng là một bước đi thông minh hơn.
Dan

2

Tôi đã tự thực hiện ActivityLifecycleCallbacks. Tôi đang sử dụng SherlockActivity, nhưng đối với lớp Activity bình thường có thể hoạt động.

Đầu tiên, tôi đang tạo một giao diện có tất cả các phương thức để theo dõi vòng đời hoạt động:

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

Thứ hai, tôi đã triển khai giao diện này trong lớp Ứng dụng của mình:

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

Thứ ba, tôi đang tạo một lớp học mở rộng từ 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);
    }   
}

Thứ tư, tất cả các lớp mở rộng từ SherlockActivity, tôi đã thay thế cho MySherlockActivity:

public class MainActivity extends MySherlockActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

}

Bây giờ, trong logcat, bạn sẽ thấy các bản ghi được lập trình trong triển khai Giao diện được thực hiện trong MyApplication.


1

Hoạt động bị tạm dừng khi Hộp thoại xuất hiện phía trên nó để tất cả các giải pháp được đề xuất là một nửa giải pháp. Bạn cần phải tạo móc cho hộp thoại là tốt.



1

Tài liệu chính thức:

Hệ thống phân biệt giữa các ứng dụng nền trước và nền. (Định nghĩa nền cho mục đích giới hạn dịch vụ khác với định nghĩa được sử dụng bởi quản lý bộ nhớ; một ứng dụng có thể ở chế độ nền liên quan đến quản lý bộ nhớ , nhưng ở phía trước liên quan đến khả năng khởi chạy dịch vụ của nó.) Một ứng dụng là được coi là ở phía trước nếu bất kỳ điều nào sau đây là đúng:

  1. Nó có một hoạt động có thể nhìn thấy, cho dù hoạt động được bắt đầu hay tạm dừng.
  2. Nó có một dịch vụ tiền cảnh.
  3. Một ứng dụng nền trước khác được kết nối với ứng dụng, bằng cách ràng buộc với một trong các dịch vụ của nó hoặc bằng cách sử dụng một trong những nhà cung cấp nội dung của nó. Ví dụ: ứng dụng nằm ở phía trước nếu ứng dụng khác liên kết với nó:
    • IME
    • Dịch vụ hình nền
    • Người nghe thông báo
    • Dịch vụ thoại hoặc văn bản

Nếu không có điều kiện nào là đúng, ứng dụng được coi là ở chế độ nền.


0

Một giải pháp khác cho bài viết cũ này (đối với những bài viết có thể giúp ích):


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

}

0

Xem bình luận trong hàm onActivityDestroyed.

Hoạt động với SDK mục tiêu phiên bản 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++;
    }
}

0

Bạn nên sử dụng tùy chọn chia sẻ để lưu trữ tài sản và hành động theo sở hữu đó bằng cách sử dụng ràng buộc dịch vụ từ các hoạt động của bạn. Nếu bạn chỉ sử dụng ràng buộc, (không bao giờ sử dụng startService), thì dịch vụ của bạn sẽ chỉ chạy khi bạn liên kết với nó, (liên kết onResume và hủy liên kết) sẽ khiến nó chỉ chạy trên nền trước và nếu bạn muốn làm việc trên nền bạn có thể sử dụng dịch vụ dừng bắt đầu thường xuyên.


0

Tôi nghĩ rằng câu hỏi này nên rõ ràng hơn. Khi nào? Ở đâu? Tình huống cụ thể của bạn mà bạn muốn konw là gì nếu ứng dụng của bạn ở chế độ nền?

Tôi chỉ giới thiệu giải pháp của tôi theo cách của tôi.
Tôi thực hiện điều này bằng cách sử dụng trường "tầm quan trọng" của RunningAppProcessInfolớp trong onStopphương thức của mọi hoạt động trong ứng dụng của tôi, có thể đạt được một cách đơn giản bằng cách cung cấp BaseActivitycho các hoạt động khác để mở rộng onStopphương thức kiểm tra giá trị của "mức độ quan trọng". Đây là mã:

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

Đây không phải là một giải pháp được đề xuất như đã nêu trong câu trả lời của @ Idolon.
CoolMind

0

Tôi khuyên bạn nên đọc qua trang này: http://developer.android.com/reference/android/app/Activity.html

Nói tóm lại, hoạt động của bạn không còn hiển thị sau khi onStop()đã được gọi.


3
Tôi có khoảng 10 hoạt động trong ứng dụng của mình. Vì vậy, tôi muốn biết nếu không ai trong số họ nếu hiển thị cho người dùng. Nói chung, tôi muốn biết toàn bộ ứng dụng của tôi có chạy ở chế độ nền hay không
cppdev

Vì vậy, sau đó bạn theo dõi tất cả 10-ish. Hoặc, như CommonsWare đề xuất, giải thích những gì bạn đang cố gắng làm.
Khóa

3
Điều này không đúng. Hoạt động của bạn được hiển thị cho đến khi onStop; giữa onPauseonStopcó thể nhìn thấy , nhưng không phải ở phía trước .
nickgrim

@nickgrim: điều gì không đúng? Tôi đã nói rằng một hoạt động không còn hiển thị sau khi onStop()được gọi, được liên kết với những gì bạn đã viết.
Khóa

@Key: Ban đầu bạn nói cho đến khi onPauseđược gọi: một chỉnh sửa gần đây đã sửa bạn.
nickgrim

0

Còn việc sử dụng getApplicationState (). IsInForeground () thì sao?


0

Theo tôi, nhiều câu trả lời giới thiệu một tải nặng mã và mang lại nhiều sự phức tạp và không dễ đọc.

Khi mọi người hỏi về SO làm thế nào để giao tiếp giữa a Servicevà a Activity, tôi thường khuyên bạn nên sử dụng LocalBroadcastManager .


Tại sao?

Vâng, bằng cách trích dẫn các tài liệu:

  • Bạn biết rằng dữ liệu bạn đang phát sẽ không rời khỏi ứng dụng của bạn, vì vậy đừng lo lắng về việc rò rỉ dữ liệu riêng tư.

  • Các ứng dụng khác không thể gửi các chương trình phát sóng này đến ứng dụng của bạn, vì vậy bạn không cần phải lo lắng về việc có các lỗ hổng bảo mật mà chúng có thể khai thác.

  • Nó hiệu quả hơn gửi một chương trình phát sóng toàn cầu thông qua hệ thống.

Không có trong các tài liệu:

  • Nó không yêu cầu các thư viện bên ngoài
  • Mã là tối thiểu
  • Thật nhanh chóng để thực hiện và hiểu
  • Không có cuộc gọi lại tự thực hiện tùy chỉnh / mẫu siêu đơn / mẫu trong quá trình nào ...
  • Không tham khảo mạnh mẽ trên Activity, Application...

Sự miêu tả

Vì vậy, bạn muốn kiểm tra xem có bất kỳ cái nào Activityhiện đang ở phía trước không. Bạn thường làm điều đó trong một Service, hoặc Applicationlớp học của bạn .

Điều này có nghĩa, các Activityđối tượng của bạn trở thành người gửi tín hiệu (Tôi đang bật / Tôi tắt). Của bạn Service, mặt khác, trở thành Receiver.

hai khoảnh khắc mà bạn Activitynói với bạn nếu nó ở phía trước hoặc phía sau (có chỉ hai ... không phải 6).

Khi Activityđi vào nền trước, onResume()phương thức được kích hoạt (còn được gọi là sau onCreate()).

Khi Activityđi ở phía sau, onPause()được gọi.

Đây là những khoảnh khắc mà bạn Activitynên gửi tín hiệu đến Serviceđể mô tả trạng thái của nó.

Trong trường hợp nhiều người Activity, hãy nhớ một cái Activityđi vào nền trước, sau đó một cái khác đi vào nền trước.

Vì vậy, tình huống sẽ là: *

Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON

Các Service/ Applicationsẽ chỉ đơn giản là hãy tiếp tục nghe những tín hiệu và hành động phù hợp.


Mã (TLDR)

Bạn Servicephải thực hiện một BroadcastReceiverđể lắng nghe tín hiệu.

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

Đăng ký ReceivertạiService::onCreate()

@Override
protected void onCreate() {
    LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}

Hủy đăng ký nó trong Service::onDestroy()

@Override
protected void onDestroy() {
    // I'm dead, no need to listen to anything anymore.
    LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}

Bây giờ bạn Activityphải truyền đạt trạng thái của họ.

Trong Activity::onResume()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Trong Activity::onPause()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Một tình huống rất, rất phổ biến

Nhà phát triển: Tôi muốn gửi dữ liệu từ tôi Servicevà cập nhật Activity. Làm cách nào để kiểm tra xem cái Activitynày có ở phía trước không?

Thường không cần phải kiểm tra xem Activitycó ở phía trước hay không. Chỉ cần gửi dữ liệu LocalBroadcastManagertừ của bạn Service. Nếu Activitylà trên, sau đó nó sẽ đáp ứng và hành động.

Đối với tình huống rất phổ biến này, người Servicetrở thành người gửi và Activitythực hiện BroadcastReceiver.

Vì vậy, tạo một Receivertrong của bạn Activity. Đăng ký onResume()và không đăng ký onPause(). Không cần phải sử dụng các phương pháp vòng đời khác .

Xác định Receiverhành vi trong onReceive()(cập nhật ListView, làm điều này, làm điều đó, ...).

Bằng cách này, Activityý chí chỉ lắng nghe nếu nó ở phía trước và sẽ không có gì xảy ra nếu nó ở phía sau hoặc bị phá hủy.

Trong trường hợp nhiều người Activity, bất cứ ai Activityđang bật sẽ trả lời (nếu họ cũng thực hiện Receiver).

Nếu tất cả đều ở trong nền, không ai sẽ phản hồi và tín hiệu sẽ bị mất.

Gửi dữ liệu từ Servicethông qua Intent(xem mã ở trên) bằng cách chỉ định ID tín hiệu.



0
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
}

0

Không có câu trả lời nào phù hợp với trường hợp cụ thể nếu bạn muốn biết liệu một hoạt động cụ thể có ở trong khu vực không và nếu bạn là SDK mà không truy cập trực tiếp vào Ứng dụng. Đối với tôi, tôi đang ở trong chủ đề nền khi vừa nhận được thông báo đẩy cho một tin nhắn trò chuyện mới và chỉ muốn hiển thị thông báo hệ thống nếu màn hình trò chuyện không ở phía trước.

Sử dụng ActivityLifecycleCallbacksđiều đó như được khuyến nghị trong các câu trả lời khác, tôi đã tạo ra một lớp sử dụng nhỏ chứa logic để xem MyActivitycó ở trong Tiền cảnh hay không.

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

}


-1

Trong các hoạt động của tôi, onResume và onP Because tôi viết một boolean isVisible cho SharedPrefences.

    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    Editor editor = sharedPrefs.edit();
    editor.putBoolean("visible", false);
    editor.commit();

Và đọc nó ở nơi khác khi cần thông qua,

    // 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)){...}

Có thể không thanh lịch, nhưng nó hoạt động với tôi ...


-1

Có thể đã quá muộn để trả lời nhưng nếu ai đó đến thăm thì đây là giải pháp tôi đề xuất, Lý do một ứng dụng muốn biết trạng thái của nó ở chế độ nền hoặc sắp tới có thể là rất nhiều, một vài là, 1. Để hiển thị bánh mì nướng và thông báo khi người dùng ở trong BG. 2.Để thực hiện một số tác vụ cho người dùng lần đầu tiên đến từ BG, như thăm dò ý kiến, vẽ lại, v.v.

Giải pháp của Idolon và những người khác chăm sóc phần đầu tiên, nhưng không dành cho phần thứ hai. Nếu có nhiều hoạt động trong ứng dụng của bạn và người dùng đang chuyển đổi giữa chúng, thì khi bạn ở hoạt động thứ hai, cờ hiển thị sẽ sai. Vì vậy, nó không thể được sử dụng một cách xác định.

Tôi đã làm một cái gì đó được đề xuất bởi CommonsWare, "Nếu Dịch vụ xác định rằng không có hoạt động nào hiển thị và vẫn duy trì như vậy trong một khoảng thời gian , hãy dừng truyền dữ liệu tại điểm dừng logic tiếp theo."

Dòng in đậm rất quan trọng và điều này có thể được sử dụng để đạt được mục thứ hai. Vì vậy, những gì tôi làm là một khi tôi nhận được onActivityPaused (), đừng thay đổi trực tiếp thành false, thay vào đó là bộ đếm thời gian 3 giây (đó là mức tối đa mà hoạt động tiếp theo sẽ được khởi chạy) và nếu không có onActivityResumed ( ) gọi trong 3 giây tiếp theo, thay đổi hiển thị thành sai. Tương tự như vậy trong onActivityResumed () nếu có bộ đếm thời gian thì tôi hủy nó. Để tổng hợp, hiển thị trở thành isAppInBackground.

Xin lỗi không thể sao chép-dán mã ...


-3

Tôi muốn khuyên bạn nên sử dụng một cách khác để làm điều này.

Tôi đoán bạn muốn hiển thị màn hình khởi động trong khi chương trình đang bắt đầu, nếu nó đã chạy trong phần phụ trợ, đừng hiển thị nó.

Ứng dụng của bạn có thể liên tục ghi thời gian hiện tại vào một tệp cụ thể. Trong khi ứng dụng của bạn đang bắt đầu, hãy kiểm tra dấu thời gian cuối cùng, nếu current_time-last_time> phạm vi thời gian bạn chỉ định để viết thời gian gần nhất, điều đó có nghĩa là ứng dụng của bạn bị dừng, do chính hệ thống hoặc người dùng tự giết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.