Search code examples
androidandroid-databindingandroid-viewmodeldagger-hiltviewmodel-savedstate

NullPointerException @ SavedStateHandle ViewModel w/ Hilt


My app crashes when running my viewmodel below

@HiltViewModel
class ReportViewModel @Inject internal constructor(
    savedStateHandle: SavedStateHandle,
    private val monthRepository: MonthRepository
) : ViewModel() {

    //error in this line (monthId) says NullPointerException
    private val monthId: Long = savedStateHandle.get<Long>(MONTH_ID_SAVED_STATE_KEY)!! 

    val report = monthRepository.getMonthData(monthId).asLiveData() 

    companion object {
        private const val MONTH_ID_SAVED_STATE_KEY = "monthId"
    }
}

The error is java.lang.NullPointerException. I am using safeArgs. See my ViewHolder below.

inner class ViewHolderOne(var binding: ListMonthItemBinding) :
    RecyclerView.ViewHolder(binding.root) {
    init {
        binding.root.setOnClickListener {

            binding.month?.let { month ->
                navigateToMonth(month, it)
            }
        }
    }
    private fun navigateToMonth(
        month: Month,
        view: View
    ) {
        val direction =
            MonthFragmentDirections.actionNavMonthsToNavReports(
                month.identification
            )
        view.findNavController().navigate(direction)
    }

    fun bind(months: Month) {
        binding.apply {
            month = months
            executePendingBindings()
        }
    }
}

When I start to navigate to my detailFragment. The app starts to crash saying NullPointerException.

The code below is my nav_graph

<fragment
    android:id="@+id/nav_months"
    android:name="com.appname.ui.report.MonthFragment"
    android:label="@string/menu_months"
    tools:layout="@layout/fragment_month" >
    <action
        android:id="@+id/action_nav_months_to_nav_reports"
        app:destination="@id/nav_reports" />

</fragment>
<fragment
    android:id="@+id/nav_reports"
    android:name="com.appname.ui.report.ReportFragment"
    android:label="@string/report" >
    <argument
        android:name="monthId"
        app:argType="long" />
</fragment>

And here is the ReportFragment

@AndroidEntryPoint
class ReportFragment : Fragment() {

    private val reportViewModel: ReportViewModel by viewModels()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
        ): View {
            val binding = DataBindingUtil.inflate<FragmentReportBinding>(
            inflater,
            R.layout.fragment_report,
            container,
            false
        ).apply {
            viewmodel = reportViewModel
            lifecycleOwner = viewLifecycleOwner
        }
        return binding.root
    }
}

I referenced the code from Sunflower App of Google. But I think I miss something.

How to fix this crash?


Solution

  • Got the issue solved. The problem is I never noticed that my MONTH_ID_SAVED_STATE_KEY value at the ReportViewModel is different from my argument name at nav_graph

    It should be

    // ReportViewModel.kt
    companion object {
        private const val MONTH_ID_SAVED_STATE_KEY = "monthId"
    }
    
    // nav_graph.xml
    <argument
        android:name="monthId"
        app:argType="long" />
    

    Already updated the code at my post for future reference.