iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 28
1
Mobile Development

程式初學:Android與Kotlin系列 第 28

Day 28--Complex lifecycle situations

  • 分享至 

  • xImage
  •  

在範例Dessert App中利用timer來觀察更複雜的生命週期

Timer

專案中的class DessertTimer有一個startTime()與stopTimer()

class DessertTimer {

    // The number of seconds counted since the timer started
    var secondsCount = 0

    private var handler = Handler()
    private lateinit var runnable: Runnable
    
    fun startTimer() {...}
    fun stopTimer() {...}
}

藉由Runnable與Handler以後台程序執行startTime()時
執行時會log每秒印出 Timer is at : $secondsCount

在MainActivity宣告一個變數

private lateinit var dessertTimer : DessertTimer

並在onCreate()中實例化

dessertTimer = DessertTimer()

Start and stop the timer

Activity生命週期

可以看出在activity顯現前,就會呼叫onStart(),而activity不可見之後,會呼叫onStop()
在這二個地方加入startTimer()與stopTimer()

override fun onStart() {
        super.onStart()
        dessertTimer.startTimer()
        Timber.i("onStart Called")
    }

override fun onStop(){
        super.onStop()
        dessertTimer.stopTimer()
        Timber.i("onStop Called")
    }
  • 執行app後可以看到onStart()被呼叫以後,Timer就開始執行

  • 按分享鍵時,會使activity進入onPause(),但並未onStop()
    所以Timer()仍會繼續

  • 按Home鍵到桌面,此時app進入onStop(),再使用最近任務將app叫回
    又會進入onStart(),Timer也繼續執行

  • 若使用Back鍵退出,或完整的結束,app會進入onDestory()
    再次執行app,Timer()將會重新計數

  • 若將onStop()中的stopTimer()註解掉,且未完整結束/退出app
    則app即使未顯示於螢幕,Timer()也會持續在後台執行與使用系統資源
    這是一種memory leak

  • 若dessertTimer.startTimer()置於onCreate()執行
    則app從後台回到前台時,Timer()不會執行,因爲onCreate()在 app被onDestroy()只會執行一次

  • 本段的重點爲:若在生命週期中設置了某些可能會一直使用資源的程式
    同時也要注意什麼時候該設置結束的程式

lifecycle library

當一個app中有許多類似Timer()這樣的程式須要注意何時結束時
可以使用lifecycle library
如上一段的例子,通常是activity在各個生命週期發生時,設置程式該做什麼,如startTimer(),stopTimer()

但使用lifecycle library則反過來,讓Timer()自我監控當生命週期改變時,該做什麼

lifecycle library主要有三個部分

  • Lifecycle owners:(持有生命週期的組件)的擁有者,如activity,fragment,實作LifecycleOwner interface
  • Lifecycle class:持有Lifecycle owners的狀態,並在生命週期發生變化時觸發事件
  • Lifecycle observers:觀察生命週期的變化並執行對應動作,實作LifecycleObserver interface

啓用lifecycle library

Lifecycle observer

在此範例app中,DessertTimer就是Lifecycle observers
因此,讓DessertTimer實作LifecycleObserver interface
得以觀察activity的生命週期而自動進行對應動作

class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {
...
}

改寫的class DessertTimer包含二件事情

  • 建構子lifecycle實例Lifecycle物件,爲Timer要觀察的對象
  • 實作LifecycleObserver interface

再用lifecycle呼叫addObserver()將DessertTimer這個class加到觀察者,並初始化

實例化DessertTimer的時候,Lifecycle owner(MainActivity)將傳入本身的lifecycle
所以這個動作就連結了MainActivity與DessertTimer

init {
   lifecycle.addObserver(this)
}

接著在對應生命週期發生時,所要執行的方法前加上annotation
@OnLifecycleEvent
並將對應的生命週期事件作爲參數傳入
如圖,屬於activity的生命週期皆可以被觀察

因此我們要在activity的生命週期

  • 在onStart()時執行startTimer()
  • 在onStop()時執行時投票TImer()
    就加上annotation
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {...}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer() {...}

Lifecycle owner , Lifecycle class

MainActivity就是本app的Lifecycle owner
而MainActivity繼承自上層的FragmentActivity已實作了LifecycleOwner

再來是將MainActivity自身的lifecycle狀態(Lifecycle class)

傳給DessertTimer的建構子

dessertTimer = DessertTimer(this.lifecycle) //this = 當前的MainActivity

至此就完成了DessertTimer對MainActivity的觀察
當MainActivity的生命週期改變時
DessertTimer即會依照設置的annotation,執行對應的方法

所以原先MainActivity onStart(),onStop()中執行的startTimer()與stopTimer()都不需要了

執行app,檢查timer print log的動作應皆相同

app shutdown

app不在當前畫面,剛進入後台時,還不會呼叫onDestroy()
等待使用者隨時可快速的返回app

但android管理所有資源,可能因爲app久未被使用或資源不夠
而將已進入後台的app完全關閉

以下用adb模擬android關閉app的情況(android api版本須爲28以上)

Android Debug Bridge (adb)

開啓Terminal,輸入adb,正常要看到Android Debug Bridge version X.XX.X
出現如圖這樣表示沒找到adb執行程式

參考官方解決方式:Windows

找自己的Android SDK Location爲 C:\Users\user\AppData\Local\Android\Sdk\

但實際上adb.exe在platform-tools資料夾內

所以路徑是
C:\Users\user\AppData\Local\Android\Sdk\platform-tools

再依照第二步,將SDK路徑加入系統變數中

進階系統設定->環境變數->系統變數

執行adb,看到版本號了

執行app並累積一些金額

按Home,將app收到後台

開啓Terminal,輸入adb shell am kill com.example.android.dessertclicker
此指令功能爲將後台的app強制結束

再將app調回前台顯示,原本從後台回到前台
預期app的內容應該是與進後台前相同

但被強制結束的app,可以回到前台之後,內容都被清空了
app的生命週期也從onCreate()開始執行,timer從0開始

android系統會自動保存已設置ID之view的狀態
儘量於app恢復執行時,顯示之前的狀態
但有些程式中的變數,android系統不認得
因此若須要保存的話,可用onSaveInstanceState()包裝起來


上一篇
Day 27--Lifecycles and logging(下)
下一篇
Day 29--savedInstanceState狀態保存,讀取Google Sheet
系列文
程式初學:Android與Kotlin30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言