最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android實(shí)用小技巧之利用Lifecycle寫出更好維護(hù)的代碼

 更新時(shí)間:2022年05月05日 17:09:00   作者:廚師小p  
lifecycle是一個(gè)類,用于存儲(chǔ)有關(guān)組件(如Activity或Fragment)的生命周期狀態(tài)的信息,并允許其他對(duì)象觀察此狀態(tài),下面這篇文章主要給大家介紹了關(guān)于Android實(shí)用小技巧之利用Lifecycle寫出更好維護(hù)的代碼的相關(guān)資料,需要的朋友可以參考下

前言

你是否在onStart()啟動(dòng)過某項(xiàng)任務(wù)卻忘記在onStop()中取消呢?人不是機(jī)器,難免會(huì)有錯(cuò)漏。就算老手不會(huì)犯錯(cuò),也不能保證新人不會(huì)。學(xué)會(huì)下面的小技巧,讓這種粗心成為不可能。

關(guān)于Lifecycle的源碼,已經(jīng)有很多大佬分析過。這篇文章的主旨是讓讀者對(duì)Lifecycle的使用場(chǎng)景有更多的體會(huì),這樣也能更好地理解源碼。先來看一個(gè)場(chǎng)景,然后一步一步優(yōu)化。

場(chǎng)景

假設(shè)我們有一個(gè)界面,模擬一個(gè)廚房。里面有灶臺(tái)和餐桌。要求每秒鐘翻炒一下,總共10秒。一種常規(guī)的實(shí)現(xiàn)如下:

class KitchenFragment : Fragment() {
    private var timer: CountDownTimer? = null
    override fun onResume() {
        ...
        timer = object : CountDownTimer(COOKING_TIME_IN_MILLIS, SECOND_IN_MILLIS) {
            override fun onTick(millisUntilFinished: Long) {
                // 翻炒
            }
            override fun onFinish() { 
                // 出鍋 
            }
        }
        timer.start()
    }
    
    override fun onPause() {
        timer?.cancel()
        ...
    }
    
    compaion object {
        private const val COOKING_TIME_IN_MILLIS = 10000L
    }
}

潛在問題:

  • 在別的地方實(shí)現(xiàn)類似的功能需要把很多重復(fù)代碼復(fù)制過去
  • 忘記cancel()可能會(huì)造成一系列的麻煩
  • 當(dāng)產(chǎn)品經(jīng)理突然提出要同時(shí)顛勺5秒以及擦桌子20秒,代碼會(huì)變得很長

優(yōu)化版本1

先解決第一個(gè)問題,把CountDownTimer放到一個(gè)單獨(dú)的class。

class KitchenFragment : Fragment() {
    private val timer: CountDownTimer? = null
    override fun onResume() {
        ...
        timer = MyCountDownTimer(onTickAction = { 翻炒 },onFinishAction = { 出鍋 })
        timer.start()
    }
    
    override fun onPause() {
        timer?.cancel()
        ...
    }
}

// MyCountDownTimer.kt
class MyCountDownTimer@JvmOverloads constuctor(
    millisUntilFinished: Long = DEFAULT_DURATION_IN_MILLIS,
    countDownInterval: LONG = SECOND_IN_MILLIS,
    private val onTickAction: () -> Unit,
    private val onFinishAction: () -> Unit = {}
) : CountDownTimer(millisUntilFinished, countDownInterval) {

    override fun onTick(millisUntilFinished: Long) {
        onTickAction.invoke()
    }
    
    override fun onFinish() {
        onFinishAction.invoke()
    }
    
    compaion object {
        private const val DEFAULT_DURATION_IN_MILLIS = 10000L
    }
}

需要復(fù)用時(shí),只需傳入需要改動(dòng)的參數(shù)/方法:

// NeighbourKitchenFragment.kt
class NeighbourKitchenFragment : Fragment() {
    private val timer: CountDownTimer? = null
    override fun onResume() {
        ...
        timer = MyCountDownTimer(onTickAction = { 翻炒 },onFinishAction = { 甩鍋 })
        timer.start()
    }
    
    override fun onPause() {
        timer?.cancel()
        ...
    }
}

復(fù)用起來好像方便了一點(diǎn),但是當(dāng)上面提到過的的問題3出現(xiàn)時(shí),代碼會(huì)變成:

class KitchenFragment : Fragment() {
    private val cookTimer1: CountDownTimer? = null
    private val cookTimer2: CountDownTimer? = null
    private val sweepTableTimer: CountDownTimer? = null
    override fun onResume() {
        ...
        cookTimer1 = MyCountDownTimer(onTickAction = { 翻炒 },onFinishAction = { 出鍋 })
        cookTimer1.start()
        
        cookTimer2 = MyCountDownTimer(millisUntilFinished = BRITAIN_SPOON_DURATION_IN_MILLIS,onTickAction = { 顛勺 })
        cookTimer2.start()
        
        sweepTableTimer = MyCountDownTimer(millisUntilFinished = SWEEP_TABLE_DURATION_IN_MILLIS,onTickAction = { 擦桌子 })
        sweepTableTimer.start()
    }
    
