Closed akrulec closed 9 months ago
Steps to reproduce the behavior: Open the App
Which app is this? The sample app?
Oh, sorry for a poor explanation. I was migrating from 1.0 to 2.0 version of the calendar in the app. Every once in a while when I open the app (my app), the app crashes and I see this error.
My XML:
<androidx.fragment.app.FragmentContainerView
android:id="@+id/week_calendar_fragment"
android:name="com.xxx.ui.today.calendar.WeekCalendarFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/banner"/>
<com.kizitonwose.calendar.view.WeekCalendarView
android:id="@+id/week_calendar_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/calendar_toolbar"
app:cv_dayViewResource="@layout/item_view_calendar_day"
app:cv_outDateStyle="endOfRow"
app:cv_orientation="horizontal"
app:cv_scrollPaged="true"
android:transitionGroup="true"
android:transitionName="@{@string/calendar_week_transition_name}"/>
Code in the Fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
_binding = FragmentCalendarWeekBinding.bind(view)
binding.weekCalendarView.apply {
// In case the user enters from the landscape, just take the smallest number.
dayWidth = min(dm.widthPixels, dm.heightPixels) / NUM_DAYS_TO_SHOW
daySize = DaySize.SeventhWidth
dayBinder = CVDayBinder()
}
val localDate = LocalDate.now()
binding.weekCalendarView.setup(
localDate.minusMonths(10), localDate.plusMonths(2), DayOfWeek.SUNDAY)
binding.weekCalendarView.doOnAttach { binding.weekCalendarView.scrollToDate(selectedDate) }
// Each time user scrolls back and forth, refresh the data.
binding.weekCalendarView.weekScrollListener =
object : WeekScrollListener {
override fun invoke(week: Week) {
todayViewModel.getHabitDays(week.days.first().date, week.days.last().date)
// Because Sunday is the first day, we need to % number of days.
// Otherwise it doesn't work correctly if the user selected a Sunday.
val dayOfWeek = (selectedDate.dayOfWeek.value % NUM_DAYS_TO_SHOW)
// Scroll to the same day in the previous/next week.
todayViewModel.setDate(week.days[dayOfWeek].date)
}
}
...
override fun onDestroyView() {
super.onDestroyView()
binding.weekCalendarView.adapter = null
_binding = null
}
/**
* Class representing Day view binder. It is responsible for binding [HabitDay] to the given day
* in the calendar.
*/
inner class CVDayBinder : WeekDayBinder<DayViewContainer> {
override fun bind(container: DayViewContainer, data: WeekDay) =
container.bind(data.date, selectedDate, null)
override fun create(view: View) =
DayViewContainer(
view, requireContext(), viewLifecycleOwner, todayViewModel, dayWidth) { date ->
selectNewDay(date)
}
}
The crash error only points to the errors in the kizitonwose part of the code. So not sure what part of my code would cause this. Thank you
This is because you remove the adapter which is managed internally and something from your code outlives the view lifecycle (could be scroll listener etc) and tries to access the calendar which causes the crash. You need to remove this binding.weekCalendarView.adapter = null
from your onDestroyView()
. If you must keep it, you need to set the internal layout manager to null
as well.
Understood, so if I want to keep it, scroll listener should be notified as well? I guess, chasing the leak canary leaks and removing/nullifying everything when the view is destroy is the reason why the adapter was set to null explicitly. Is there a better way of 'cleaning up' and making sure things don't outlive the view? Thank you
Maybe something like this could work:
binding.weekCalendarView.adapter = null
binding.weekCalendarView.layoutManager = null
binding.weekCalendarView.weekScrollListener = null
Hey @akrulec is this resolved?
I have tried a version where I only set binding to null in on destroy:
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
and I still see crashes, so I'm going to try a version now that clears all three objects before setting binding to null.
@akrulec Any updates on this?
Yes, sorry for late response. I removed everything from onDestroy, and there is no more leaks and no more crashes. Just nullifying the _binding works.
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
Library information:
Describe the bug
Error when opening the application which inflates this class:
java.lang.NullPointerException: null cannot be cast to non-null type com.kizitonwose.calendar.view.internal.weekcalendar.WeekCalendarAdapter at com.kizitonwose.calendar.view.internal.weekcalendar.WeekCalendarLayoutManager.getAdapter(WeekCalendarLayoutManager.kt:13)
To Reproduce (if applicable)
Steps to reproduce the behavior: Open the App