Null pointer exception on API response retrofit2, despite the response operation being successful

45 Views Asked by At

Proplem:

I'm trying to pass the ID parameter to a dialog screen to view the required data by ID. Despite the response process being successfully done as shown in the stack trace, the venue's data always returns a NullPointerException. The same steps in the following lines with this API work perfectly with other parts of the project.

As you can see in this image, the response state is successful, and the API data is already fetched, but it still provides this error.

If anyone can help.

Error:

[![NullPointerException][1]][1]

API Response Steps:

//Dialog Screen

@Composable
fun FetchVenueInfo(
    onDismiss: ()-> Unit,
    onConfirm:()->Unit,
    mainViewModel: MainViewModel = hiltViewModel()
) {
    val context = LocalContext.current
    LaunchedEffect(key1 = Unit) {
        if (!mainViewModel.isVenuesByIdInitialized.value) {
            mainViewModel.getVenuesById(mainViewModel.venueId.value)
            mainViewModel.isVenuesByIdInitialized.value = true
        }
    }
    when (val state = mainViewModel.venuesByIdState.collectAsState().value) {
        is VenuesByIdState.Empty -> {  }
        is VenuesByIdState.Loading -> {  }
        is VenuesByIdState.Error -> {
            Toast.makeText(context, state.message, Toast.LENGTH_SHORT).show()
        }
        is VenuesByIdState.Success -> {
            CustomDialog(
                onDismiss = { onDismiss() },
                onConfirm = { onConfirm() },
                venuesResponse = state.data.body()!!
            )
        }
    }
}

@Composable
fun CustomDialog(
    onDismiss: ()-> Unit,
    onConfirm:()->Unit,
    venuesResponse: VenuesResponse
) {
    val dark = LocalTheme.current.isDark
    Dialog(
        onDismissRequest = {
            onDismiss()
        },
        properties = DialogProperties(
            usePlatformDefaultWidth = false
        )
    ) {
       Card(
            elevation = CardDefaults.cardElevation(8.dp),
            shape = RoundedCornerShape(15.dp),
            modifier = Modifier
                .fillMaxWidth(0.95f)
                .border(
                    2.dp, color = MaterialTheme.colorScheme.primary,
                    shape = RoundedCornerShape(15.dp)
                )
       ) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(15.dp),
                verticalArrangement = Arrangement.spacedBy(25.dp)
            ){
                Text(venueResponse.name!!) // <- NullPointerException
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                ) {
                    AsyncImage(
                        model = venuesResponse.image, // <- NullPointerException
                        contentDescription = "Image"
                    )
                }
            }
       }
    }
}

//Opening Dialog onClick

@Composable
fun MainScreen(
    matchByIdResponseItem: MatchByIdResponseItem,
    mainViewModel: MainViewModel = hiltViewModel()
) {
    if (mainViewModel.isDialogShown) {
        FetchStadiumInfo(
            onDismiss = { mainViewModel.onDismiss() },
            onConfirm = {}
        )
    }
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(
                 interactionSource = remember { MutableInteractionSource() },
                 indication = ripple(
                      bounded = true,
                      color = MaterialTheme.colorScheme.primary
                 ),
                 onClick = {
                      mainViewModel.setId(matchByIdResponseItem.fixture!!.venue!!.id!!)
                      mainViewModel.onDialogOpened()
                 }
            )
            .padding(top = 1.dp),
       colors = CardDefaults.cardColors(
            containerColor = Color.Transparent
       )
    ) {
       //...
    }
}
2

There are 2 best solutions below

0
Mahmoud Nabil On BEST ANSWER

Well, somehow not Venues class, but VenuesResponse is called Response<> by mistake within my code, and then the list will never be fetched. So, simply put, this is my data class: All I did was replace VenuesResponce with Venues in all steps:

data class Venues(

    @field:SerializedName("response")
    val response: List<VenuesResponse?>? = null,

    @field:SerializedName("get")
    val get: String? = null,

    @field:SerializedName("paging")
    val paging: Paging? = null,

    @field:SerializedName("parameters")
    val parameters: Parameters? = null,

    @field:SerializedName("results")
    val results: Int? = null,

    @field:SerializedName("errors")
    val errors: List<Any?>? = null
)

//ex.
interface MainApi {
    @Headers("api-key: $API_KEY")
    @GET(Constants.GET_VENUES)
    suspend fun getVenuesById(
        @Query(Constants.VENUE_BY_ID_PARAM) id: Int
    ): Response<Venues>
}
2
BenjyTec On

Your log is only showing that the API is successfully returning data. But it does not give you any hint whether Retrofit/OkHttp/Gson were able to map the response data onto your Response<VenuesResponse> class successfully.

You have not provided your VenuesResponse class, but it could happen that if there is a mismatch in the field names or in the hierarchy of the JSON response and your Kotlin classes, all fields will set to null.

I would recommend that you update your log statement as follows:

Log.d("Tag", "Success: $venueByIdResponse ")

and check whether the values are already null at that point.


Also, make sure that you use the correct variable names. It is probably just a typo, but

Text(venueResponse.name!!)

shouldn't even compile, as venueResponse does not exist, it should be venuesResponse.