    override fun onPause() {
        cookTimer1?.cancel()
        cookTimer2?.cancel()
        sweepTableTimer?.cancel()
        ...
    }
    
    compaion object {
        private const val BRITAIN_SPOON_DURATION_IN_MILLIS = 5000L
        private const val SWEEP_TABLE_DURATION_IN_MILLIS = 20000L
    }
}

隨著需求增加,F(xiàn)ragment變得越來越長,也更難維護(hù)。同時(shí),當(dāng)在onResume中添加timer時(shí)被同事打斷,之后就有可能會(huì)忘記在onPause中cancel()。有沒有辦法解決這些問題呢?

接下來切入正題,讓我們看看Lifecycle能做什么。

優(yōu)化版本2

首先讓MyCountDownTimer實(shí)現(xiàn)DefaultLifecycleObserver,這樣它就是lifecycle-aware的了。這有什么用呢?有了這個(gè),MyCountDownTimer就能在fragment/activity生命周期發(fā)生變化的時(shí)候得到通知并在內(nèi)部處理cancel()等操作。

// MyCountDownTimer.kt
// Lifecycle-aware CountDownTimer
class MyCountDownTimer@JvmOverloads constuctor(
    millisUntilFinished: Long = DEFAULT_DURATION_IN_MILLIS,
    countDownInterval: LONG = SECOND_IN_MILLIS,
    private val onTickAction: () -> Unit,
    private val onFinishAction: () -> Unit = {}
) : CountDownTimer(millisUntilFinished, countDownInterval), DefaultLifecycleObserver {
    override fun onTick(millisUntilFinished: Long) {
        onTickAction.invoke()
    }
    
    override fun onFinish() {
        onFinishAction.invoke()
    }
    
    // onResume時(shí)自動(dòng)開始
    override fun onResume(owner: LifecycleOwner) {
        start()
    }
    
    // onPause時(shí)自動(dòng)取消
    override fun onPause(owner: LifecycleOwner) {
        cancel()
    }
    
    // onDestroy時(shí)停止觀察
    override fun onDestroy(owner: LifecycleOwner) {
        owner.lifecycle.removeObserver(this)
    }
    
    compaion object {
        private const val DEFAULT_DURATION_IN_MILLIS = 10000L
    }
}

上面例子中的KitchenFragment將會(huì)變成這樣:

class KitchenFragment : Fragment() {
    override fun onCreate() {
        ...
        initTimer()
    }
    
    private fun initTimer() {
        // 翻炒任務(wù)
        val timer1 = MyCountDownTimer(onTickAction = { 翻炒 },onFinishAction = { 出鍋 })
        // 顛勺任務(wù)
        val timer2 = MyCountDownTimer(millisUntilFinished = BRITAIN_SPOON_DURATION_IN_MILLIS,onTickAction = { 顛勺 })
        // 擦桌任務(wù)
        val timer3 = MyCountDownTimer(millisUntilFinished = SWEEP_TABLE_DURATION_IN_MILLIS,onTickAction = { 擦桌子 })
        
        viewLifecycleOwner.lifecycle.apply {
            addObserver(timer1)
            addObserver(timer2)
            addObserver(timer3)
        }
    }
    
    compaion object {
        private const val BRITAIN_SPOON_DURATION_IN_MILLIS = 5000L
        private const val SWEEP_TABLE_DURATION_IN_MILLIS = 20000L
    }
}

在Fragment中只需要專注于添加需要的功能,不用操心取消任務(wù)與停止觀察。既清爽又不容易犯錯(cuò)。

單元測(cè)試

因?yàn)檫壿嫶a都封裝在MyCountDownTimer,主要測(cè)試這個(gè)class就可以了。不需要給每一個(gè)使用MyCountDownTimer的Fragment都寫詳細(xì)的測(cè)試。

只需要mock一個(gè)LifecycleOwner就足夠,也不需要啟動(dòng)一個(gè)mock Fragment。

class MyCountDownTimerTest {
    private lateinit var timer: MyCountDownTimer
    private lateinit var lifeCycle: LifecycleRegistry
    @Before
    fun setUp() {
        val lifeCycleOwner: LifecycleOwner = mock(LifecycleOwner::class.java)
        lifeCycle = LifecycleRegistry(lifeCycleOwner)
        timer = MyCountDownTimer(onTickAction = { 翻炒 },onFinishAction = { 出鍋 })
        lifeCycle.addObserver(timer)

        lifeCycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    }

    @Test
    fun timerActionExecuted() {
        lifeCycle.markState(Lifecycle.State.RESUMED)
        // 檢測(cè)是否開始翻炒,出鍋
        ...
    }
}

總結(jié)

