본문 바로가기

카테고리 없음

[Android] Fragment에서 Coroutine의 사용

Flow를 사용하여 Fragment의 View를 업데이트하게 되면

Coroutine을 이용하여 해당 값을 관찰하는 코드를 작성하게 된다.

 

테스트를 하기 위해서 아래와 같이 작성해 주도록 하자.

먼저 Activity이다.

class MainActivity : AppCompatActivity() {
    
    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        with(binding) {
            button1.setOnClickListener {
                setFragment(FirstFragment())
            }
            button2.setOnClickListener {
                setFragment(SecondFragment())
            }
        }
    }

    private fun setFragment(fragment: Fragment) {
        supportFragmentManager.commit {
            replace(binding.frameLayout.id, fragment)
            setReorderingAllowed(true)
            addToBackStack("")
        }
    }
}

 

엑티비티에 FrameLayout을 하나 주고, 해당 Layout에 button을 누르는 것으로

fragment1, fragment2를 올리는 코드이다.

 

두번째로는 ViewModel 이다.

class MainViewModel: ViewModel() {

    private var count = 0

    private val _countObserve = MutableStateFlow(0)
    val countObserve = _countObserve.asStateFlow()

    init {
        viewModelScope.launch {
            while(true) {
                _countObserve.value = ++count
                delay(500)
            }
        }
    }
}

 

정수값을 받는 countObserve stateflow를 작성해 주고

값을 emit 하고 0.5초를 대기하는 작업을 반복하도록 한다.

 

세번째로는 Fragment이다.

class FirstFragment : Fragment() {

    private val TAG = "FirstFragment"

    private val binding by lazy { FragmentFirstBinding.inflate(layoutInflater) }
    private val viewModel: MainViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View = binding.root

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val job1 = lifecycleScope.launch {
            viewModel.countObserve.collect {
                Log.e(TAG, "onViewCreated: job1 lambda: called", )
            }
        }

        val job2 = viewLifecycleOwner.lifecycleScope.launch {
            viewModel.countObserve.collect {
                Log.e(TAG, "onViewCreated: job2 lambda: called", )
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.e(TAG, "onDestroyView: called")
    }
}

 

Fragment가 onViewCreated에 진입하면 viewModel의 countObserve를 관찰하도록 하고,

한쪽은 lifecycleScope, 다른 한쪽은 viewLifecycleOwner.lifecycleScope를 통하여 작성해 주었다.

 

SecondFragment는 Fragment 틀만 만들어 주었으므로 생략하겠다.

 

이를 실행하여 Fragment1를 올리는 버튼을 누르면 다음과 같은 로그를 확인할 수 있다.

 

그리고 SecondFragment button을 클릭하여 Replace 하고 나서 로그를 확인해 보면 

위와 같다.

 

viewLifecycleOwner.lifecycleScope를 사용하는 경우는 Fragment의 lifecycle에 따라서

onDestoryView가 호출된 후에 로그를 더이상 보여주지 않는 반면에

lifecycleScope를 사용하는 경우는 계속해서 로그를 보여주었다.

 

 기본적으로 lifecycleScope는 호출하는 lifecycle과 함께 동작한다.

Activity, Fragment, ViewModel 등 lifecycle을 사용하는 대상의 lifecycle을 따른다는 것이다.

 

이때, lifecycleScope를 사용하면 Activity를 따른다. 

따라서, 위와 같은 테스트의 결과를 확인할 수 있었던 것이다.

 

viewLifecycleOwner는 fragment의 property로서 fragment의 view lifecycle을 따른다.

따라서, Fragment의 view가 파괴되었을 때 더 이상 로그를 보여주지 않았던 것이다.