Android - 현재 프래그먼트 가져오기 & 백 스택이 전부 지워지지 않는 문제 해결 (Kotlin)

업데이트:

  • 연구주제 : Android - 현재 프래그먼트 가져오기 & 백 스택이 전부 지워지지 않는 문제 해결 (Kotlin)
  • 연구목적 : 안드로이드에서의 코틀린 활용
  • 연구일시 : 2020년 04월 24일 09:00~17:00
  • 연구자 : 이재환 ljh951103@naver.com
  • 연구장비 : HP EliteDesk 800 G4 TWR, Kotlin, Android studio, IntelliJ
  • 관련연구 : Java, Android, Kotlin, BottomNavigationView, BackStack


서론

프래그먼트 백 스택 관리에 대한 내용을 여러번 포스팅 해왔습니다.

하지만 이번에도 한 가지 버그를 발견했는데 popBackStack 을 호출하면 이전 백 스택에 담긴 데이터들이 지워져야하지만 실제로 특정 프래그먼트를 중복으로 클릭한 후, 뒤로가기를 하게 되면 화면 뷰가 보이지 않는 버그가 발생했습니다. 이 버그를 해결하는 방법을 다루어 보겠습니다.

또, 새로운 기능을 구현해야 하는데 현재 화면에 보여지는 프래그먼트를 찾아서 가져와야할 필요가 있었습니다. 현재 보여지는 프래그먼트를 가져오는 부분까지 다루어 보도록 하겠습니다.


본론

현재 프래그먼트 가져오기

우선 현재 프래그먼트를 가져오는 방법에 대해 다루어 보겠습니다.

for(fragment: Fragment in supportFragmentManager.fragments) {
    if (fragment.isVisible) {
        // 코드 입력
    }
}

다음과 같이 프래그먼트를 찾기위해 for문으로 돌리면서 현재 보이는 프래그먼트를 찾으면 그 프래그먼트(fragment) 인스턴스로 작업을 하면 됩니다.


다음은 예시 코드입니다.
특정 이벤트를 수행하면 현재 프래그먼트를 가져와서 다시 프래그먼트를 호출하여 갱신하는 동작을 합니다.

for(fragment: Fragment in supportFragmentManager.fragments) {
    if (fragment.isVisible) {
        val tag = fragment.tag
        lateinit var frag: Fragment
        val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
        supportFragmentManager.popBackStackImmediate(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE)
        when(tag) {
            "name" -> {
                frag = NameFragment(appbar)
            }
            "tag" -> {
                frag = TagFragment(appbar)
            }
            "location" -> {
                frag = LocationFragment(appbar)
            }
        }
        transaction.replace(R.id.frame_layout, frag, tag)
        transaction.addToBackStack(tag)
        transaction.commit()
        transaction.isAddToBackStackAllowed
        break
    }
}


백 스택이 전부 지워지지 않는 문제

override fun onNavigationItemSelected(p0: MenuItem): Boolean {
        val fm = supportFragmentManager
        val transaction: FragmentTransaction = fm.beginTransaction()

        when(p0.itemId){
            R.id.menu_name -> {
                fm.popBackStack("name", FragmentManager.POP_BACK_STACK_INCLUSIVE)
                val fragmentA = NameFragment()
                transaction.replace(R.id.frame_layout,fragmentA, "name")
                transaction.addToBackStack("name")
            }
            R.id.menu_tag -> {
                fm.popBackStack("tag", FragmentManager.POP_BACK_STACK_INCLUSIVE)
                val fragmentB = TagFragment()
                transaction.replace(R.id.frame_layout,fragmentB, "tag")
                transaction.addToBackStack("tag")
            }
            R.id.menu_cal -> {
                fm.popBackStack("cal", FragmentManager.POP_BACK_STACK_INCLUSIVE)
                val fragmentC = DateFragment()
                transaction.replace(R.id.frame_layout,fragmentC, "cal")
                transaction.addToBackStack("cal")
            }
            R.id.menu_location -> {
                fm.popBackStack("location", FragmentManager.POP_BACK_STACK_INCLUSIVE)
                val fragmentD = LocationFragment()
                transaction.replace(R.id.frame_layout,fragmentD, "location")
                transaction.addToBackStack("location")
            }
        }
        transaction.commit()
        transaction.isAddToBackStackAllowed
        return true
    }

다음의 코드는 이전 포스팅에서 다룬적이 있습니다.
하지만 이렇게 작성하게되면 특정 프래그먼트를 중복해서 클릭할 경우, 이전의 프래그먼트가 제대로 지워지지않아서 호출되지 않는 버그가 생기게 됩니다.

이 버그는 popBackStack() 을 사용하지말고 popBackStackImmediate()을 사용하면 해당 버그를 해결할 수 있습니다.
그럼 이 두가지 함수는 어떤 차이가 있을까요?

간단하게 말하면 popBackStack()는 비동기식 이기 때문에 팝 요청을 큐에 넣지만 응용 프로그램이 이벤트 루프로 돌아올 때까지 작업이 수행되지 않습니다.

반면에 popBackStackImmediate()는 유사하지만 호출 내부에서 즉시 작업을 수행합니다.
이것은 나중에 executePendingTransactions ()를 호출하는 것과 같습니다.


다음의 코드는 popBackStackImmediate() 함수로 수정을 거친 코드입니다.
4가지 프래그먼트에 대한 백스택을 관리해줍니다.

override fun onNavigationItemSelected(p0: MenuItem): Boolean {
        val fm = supportFragmentManager
        val transaction: FragmentTransaction = fm.beginTransaction()

        when(p0.itemId){
            R.id.menu_name -> {
                fm.popBackStackImmediate("name", FragmentManager.POP_BACK_STACK_INCLUSIVE)
                val fragmentA = NameFragment(appbar)
                transaction.replace(R.id.frame_layout,fragmentA, "name")
                transaction.addToBackStack("name")
            }
            R.id.menu_tag -> {
                fm.popBackStackImmediate("tag", FragmentManager.POP_BACK_STACK_INCLUSIVE)
                val fragmentB = TagFragment(appbar)
                transaction.replace(R.id.frame_layout,fragmentB, "tag")
                transaction.addToBackStack("tag")
            }
            R.id.menu_cal -> {
                fm.popBackStackImmediate("cal", FragmentManager.POP_BACK_STACK_INCLUSIVE)
                val fragmentC = DateFragment(appbar)
                transaction.replace(R.id.frame_layout,fragmentC, "cal")
                transaction.addToBackStack("cal")
            }
            R.id.menu_location -> {
                fm.popBackStackImmediate("location", FragmentManager.POP_BACK_STACK_INCLUSIVE)
                val fragmentD = LocationFragment(appbar)
                transaction.replace(R.id.frame_layout,fragmentD, "location")
                transaction.addToBackStack("location")
            }
        }
        //transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
        transaction.commit()
        transaction.isAddToBackStackAllowed
        return true
    }


결론

지금의 백 스택 관리에 대해 다뤄보았습니다.
지속적으로 버그가 발생하는 것을 보면 앞으로도 많은 테스팅을 거쳐야 할 것 같습니다.
백 스택에 관련된 다른 함수들은 다른 포스팅에 작성되어있으니 그 포스팅을 참고해주시면 감사하겠습니다.


향후과제


참고자료

https://binsolb.tistory.com/entry/popBackStack-vs-popBackStackImmediate


Writer: Jae-Hwan Lee

댓글남기기