Vòng đời của Activity được thể hiện rất rõ qua các sự kiện onPause, onStop, hay onDestroy nhưng đấy là khi bạn đang thao tác trực tiếp với Activity đó. Còn khi bạn viết code dưới Service's hay BroadcastReceiver's thì Andrroid không cung cấp trực tiếp các hàm đề bạn có thể biết được Activity bạn cần thao tác hiện đang ở trạng thái đang hoạt động hay đang chạy nền.

Nhưng từ API 14 (Android 4, ICS) chúng ta có thể biết được thông tin này thông qua Application.registerActivityLifecycleCallbacks. trong Application của ứng dụng.

Đầu tiên là bạn cần tùy chỉnh Application mặc định của ứng dụng bằng một MyApplication.

class MyApplication extends Application {
    public void onCreate(){
        LifecycleHandler.init(this);
    }
}

Đăng ký Application trong Manifest.xml

<application
    android:label="@string/app_name"
    android:theme="@style/AppTheme"
    android:name=".MyApplication">

Tiếp đến chúng ta khỏi tạo class LifecycleHandler

class LifecycleHandler
implements Application.ActivityLifecycleCallbacks {

    private static LifecycleHandler instance;

    public static void init(Application app){
        if (instance == null){
            instance = new LifecycleHandler();
            app.registerActivityLifecycleCallbacks(instance);
        }
    }

    public static LifecycleHandler get(){
        return instance;
    }

    private LifecycleHandler(){}

    // TODO: implement the lifecycle callback methods!

}

Trong này mình có sử dụng Singleton Pattern, cách khai báo như này được khuyến khích từ đội ngũ kỹ thuật của Google.

private boolean foreground;

public boolean isForeground(){
    return foreground;
}

public boolean isBackground(){
    return !foreground;
}

public void onActivityPaused(Activity activity){
    foreground = false;
}

public void onActivityResumed(Activity activity){
    foreground = true;
}

// other ActivityLifecycleCallbacks methods omitted for brevity
// we don't need them, so they are empty anyway ;)

đến đây bạn có thể gọi LifecycleHandler.get().isForeground() để biết ứng dụng của mình có đang hoạt động hay không (hoặc đang nền). Nhưng làm như này sẽ nảy sinh các vấn đề sau:

  • Activity có thể được chuyển xuống chạy nền bất cứ lúc nào. Nên ở đây chúng ta nên sử dụng cơ chế notify để thông báo có cácService's hay BroadcastReceiver's cần sử dụng.
  • Khi úng dụng của bạn đang chuyển đổi giữa các Activity thì khi Activity thứ nhất tạm dừng (onPaused() được gọi) và Activity thứ 2 đang trong quá trình khởi tạo thì hàm isForeground() sẽ trả về false mặc dù ứng dụng của bạn đang chạy.

Để giải quyết 2 vấn đề trên mình có thêm 1 số đoạn code để tối ưu hơn cho việc Service's hay BroadcastReceiver's theo dõi sự hoạt động của ứng dụng.

Đầu tiên là sử dụng Callback

   public interface Listener {
           public void onBecameForeground();
           public void onBecameBackground();
       }
   
   private List listeners =
   new CopyOnWriteArrayList();

   public void addListener(Listener listener){
       listeners.add(listener);
   }

   public void removeListener(Listener listener){
       listeners.remove(listener);
   }

sử dụng notify trong 2 hàm onActivityPausedonActivityResumed

    public void onActivityPaused(){
       foreground = false;
       for (Listener l : listeners){
           try {
               l.onBecameBackground();
           } catch (Exception exc) {
               Log.e("Foreground", "Unhappy listener", exc);
           }
       }
   }

   public void onActivityResumed(){
       foreground = true;
       for (Listener l : listeners){
           try {
               l.onBecameForeground();
           } catch (Exception exc) {
               Log.e("Foreground", "Unhappy listener", exc);
           }
       }
   }

ở đây các Service chỉ cần đăng ký lắng nghe là có thể chủ động biết ứng dụng của bạn có đang hoạt động hay không. Như vậy là vấn đề 1 đã được giải quyết, còn vấn đề 2 là khoảng thời gian chuyển đổi giữa 2 Activity.

Vấn đề này chúng ta sẽ đưa ra 1 khoảng thời gian nhất định là khoảng thời gian chuyển đổi giữa 2 Acvitiy. Tất hiên khoảng thời gian này chỉ là tương đối đối với từng ứng dụng của bạn

    public static final long CHECK_DELAY = 500;

Và sử dụng HandlerRunnable để quản lý việc Notify.

   private boolean foreground = false, paused = true;
   private Handler handler = new Handler();
   private Runnable check;

Giờ chúng ta sẽ cập nhật lại 2 hàm onActivityPausedonActivityResumed.

   @Override
   public void onActivityResumed(Activity activity) {
       paused = false;
       boolean wasBackground = !foreground;
       foreground = true;

       if (check != null)
           handler.removeCallbacks(check);

       if (wasBackground){
           Log.i(TAG, "went foreground");
           for (Listener l : listeners) {
               try {
                   l.onBecameForeground();
               } catch (Exception exc) {
                   Log.e(TAG, "Listener threw exception!", exc);
               }
           }
       } else {
           Log.i(TAG, "still foreground");
       }
   }

   @Override
   public void onActivityPaused(Activity activity) {
       paused = true;

       if (check != null)
           handler.removeCallbacks(check);

       handler.postDelayed(check = new Runnable(){
           @Override
           public void run() {
               if (foreground && paused) {
                   foreground = false;
                   Log.i(TAG, "went background");
                   for (Listener l : listeners) {
                       try {
                           l.onBecameBackground();
                       } catch (Exception exc) {
                           Log.e(TAG, "Listener threw exception!", exc);
                       }
                   }
               } else {
                   Log.i(TAG, "still foreground");
               }
           }
       }, CHECK_DELAY);
   }

Tới đây thì gần như đã hoàn thành việc quản lý ứng dụng đang hoạt động hay chạy nền. Các Service … có thế đăng ký lắng nghe Listener thông qua 2 hàm onBecameForegroundonBecameBackground để có những xử lý tương tác chính xác với Activity.