๐ ์ด๋ฌํ ํจํด์ ์ฝ๋์ ๊ตฌ์ฑ์ ๋์๊ฒํ๊ณ ์ค๋ฅ๊ฐ ์ฆ๊ฐํ๋ ์์ธ์ด ๋จ.
Lifecycle-aware ์ปดํฌ๋ํธ๋ฅผ ์ด์ฉํ๋ฉด ์ข
์ ์ปดํฌ๋ํธ์ ์ฝ๋๋ฅผ ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋ ๋ฐ์ผ๋ก ์ด๋์ํฌ ์ ์์.
Lifecycle-aware ์ปดํฌ๋ํธ๋ ์กํฐ๋นํฐ ๋ฐ ํ๋๊ทธ๋จผํธ์ ๋ผ์ดํ์ฌ์ดํด ์ํ ๋ณํ์ ๋ฐ๋ผ ์์
์ ์ํํจ. ์ด๋ฌํ ์ปดํฌ๋ํธ๋ค์ ๋ ๊ฐ๋ณ๊ณ ์กฐ์งํ๋ ์ฝ๋๋ฅผ ์์ฐํ ์ ์๊ฒ ํด์ฃผ๊ณ ๊ด๋ฆฌ๋ฅผ ์ฝ๊ฒ ํด์ค.
internal class MyLocationListener(
private val context: Context,
private val callback: (Location) -> Unit
) {
fun start() {
// connect to system location service
}
fun stop() {
// disconnect from system location service
}
}
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this) { location ->
// update UI
}
}
public override fun onStart() {
super.onStart()
myLocationListener.start()
// manage other components that need to respond
// to the activity lifecycle
}
public override fun onStop() {
super.onStop()
myLocationListener.stop()
// manage other components that need to respond
// to the activity lifecycle
}
}
์์ ์ฝ๋๋ ๋ฌธ์ ๊ฐ ์์ด๋ณด์ด์ง๋ง ๋ผ์ดํ์ฌ์ดํด์ ํ์ฌ ์ํ์ ๋ฐ๋ผ UI ๋ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๊ด๋ฆฌํ๋ ํธ์ถ์ด ๋๋ฌด ๋ง์์ง๋ ๋ฌธ์ ๊ฐ ๋ฐ์.
์ฌ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ๊ด๋ฆฌํ ๊ฒฝ์ฐ onStart()
๋ฐ onStop()
๊ณผ ๊ฐ์ ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋์ ์๋นํ ์์ ์ฝ๋๋ฅผ ์์ฑํ๊ฒ ๋์ด ์ ์ง๋ณด์๊ฐ ์ด๋ ค์์ง.
์ปดํฌ๋ํธ๊ฐ ์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ๊ฐ ์ค์ง๋๊ธฐ ์ ์ ์์๋๋ค๋ ๋ณด์ฅ์ด ์์. (์๋ฅผ ๋ค์ด onStart()
์์ ์ค์ ํ์ธ๊ณผ ๊ฐ์ ๊ธด ์์
์ ์ํํด์ผ ํ ๊ฒฝ์ฐ)
onStop()
๋ฉ์๋๊ฐ onStart()
๋ฉ์๋๋ณด๋ค ๋จผ์ ์๋ฃ๋์ด ์ปดํฌ๋ํธ๊ฐ ํ์ ์ด์์ผ๋ก ์ค๋ ์ ์ง๋๋ ๊ฒฝ์์ํ(race condition)๊ฐ ๋ฐ์ํ๊ฒ ๋จ.
๊ฐ์ ๋ ์ฝ๋
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this) { location ->
// update UI
}
}
public override fun onStart() {
super.onStart()
Util.checkUserStatus { result ->
// what if this callback is invoked AFTER activity is stopped?
if (result) {
myLocationListener.start()
}
}
}
public override fun onStop() {
super.onStop()
myLocationListener.stop()
}
}
๋ผ์ดํ ์ฌ์ดํด์ ์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ์ ๊ฐ์ ์ปดํฌ๋ํธ์ ์๋ช ์ฃผ๊ธฐ ์ํ์ ๋ํ ์ ๋ณด๋ฅผ ๋ณด์ ํ๊ณ ๋ค๋ฅธ ๊ฐ์ฒด๋ค์ด ์ด ์ํ๋ฅผ ๊ด์ฐฐํ ์ ์๋๋ก ํด์ฃผ๋ ํด๋์ค.
Event
ํ๋ ์์ํฌ์ ๋ผ์ดํ์ฌ์ดํด ํด๋์ค์์ ์ ๋ฌ๋๋ ๋ผ์ดํ์ฌ์ดํด ์ด๋ฒคํธ. ์ด๋ฒคํธ๋ ์กํฐ๋นํฐ์ ํ๋๊ทธ๋จผํธ์ ์ฝ๋ฐฑ ์ด๋ฒคํธ์ ๋งคํ๋จ.
State
๋ผ์ดํ์ฌ์ดํด ๊ฐ์ฒด์ ์ํด ์ถ์ ๋๋ ๊ตฌ์ฑ์์์ ํ์ฌ ์ํ.
์ ๊ทธ๋ฆผ์์ State๋ ๊ทธ๋ํ์ ๋ ธ๋, Event๋ ๋ ธ๋ ์ฌ์ด์ ์ฃ์ง.
class MyObserver : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
connect()
}
override fun onPause(owner: LifecycleOwner) {
disconnect()
}
}
myLifecycleOwner.getLifecycle().addObserver(MyObserver())
DefaultLifecycleObserver๋ฅผ ๊ตฌํํ๊ณ , onCreate onStart ๋ฑ๊ณผ ๊ฐ์ ๋งค์๋๋ฅผ ์ฌ์ ์ํ์ฌ ์ปดํฌ๋ํธ ๋ผ์ดํ์ฌ์ดํด ์ํ๋ฅผ ๋ชจ๋ํฐ๋ง ํ ์ ์์.
Lifecycle ํด๋์ค์ addObserver()
๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์ต์ ๋ฒ ์ธ์คํด์ค๋ฅผ ์ ๋ฌํ์ฌ ์ต์ ๋ฒ๋ฅผ ์ถ๊ฐํ ์ ์์.
LifecycleOwner๋ ํด๋น ํด๋์ค๊ฐ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ ๊ฒ์ ๋ํ๋ด๋ ๋จ์ผ ๋ฉ์๋ ์ธํฐํ์ด์ค.
getLifecycle()
์ด๋ผ๋ ํ๋์ ๋ฉ์๋๋ฅผ ๊ฐ์ง๋ฉฐ ํด๋น ํด๋์ค์์ ๊ตฌํ๋์ด์ผ ํจ.
Fragment ๋ฐ AppCompatActivity์ ๊ฐ์ ๊ฐ๋ณ ํด๋์ค๋ก๋ถํฐ ๋ผ์ดํ์ฌ์ดํด ์์ ๊ถ์ ์ถ์ํํ๊ณ ์ด์ ์๋ํ๋ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ ์ ์๊ฒ ํจ.
owner๋ ๋ผ์ดํ์ฌ์ดํด์ ์ ๊ณตํ๊ณ observer๋ ์ด๋ฅผ ๋ฑ๋กํ์ฌ ๊ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ DefaultLifecycleObserver๋ฅผ ๊ตฌํํ๋ ์ปดํฌ๋ํธ๋ LifecycleOwner๋ฅผ ๊ตฌํํ๋ ์ปดํฌ๋ํธ์ ์ํํ๊ฒ ์๋ํจ.
โญ๏ธ ์์ - ์์น ์ถ์ ์์
MyLocationListener ํด๋์ค๋ฅผ DefaultLifecycleObserver๋ฅผ ๊ตฌํํ๋๋ก ๋ง๋ค๊ณ onCreate()
๋ฉ์๋์์ ์กํฐ๋นํฐ ๋ผ์ดํ์ฌ์ดํด์ ์ด๊ธฐํํ๋๋ก ํจ.
๐ ๋ผ์ดํ์ฌ์ดํด ์ํ๋ณ๊ฒฝ์ ๋ํ ๋ฐ์ ๋ก์ง์ด ์กํฐ๋นํฐ๊ฐ ์๋ MyLocationListener์ ์ ์ธ๋จ. ๊ฐ๋ณ ์ปดํฌ๋ํธ๊ฐ ์์ฒด ๋ก์ง์ ์ ์ฅํ๋๋ก ํ๋ ๊ฒ์ด ์กํฐ๋นํฐ์ ํ๋๊ทธ๋จผํธ ๋ก์ง์ ๊ด๋ฆฌํ๊ธฐ ์ฌ์.
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this, lifecycle) { location ->
// update UI
}
Util.checkUserStatus { result ->
if (result) {
myLocationListener.enable()
}
}
}
}
์ผ๋ฐ์ ์ผ๋ก ํ์ฌ ๋ผ์ดํ์ฌ์ดํด ์ํ๊ฐ ์ข์ง ์์ ๊ฒฝ์ฐ ํน์ ์ฝ๋ฐฑ์ ํธ์ถํ์ง ์๋๋ก ํจ.
-> ์: ์กํฐ๋นํฐ ์ํ๊ฐ ์ ์ฅ๋ ํ(onSaveInstanceState) ํ๋๊ทธ๋จผํธ ํธ๋์ญ์
์ ์คํํ๋ ์ฝ๋ฐฑ์ด ์คํ๋๋ค๋ฉด ์ถฉ๋์ด ์ผ์ด๋ ์ ์์.
์์ ์ผ์ด์ค๋ฅผ ๊ฐ๋จํ๊ฒ ๋ง๋ค๊ธฐ ์ํด Lifecycle ํด๋์ค๋ ๋ค๋ฅธ ๊ฐ์ฒด๊ฐ ํ์ฌ ์ํ๋ฅผ ์กฐํํ ์ ์๋๋ก ํ์ฉ.
internal class MyLocationListener(
private val context: Context,
private val lifecycle: Lifecycle,
private val callback: (Location) -> Unit
): DefaultLifecycleObserver {
private var enabled = false
override fun onStart(owner: LifecycleOwner) {
if (enabled) {
// connect
}
}
fun enable() {
enabled = true
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
// connect if not connected
}
}
override fun onStop(owner: LifecycleOwner) {
// disconnect if connected
}
}
๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋๋ก์ด๋ ์๋ช ์ฃผ๊ธฐ์ ํจ๊ป ๋์ํด์ผ ํ๋ ํด๋์ค๋ฅผ ์ ๊ณตํ๋ค๋ฉด lifecylce-aware-components ์ฌ์ฉ์ ๊ถ์ฅ.
LifecylceOwner๋ก ๋ง๋ค๊ณ ์ถ์ ์ปค์คํ ํด๋์ค๊ฐ ์๋ค๋ฉด LifecycleRegistry ํด๋์ค๋ฅผ ์ฌ์ฉํ ์ ์์ง๋ง, ์ด๋ฒคํธ๋ฅผ ํด๋น ํด๋์ค๋ก ์ ๋ฌํด์ผ ํจ.
class MyActivity : Activity(), LifecycleOwner {
private lateinit var lifecycleRegistry: LifecycleRegistry
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleRegistry = LifecycleRegistry(this)
lifecycleRegistry.markState(Lifecycle.State.CREATED)
}
public override fun onStart() {
super.onStart()
lifecycleRegistry.markState(Lifecycle.State.STARTED)
}
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
}
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
class CustomLifecycleComponent : LifecycleOwner {
private val lifecycleRegistry = LifecycleRegistry(this)
init {
// Initialize the lifecycle to CREATED state
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
fun start() {
// Simulate starting the component
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
}
fun stop() {
// Simulate stopping the component
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
}
fun destroy() {
// Simulate destroying the component
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
}
UI ์ปจํธ๋กค๋ฌ(์กํฐ๋นํฐ, ํ๋๊ทธ๋จผํธ)๋ฅผ ์ต๋ํ ๊ฐ๋ณ๊ฒ ์ ์ง. ๋ฐ์ดํฐ๋ฅผ ํ๋ํ๋ ค๊ณ ํ์ง ์์์ผ ํ๋ฉฐ, ๋์ ๋ฐ์ดํฐ๋ฅผ ํ๋ํ๋๋ฐ ViewModel์ ์ฌ์ฉํ๊ณ LiveData ๊ฐ์ฒด๋ฅผ ๊ด์ฐฐํ์ฌ ๋ณ๊ฒฝ์ฌํญ์ ๋ทฐ์ ๋ฐ์ํด์ผ ํจ.
๋ฐ์ดํฐ ์ฃผ๋ํ UI๋ฅผ ์์ฑํ๋ ค๊ณ ํ ๊ฒ. UI ์ปจํธ๋กค๋ฌ์ ์ญํ ์ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋ ๋ทฐ๋ฅผ ์ ๋ฐ์ดํธํ๊ฑฐ๋ ์ฌ์ฉ์ ์์ ์ ViewModel๋ก ์๋ฆฌ๋ ๊ฒ.
๋ฐ์ดํฐ ๋ก์ง์ ViewModel ํด๋์ค์ ๋ ๊ฒ. ViewModel์ UI ์ปจํธ๋กค๋ฌ์ ์ฑ์ ๋๋จธ์ง ๋ถ๋ถ ๊ฐ์ ์ฐ๊ฒฐ ์ญํ ์ ํด์ผ ํจ. ํ์ง๋ง ViewModel์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ(์๋ฅผ ๋ค์ด ๋คํธ์ํฌ๋ก๋ถํฐ)์ ๋ํ ์ฑ ์์ ์์. ViewModel์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ์ ์ ํ ์ปดํฌ๋ํธ๋ฅผ ํธ์ถํ ๋ค์ ๊ฒฐ๊ณผ๋ฅผ UI ์ปจํธ๋กค๋ฌ์ ์ ๊ณต.
๋ทฐ์ UI ์ปจํธ๋กค๋ฌ๊ฐ์ ๊น๋ํ ์ธํฐํ์ด์ค ์ ์ง๋ฅผ ์ํด Data Binding์ ์ฌ์ฉํ ๊ฒ. ๋ทฐ๋ฅผ ๋ณด๋ค ์ ์ธ์ ์ผ๋ก ๋ง๋ค๊ณ ์กํฐ๋นํฐ์ ํ๋๊ทธ๋จผํธ์์ ์์ฑํด์ผํ๋ ์
๋ฐ์ดํธ ์ฝ๋๋ฅผ ์ต์ํํ ์ ์์.
Java๋ก๋ Butter Knife ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ.
๋ง์ฝ UI๊ฐ ๋ณต์กํ๋ค๋ฉด UI ์์ ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ํ๋ ์ ํฐ ํด๋์ค๋ฅผ ์์ฑ. UI ์ปดํฌ๋ํธ๋ฅผ ํ ์คํธํ๊ธฐ ์ฌ์์ง.
ViewModel์์ View ๋๋ ์กํฐ๋นํฐ ์ปจํ ์คํธ๋ฅผ ์ฐธ์กฐํ์ง ์๋๋ก ํด์ผํจ. ViewModel์ด ์กํฐ๋นํฐ ์๋ช ๋ณด๋ค ๊ธธ๋ฉด(์๋ฅผ ๋ค์ด ํ๋ฉด ํ์ ๊ฐ์ ๊ตฌ์ฑ ๋ณ๊ฒฝ.) ์กํฐ๋นํฐ๊ฐ ๊ฐ๋น์ง ์ปฌ๋ ํฐ์ ์ํด ์ ๋๋ก ํ๊ธฐ๋์ง ์์ ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ๋ ์ ์์.
๊ธด ์คํ ์์ ๋ฐ ๋ค๋ฅธ ๋น๋๊ธฐ ์์ ๊ด๋ฆฌ๋ฅผ ์ํด ์ฝ๋ฃจํด์ ์ฌ์ฉํ ๊ฒ.
๋๋ต์ ์ธ(coarse-grained) ์์น์ ์ธ๋ถํ ๋(fined-grained) ์์น ์
๋ฐ์ดํธ ์ฌ์ด์ ์ ํ ์์ ์ฌ์ฉ.
๋ผ์ดํ ์ฌ์ดํด ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ ์์น ์ฑ์ด ํ๋ฉด์ ํ์๋๋ ๋์ ์ธ๋ถํ๋ ์์น ์
๋ฐ์ดํธ๋ฅผ ํ์ฑํํ๊ณ , ์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ์ด๋ํ ๋ ๋๋ต์ ์ธ ์์น ์
๋ฐ์ดํธ๋ก ์ ํ.
LiveData, lifecycle-aware ์ปดํฌ๋ํธ๋ก ์ฌ์ฉ์๊ฐ ์์น๋ฅผ ๋ณ๊ฒฝํ ๋ ์ฑ์ด UI๋ฅผ ์๋์ผ๋ก ์
๋ํ๋๋ก ํจ.
๋น๋์ค ๋ฒํผ๋ง ์ค์ง ๋ฐ ์์ ์์ ์ฌ์ฉ. lifecycle-aware ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฒํผ๋ง์ ๊ฐ๋ฅํ ๋นจ๋ฆฌ ์์ํ๊ณ ์ฑ์ด ์์ ํ ์์ํ ๋๊น์ง ์ฌ์ ์ฐ๊ธฐ. ์ฑ์ด ์ข ๋ฃ๋ ๋ ๋ฒํผ๋ง์ ๋นจ๋ฆฌ ์ค๋จํ๋๋ฐ์๋ ์ฌ์ฉ๋จ.
๋คํธ์ํฌ ์ฐ๊ฒฐ์ ์์ ๋ฐ ์ค์ง์ ์ฌ์ฉ. lifecycle-aware ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ์ด ํฌ๊ทธ๋ผ์ด๋์ ์์ ๋ ๋คํธ์ํฌ ๋ฐ์ดํฐ ๋ผ์ด๋ธ ์ ๋ฐ์ดํธ(์คํธ๋ฆฌ๋ฐ)์ ํ์ฑํ, ์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ์ ํ๋ ๋ ์๋์ผ๋ก ์ผ์์ค์ง๋๋๋ก ํจ.
์ ๋๋ฉ์ด์ drawable์ ์ผ์ ์ค์ง ๋ฐ ์ฌ๊ฐ. lifecycle-aware ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์ ์์ ๋ ์ ๋๋ฉ์ด์ drawable์ ์ผ์์ค์งํ๊ณ ์ฑ์ด ๋ค์ ํฌ๊ทธ๋ผ์ด๋๋ก ์ ํ๋๋ฉด drwable์ ์ฌ๊ฐํ๋๋ฐ ์ฌ์ฉํจ.