通過把重復(fù)的代碼和邏輯封裝在自定義的LifecycleObserver內(nèi)部,不僅可以給Activity/Fragment“瘦身”,防止忘記在onStop()/onDestroy()中收拾,還可以使復(fù)用代碼更加方便。同時(shí)也遵循設(shè)計(jì)模式,降低了Fragment與Timer之間的耦合度,讓代碼更好維護(hù)。

到此這篇關(guān)于Android實(shí)用小技巧之利用Lifecycle寫出更好維護(hù)的代碼的文章就介紹到這了,更多相關(guān)Android Lifecycle好維護(hù)的代碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺析Android中build.gradle的實(shí)用技巧

    淺析Android中build.gradle的實(shí)用技巧

    這篇文章主要介紹了淺析Android中build.gradle的實(shí)用技巧,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-03-03
  • Android Activity Results API代替onActivityResult處理頁面數(shù)據(jù)

    Android Activity Results API代替onActivityResul

    說到onActivityResult,我們已經(jīng)非常熟悉來,通過在A activity啟動(dòng)B activity并且傳入數(shù)據(jù)到B中,然后在A中通過onActivityResult來接收B中返回的數(shù)據(jù)。在最新的activity-ktx的beta版本中,谷歌已經(jīng)廢棄了onActivityResult
    2022-09-09
  • android 實(shí)現(xiàn)側(cè)邊彈窗特效代碼

    android 實(shí)現(xiàn)側(cè)邊彈窗特效代碼

    側(cè)邊彈窗是在左邊,需要定位好位置,實(shí)現(xiàn)原理其實(shí)就是進(jìn)出動(dòng)效,用位移加透明度效果來控制,下面通過代碼給大家介紹android 實(shí)現(xiàn)側(cè)邊彈窗,需要的朋友參考下吧
    2021-06-06
  • 詳解Android的網(wǎng)絡(luò)數(shù)據(jù)存儲(chǔ)

    詳解Android的網(wǎng)絡(luò)數(shù)據(jù)存儲(chǔ)

    LeanCloud是一種簡單高效的數(shù)據(jù)和文件存儲(chǔ)服務(wù),本文主要介紹了利用LeanCloud來進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)的存儲(chǔ)的實(shí)現(xiàn)方法。具有很好的參考價(jià)值,需要的朋友一起來看下吧
    2016-12-12
  • Android 6.0權(quán)限請(qǐng)求相關(guān)及權(quán)限分組方法

    Android 6.0權(quán)限請(qǐng)求相關(guān)及權(quán)限分組方法

    今天小編就為大家分享一篇Android 6.0權(quán)限請(qǐng)求相關(guān)及權(quán)限分組方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Android編程實(shí)現(xiàn)仿QQ發(fā)表說說,上傳照片及彈出框效果【附demo源碼下載】

    Android編程實(shí)現(xiàn)仿QQ發(fā)表說說,上傳照片及彈出框效果【附demo源碼下載】

    這篇文章主要介紹了Android編程實(shí)現(xiàn)仿QQ發(fā)表說說,上傳照片及彈出框效果,涉及Android動(dòng)畫特效的相關(guān)實(shí)現(xiàn)技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下
    2017-01-01
  • Android控件之ProgressBar用法實(shí)例分析

    Android控件之ProgressBar用法實(shí)例分析

    這篇文章主要介紹了Android控件之ProgressBar用法,以一個(gè)完整實(shí)例形式較為詳細(xì)的分析了ProgressBar控件操作進(jìn)度顯示的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • Android 通用型手電筒代碼

    Android 通用型手電筒代碼

    說到手機(jī)手電筒功能,很多人都是直接調(diào)用閃光燈,而本文給大家介紹的是用相機(jī)功能來實(shí)現(xiàn)的,有需要的小伙伴可以參考下。
    2015-06-06
  • Android基于HttpUrlConnection類的文件下載實(shí)例代碼

    Android基于HttpUrlConnection類的文件下載實(shí)例代碼

    本文通過實(shí)例代碼給大家介紹了Android基于HttpUrlConnection類的文件下載功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧
    2017-09-09
  • Android復(fù)選框CheckBox與開關(guān)按鈕Switch及單選按鈕RadioButton使用示例詳解

    Android復(fù)選框CheckBox與開關(guān)按鈕Switch及單選按鈕RadioButton使用示例詳解

    這篇文章主要介紹了Android復(fù)選框CheckBox與開關(guān)按鈕Switch及單選按鈕RadioButton使用示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-09-09

最新評(píng)論

玉林市| 尖扎县| 谷城县| 古丈县| 延长县| 伊春市| 富民县| 怀仁县| 台前县| 黄平县| 广丰县| 思南县| 巴塘县| 绵阳市| 建始县| 丰顺县| 临沂市| 伊吾县| 龙陵县| 民和| 来宾市| 康乐县| 临夏市| 冀州市| 云林县| 封开县| 萍乡市| 安溪县| 石景山区| 尼勒克县| 玛曲县| 平湖市| 万宁市| 彩票| 徐汇区| 茶陵县| 大港区| 龙岩市| 东安县| 稻城县| 山西省|