
前言
最近開發(fā)應(yīng)用有這么一個需求,需要監(jiān)聽app的生命周期,能夠感知到用戶劃到后臺,回到前臺這些事件。
方案
一般前后臺監(jiān)聽有兩種方案:
- 利用 ActivityLifecycleCallbacks 監(jiān)聽所有activity的生命周期
- 使用 ProcessLifecycleOwner
代碼實現(xiàn)
方案一:利用 ActivityLifecycleCallbacks 監(jiān)聽所有activity的生命周期
定制前后臺切換監(jiān)聽類:ForegroundCallbacks.kt
/**
* 實現(xiàn)方式1:前后臺監(jiān)聽
*/
private const val TAG = "ForegroundCallbacks"
class ForegroundCallbacks(private val mOnAppStatusListener: OnAppStatusListener?) :
Application.ActivityLifecycleCallbacks {
// 當(dāng)前Activity數(shù)量
private var activityStartCount = 0
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
APieLog.d(TAG, "onActivityCreated: $activity")
}
override fun onActivityStarted(activity: Activity) {
APieLog.d(TAG, "onActivityStarted: $activity")
activityStartCount++
// 數(shù)值從0變到1說明是從后臺切到前臺
if (activityStartCount == 1) {
//從后臺切到前臺
mOnAppStatusListener?.onForeground()
}
}
override fun onActivityResumed(activity: Activity) {
APieLog.d(TAG, "onActivityResumed: $activity")
}
override fun onActivityPaused(activity: Activity) {
APieLog.d(TAG, "onActivityPaused: $activity")
}
override fun onActivityStopped(activity: Activity) {
APieLog.d(TAG, "onActivityStopped: $activity")
activityStartCount--
// 數(shù)值從1到0說明是從前臺切到后臺
if (activityStartCount == 0) {
// 從前臺切到后臺
mOnAppStatusListener?.onBackground()
}
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
APieLog.d(TAG, "onActivitySaveInstanceState: $activity")
}
override fun onActivityDestroyed(activity: Activity) {
APieLog.d(TAG, "onActivityDestroyed: $activity")
}
}
使用ForegroundCallbacks.kt
在項目的 application 里注冊監(jiān)聽
class APieApplication : Application() {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(ForegroundCallbacks(object : OnAppStatusListener {
override fun onForeground() {
APieLog.d("ForegroundCallbacks", "正在前臺")
}
override fun onBackground() {
APieLog.d("ForegroundCallbacks", "進入后臺")
}
}))
}
}
效果

方案二:使用 ProcessLifecycleOwner
ProcessLifecycleOwner是Google Lifecycle中的一個類,更優(yōu)雅的監(jiān)聽前后臺的切換
關(guān)于ProcessLifecycleOwner,官方文字是這樣介紹的
[圖片上傳失敗...(image-17d168-1731395595528)]
使用方法
1、 引入依賴庫
implementation "androidx.lifecycle:lifecycle-process:2.2.0"
2、代碼中使用
class MainActivity : AppCompatActivity() {
private var binding: ActivityMainBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
runOnUiThread {
// 前后臺監(jiān)聽
ProcessLifecycleOwner.get().lifecycle.addObserver(object : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
APieLog.d("ForegroundCallbacks-1", "onProcessLifecycleChanged: $event")
})
}
}
}
效果

