-
Notifications
You must be signed in to change notification settings - Fork 2
Hilt와 TMap을 함께 사용할 때 발생했던 Context 이슈
fragment에서 activityViewModels()로 생성하는 viewModel과 viewModels()로 생성하는 viewModel 두개를 같이 사용할 시 아래와 같은 에러 발생
@AndroidEntryPoint
class MapFragment : Fragment(), MapHandler {
private var _binding: FragmentMapBinding? = null
private val binding get() = _binding!!
private val alarmViewModel: AlarmViewModel by activityViewModels()
private val placeSearchViewModel: PlaceSearchViewModel by activityViewModels()
private val mapViewModel: MapViewModel by viewModels()
JNI DETECTED ERROR IN APPLICATION: JNI NewLocalRef called with pending exception java.lang.ClassCastException:
dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper cannot be cast to android.app.Activity
처음에는 viewModels()를 붙이자 에러가 발생해서 activityViewModels()와 viewModels()를 같이 쓰면 안되나? 하고 의심을 했다.
하지만 내부코드를 들여다보며 공부한 결과, 그 문제가 아닌 Hilt와 TMap을 사용하면서 발생한 Context 이슈였다.
Fragment에 @AndroidEntryPoint
를 달아주면 Hilt_XxxFragment
클래스가 생성된다.
이것의 내부코드를 보면 getContext()
라는 함수가 존재한다.
@Override
public Context getContext() {
if (super.getContext() == null && !disableGetContextFix) {
return null;
}
initializeComponentContext();
return componentContext;
}
private void initializeComponentContext() {
if (componentContext == null) {
// Note: The LayoutInflater provided by this componentContext may be different from super Fragment's because we getting it from base context instead of cloning from the super Fragment's LayoutInflater.
componentContext = FragmentComponentManager.createContextWrapper(super.getContext(), this);
disableGetContextFix = FragmentGetContextFix.isFragmentGetContextFixDisabled(super.getContext());
}
}
이곳에서 initializeComponentContext()
를 호출한다.
해당 함수는 creatContextWrapper
를 통해 자체적인 context를 만들고 있다.
@AndroidEntryPoint
를 붙이고 아래 세개의 context들을 Log에 찍어보았다.
val context1 = requireContext()
val context2 = requireActivity()
val context3 = view?.context
context 1 : dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper@13ad174
context 2 : com.stop.MainActivity@fb2dde0
context 3 : dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper@ae5af93
requireActivity()
만 상위 액티비티의 context를 불러오고, requireContext()
와 view?.context
는 FragmentContextWrapper
를 가져온다.
현재 우리의 프로젝트에서 TMapView
를 사용하고 있다.
이를 생성하기 위해선 context
가 필요한데, 현재 requireContext
를 이용해서 넘겨주고 있다.
TMapView의 내부코드를 보면
OnUpdateMyPosition()
함수에서 TMapView.this.context
를 Activity로 캐스팅하고 있다.
public void OnUpdateMyPosition(VSMMapPoint vsmMapPoint) {
TMapView.this.tMapLayer.setRect();
((Activity)TMapView.this.context).runOnUiThread(new Runnable() {
public void run() {
TMapView.this.tMapLayer.setCallOutPosition();
}
});
}
MapFragment에서 context를 requireContext
로 넘겨 FragmentContextWrapper
가 context로 들어갔기 때문에FragmentContextWrapper cannot be cast to android.app.Activity
라는 에러가 발생한 것이다.
requireContext
가 아닌 requireActivity
를 context로 넘겨주면 해결된다!