我記得在早期職涯剛接觸 Android 的時候,那時遇到這個需求,我的第一個想法就是在 Application 存放一個 boolean flag isAppActive,然後定義一個 abstract base activity,在 onStart 跟 onStop 的時候通知 Application:
public class MyApplication extends Application {
private boolean isAppActive = false;
private int activeActivityCounter = 0;
public boolean isActive() {
return isAppActive;
}
public void onActivityStart() {
if (activeActivityCounter == 0) {
isAppActive = true;
}
activeActivityCounter++;
}
public void onActivityStop() {
activeActivityCounter--;
if (activeActivityCounter == 0) {
isAppActive = false;
}
}
}
public abstract class MyBaseActivity extends Activity {
private MyApplication app;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (MyApplication) getApplication();
}
@Override
protected void onStart() {
super.onStart();
app.onActivityStart();
}
@Override
protected void onStop() {
super.onPause();
app.onActivityStop();
}
}
App 裡的每個 Activity 都必須繼承這個 Base Activity,這樣我們就能知道目前 app 是處於前景還是背景。
這個寫法基本上沒什麼問題,但如果做 code review,我會提幾個 concerns:
1. Activity 跟 Application 的互動方式。在這個寫法裡面,Application 算是 “被動" 地接收 Activity 的狀態變化通知。如果可以,我會希望是 Application “主動” 地觀察每個 Activity 的狀態變化。
2. 責任被分散在各個 Activity。如果團隊中有人寫了一個新的 Activity,但忘記繼承 Base Activity,這個方法就會出錯。
後來,Android 提供了一個更便利的方法讓我們觀察每個 Activity 的狀態變化:ActivityLifecycleCallbacks
public class MyApplication extends Application {
private boolean isAppActive = false;
private int activeActivityCounter = 0;
public boolean isActive() {
return isAppActive;
}
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(
new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(
@NonNull Activity activity, @Nullable Bundle savedInstanceState) {}
@Override
public void onActivityStarted(@NonNull Activity activity) {
if (activeActivityCounter == 0) {
isAppActive = true;
}
activeActivityCounter++;
}
@Override
public void onActivityResumed(@NonNull Activity activity) {}
@Override
public void onActivityPaused(@NonNull Activity activity) {}
@Override
public void onActivityStopped(@NonNull Activity activity) {
activeActivityCounter--;
if (activeActivityCounter == 0) {
isAppActive = false;
}
}
@Override
public void onActivitySaveInstanceState(
@NonNull Activity activity, @NonNull Bundle outState) {}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {}
});
}
}
這個方法跟上面那個方法基本上是同一套邏輯,只是差別在於 Application 是主動觀察每個 Activity 的狀態變化,而且每個 Activity 也不需另外通知 Application,Android Framework 會幫我們完成這些事情。
這個寫法其實已經沒什麼好挑剔的,但如果真的要挑,我們可以考慮把 activeActivityCounter 跟 registerActivityLifecycleCallbacks 裡面的內容做封裝,讓這些邏輯獨立在 Application 之外,這樣做不但可以保持 Application 乾淨簡短,我們也可以替這段邏輯做命名:AppStatusDetector。之後其他工程師看到這個 class,不用讀完整個檔案,馬上就可以理解它的目的。
其實,在我們這麼想的同時,Android 又提供了一個更好用的方法。
Google 在 AndroidX 裡面提供了很多改進以及好用的 libraries,其中一項很重要的東西 Lifecycle-aware components 就是為了解決生命週期以及狀態變化這個棘手問題。為了更優雅地處理 App 的狀態改變,我們可以利用 androidx.lifecycle 裡面的 ProcessLifecycleOwner 跟 LifecycleObserver:
public class MyApplication extends Application implements LifecycleObserver {
private boolean isAppActive = false;
public boolean isActive() {
return isAppActive;
}
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onAppStart() {
isAppActive = true;
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private void onAppStop() {
isAppActive = false;
}
}
得到的結果一樣,但 code 只有一半,因為 AndroidX 幫我們處理了很多細節。一些檢查邏輯都被封裝進 ProcessLifecycleOwner,如果我們點進去看,就會發現裡面做的事情跟上面第二種寫法的邏輯很類似,也是呼叫 registerActivityLifecycleCallbacks 來觀察每個 Activity 的狀態變化,只是它的判斷邏輯更精細、也更嚴謹。
這次會想要聊這個主題,主要是因為前陣子在一份 code 裡面,看到上面提到的第二種寫法,然後又讓我想起最近幾年看過的第三種寫法,想著想著… 又想起了自己剛學 Android 時寫過的第一種寫法。沒想到一個主題,可以帶我回顧自己的 Android 職涯,忽然有種感慨:時間過得好快啊…