源碼解讀
核心代碼ProcessLifecycleOwner
根據(jù)上圖得知 ProcessLifecycleOwner 實現(xiàn)了 LifecycleOwner 接口
由于在ProcessLifecycleOwnerInitializer中初始化時傳入了 Context,因此 ProcessLifecycleOwner在 attach 方法中借助 Context 拿到了 Application 實例,并調(diào)用了 registerActivityLifecycleCallbacks
@Suppress("DEPRECATION")
internal fun attach(context: Context) {
handler = Handler()
registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
val app = context.applicationContext as Application
app.registerActivityLifecycleCallbacks(object : EmptyActivityLifecycleCallbacks() {
@RequiresApi(29)
override fun onActivityPreCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
// We need the ProcessLifecycleOwner to get ON_START and ON_RESUME precisely
// before the first activity gets its LifecycleOwner started/resumed.
// The activity's LifecycleOwner gets started/resumed via an activity registered
// callback added in onCreate(). By adding our own activity registered callback in
// onActivityPreCreated(), we get our callbacks first while still having the
// right relative order compared to the Activity's onStart()/onResume() callbacks.
Api29Impl.registerActivityLifecycleCallbacks(activity,
object : EmptyActivityLifecycleCallbacks() {
override fun onActivityPostStarted(activity: Activity) {
activityStarted()
}
override fun onActivityPostResumed(activity: Activity) {
activityResumed()
}
})
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
// Only use ReportFragment pre API 29 - after that, we can use the
// onActivityPostStarted and onActivityPostResumed callbacks registered in
// onActivityPreCreated()
if (Build.VERSION.SDK_INT < 29) {
activity.reportFragment.setProcessListener(initializationListener)
}
}
override fun onActivityPaused(activity: Activity) {
activityPaused()
}
override fun onActivityStopped(activity: Activity) {
activityStopped()
}
})
}
EmptyActivityLifecycleCallbacks為 Application.ActivityLifecycleCallbacks的實現(xiàn)類,內(nèi)部為空實現(xiàn)
內(nèi)部也維護了幾個變量,用來記錄 start 和 resume 的數(shù)量
private var startedCounter = 0
private var resumedCounter = 0
private var pauseSent = true
private var stopSent = true
在不同的時機調(diào)用
internal fun activityStarted() {
startedCounter++
if (startedCounter == 1 && stopSent) {
registry.handleLifecycleEvent(Lifecycle.Event.ON_START)
stopSent = false
}
}
internal fun activityResumed() {
resumedCounter++
if (resumedCounter == 1) {
if (pauseSent) {
registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
pauseSent = false
} else {
handler!!.removeCallbacks(delayedPauseRunnable)
}
}
}
internal fun activityPaused() {
resumedCounter--
if (resumedCounter == 0) {
handler!!.postDelayed(delayedPauseRunnable, TIMEOUT_MS)
}
}
internal fun activityStopped() {
startedCounter--
dispatchStopIfNeeded()
}
internal fun dispatchPauseIfNeeded() {
if (resumedCounter == 0) {
pauseSent = true
registry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
}
}
internal fun dispatchStopIfNeeded() {
if (startedCounter == 0 && pauseSent) {
registry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
stopSent = true
}
}
在 activityStarted 和 activityResumed 方法中對 這兩個數(shù)值進行 ++,同時更新 lifecycle 狀態(tài)
在 activityPaused 和 activityStopped 方法對這兩個數(shù)值進行 --
并且在 activityPaused 時做了個 700ms 的延時
@VisibleForTesting
internal const val TIMEOUT_MS: Long = 700 // mls
internal fun activityPaused() {
resumedCounter--
if (resumedCounter == 0) {
handler!!.postDelayed(delayedPauseRunnable, TIMEOUT_MS)
}
}
這里為什么要延時呢?
在測試的時候發(fā)現(xiàn)一個很奇怪的問題:
當(dāng)退出應(yīng)用又立馬切回來的時候,并不會調(diào)用onStop和onResume方法
反復(fù)試了好幾遍,終于找到了原因:就是這里的延時,當(dāng)切換的時間太短時,就監(jiān)聽不到了。
經(jīng)過思考,發(fā)現(xiàn):
我們先來看看這幾個場景:在 A-Activity 中打開 B-Activity,生命周期是這樣的:
A: onPause
B: onCreate
B: onStart
B: onResume
A: onStop
接著:關(guān)閉 B-Activity 返回 A-Activity
B: onPause
A: onRestart
A: onStart
A: onResume
B: onStop
B: onDestroy
再接著:從 App 返回桌面
A:onPause
A:onStop
ProcessLifecycleOwner 的原理其實是監(jiān)聽Activity的生命周期,當(dāng)你在 App內(nèi)部新開一個Activity或者銷毀Activity時,都會引起Activity調(diào)用onPause,onStop方法。
所以,當(dāng)ProcessLifecycleOwner接受到onPause,onStop這些事件時,并不知道是來自用戶退到后臺,還是來自app內(nèi)部的Activity變化。
因此,這里才設(shè)置了一個延時,如果是app內(nèi)部Activity變動,那么一個Activity的onPause必然會伴隨另一個Activity的onResume。
從前面的流程可以看出:不管什么情況,都會先調(diào)用onPause
如果太長時間沒有調(diào)用onResume,就認(rèn)為是退到后臺
如果很快onResume就被調(diào)用了,那就認(rèn)為這只是app內(nèi)部的activity在變化。這就是設(shè)置延時的作用。