최근에 간단하게 개발하면서 Fragment -> Activity로 값을 전달하는 경우가 있었다. Activity -> Fragment 방향으로 데이터를 전달할 때는 Bundle 객체를 사용하여 값을 담아서 전달한다. 그렇다면 반대의 경우는 어떻게 전달할 수 있을까??

1) Interface Use

  • 가장 널리 사용되는 방법 중 하나일 것이다.

1. Interface 생성
- Interface를 생성하고 구현할 콜백을 정의한다.
- 주로, Fragment 안에 생성한다. (밖에 생성해도 상관 없다.)

1
2
3
interface OnFilterListenter{
fun setFilter(count: Int)
}

2. Activity에서 Interface 구현
- Activity에서 1번에서 생성한 인터페이스를 구현한다.
- Fragment에서 count 값을 받아 total 변수에 저장한다.

1
2
3
4
5
6
7
class ShopActivity : BaseActivity<ActivityShopBinding>(), OnFilterListenter {

private var total = 0
override fun setFilter(count: Int){
total = count
}
}

3. Fragment 구현
- Activity는 우리가 만든 Interface를 구현했으므로, Fragment에서 접근할 수 있는 Context를 해당 Interface로 캐스팅하여 접근할 수 있다.
- onAttach()에서 context에 접근할 수 있고, 이 리스너를 변수에 담아서 사용하면 된다.
- 주의할 점은 detach 되는 시점에 null로 초기화하여 메모리 누수를 방지해야 한다.

1
2
3
4
5
6
7
8
9
10
11
private var listener : OnFilterListenter? = null

override fun onAttach(context: Context) {
super.onAttach(context)
if (context as OnFilterListenter) listener = context
}

override fun onDetach() {
super.onDetach()
listener = null
}
  • 이제 이 리스너를 이요해서 Activity에서 구현한 메소드를 호출하여 Activity 쪽으로 데이터를 전달하게 된다.

2) ViewModel Use

  • 필자는 MVVM 구조를 사용하고 있었고, bottomSheetDialogFragment를 이용하여 필터 설정을 구현했다.
  • Activity와 필터 값을 공유할 필요가 있었고, 결국 필터 화면은 Activity의 일부로 판단했다. 따라서 Activity, Fragment의 ViewModel을 공유하는 것이 가장 적합한 방법으로 보였다.
  • Koin을 사용해 의존성을 주입하고 ViewModel 또한 주입하고 있으므로 Koin을 사용하여 해결할 수 있었다.

상황

  • Activity의 ViewModel을 Fragment와 공유하여 사용한다.

In Activity

1
private val viewModel: ShopViewModel by viewModel()
  • ShopViewModel은 Shop에 대한 정보 처리와 Filter 값을 관리한다.
  • Activity에서 ViewModel을 주입받는다.

In Fragment created from Activity

1
private val shopViewModel: ShopViewModel by sharedViewModel()
  • Fragment에서는 이 ViewModel을 공유하여 필터 값을 Activity와 공유한다.
  • by viewModel이 아닌 by sharedViewModel을 사용함으로써 해당 뷰모델의 생성은 두번 되지 않고 Activity에서 생성한 뷰모델을 공유해서 사용한다.
  • by viewModel을 통해 생성하면 새로운 ViewModel 객체가 생성된다.

Reference