【JetPack】Lifecycle監(jiān)聽?wèi)?yīng)用前后臺切換

lifecycle-process.png

前言

最近開發(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", "進入后臺")
            }
        }))
    }
}

效果

ForegroundCallbacks.gif

方案二:使用 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")

            })
        }
    }
                                                              }

效果

onProcessLifecycle.gif

源碼解讀

核心代碼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()
            }
        })
    }

EmptyActivityLifecycleCallbacksApplication.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è)置延時的作用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容