0

I have this observer for my viewmodel so that I can setup my adapter but, when I run the app, it gives me the NullPointerException error on this line:

japaneseAdapter = it.data?.let { it1 -> JapaneseAdapter(it1) }!!

This is the activity with that line:

@AndroidEntryPoint
class JapaneseActivity : AppCompatActivity() {
   private lateinit var binding: ActivityJapaneseBinding
   private val japaneseViewModel by viewModels<JapaneseViewModel>()
   private lateinit var japaneseAdapter: JapaneseAdapter

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       binding = ActivityJapaneseBinding.inflate(layoutInflater)
       setContentView(binding.root)

       japaneseViewModel.japaneseResponse.observe(this, {
           when(it.status){
               Resource.Status.LOADING -> { }
               Resource.Status.SUCCESS -> {
                   japaneseAdapter = it.data?.let { it1 -> JapaneseAdapter(it1) }!!
                   binding.rvNews.adapter = japaneseAdapter
               }
               Resource.Status.ERROR -> { Log.d("ERROR","ERROR RAISED") }
           }
       })
   }
}

This is the adapter:

class JapaneseAdapter(private var japaneseResponse: List<JapaneseResponse>) :
    RecyclerView.Adapter<JapaneseAdapter.ViewHolder>() {

    inner class ViewHolder(
        view: View
    ) : RecyclerView.ViewHolder(view) {
        private val binding = NewsItemsBinding.bind(view)

        private val itemTitle: TextView = binding.tvTitle
        private val itemImage: ImageView = binding.ivNews
        private val itemDescription: TextView = binding.tvDescription

        fun bind(response: JapaneseResponse) {
            Picasso.get().load(response.urlToImage).into(itemImage)
            itemTitle.text = response.Title
            itemDescription.text = response.Description
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val v = LayoutInflater.from(parent.context).inflate(R.layout.news_items, parent, false)
        return ViewHolder(v)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(japaneseResponse[position])
    }

    override fun getItemCount(): Int {
        return japaneseResponse.size
    }

}

Generic data source:

abstract class BaseDataSource {

    protected suspend fun <T> getResult(call: suspend () -> Response<ApiResponse<T>>): Resource<T> {
        try {
            val response = call()
//            if(response.isSuccessful) {
//                val body = response.body()?.data
//                if(body != null) return Resource.success(body)
//            }
            val body = response.body()?.data
            return Resource.success(body)
            //return Resource.error("${response.code()}: ${response.message()}")
        } catch (e: Exception) {
            return Resource.error(e.message ?: "Generic error")
        }
    }
}

data class Resource<out T>(val status: Status, val data: T?, val message: String?) : Serializable {

    enum class Status {
        SUCCESS,
        ERROR,
        LOADING
    }

    companion object {
        fun <T> success(data: T?): Resource<T> {
            return Resource(
                Status.SUCCESS,
                data,
                null
            )
        }

        fun <T> error(message: String, data: T? = null): Resource<T> {
            return Resource(
                Status.ERROR,
                data,
                message
            )
        }

        fun <T> loading(data: T? = null): Resource<T> {
            return Resource(
                Status.LOADING,
                data,
                null
            )
        }
    }

    fun isSuccessful() = status == Status.SUCCESS

    fun isError() = status == Status.ERROR

    fun isLoading() = status == Status.LOADING
}

The data source for the Japanese news:

class JapaneseDataSource @Inject constructor(private val japaneseService: JapaneseService) :
    BaseDataSource() {

    suspend fun getJpNews() = getResult { japaneseService.jpNews() }
}

Repository:

class JapaneseRepository @Inject constructor(
    private val remote: JapaneseDataSource
) {
    suspend fun jpNews() =
        remote.getJpNews()
}

The service:

interface JapaneseService {
    @GET("/v2/top-headlines?country=jp&apiKey=77acc490875643c5b2328fb615e0cf83")
    suspend fun jpNews(): Response<ApiResponse<List<JapaneseResponse>>>
}

I can see that the response is there since I have okhttp logging it for me but for some reason it seems to be null and I am not sure why... Any help?

dazai
  • 766
  • 4
  • 25
  • It's still a duplicate, there's an extensive canned answer ...and there is no debug info at all. – Martin Zeitler Oct 24 '21 at 23:27
  • @MartinZeitler: It is very possible that I should have left it alone. I don't think that `NullPointerException` lends itself to a canned answer, since the exception has a zillion possible causes. Usually, I *do* leave it alone; I rarely vote to reopen. This time, I was 80% of the way through typing in an answer that would more directly help this developer when it was closed, so I peevishly voted to reopen. I forgot that in this case it would go all the way open (I expected it to just count as a vote). But, given that, I wrapped up my answer. I apologize if you took offense at my actions. – CommonsWare Oct 24 '21 at 23:54
  • @CommonsWare I've indeed looked for a more Kotln-specific dupe... and null-ness is being handled slightly different, but in the end - it still produces a Java NPE. I'd rather suggest to [debug](https://developer.android.com/studio/debug) and to read the corresponding [Kotlin](https://kotlinlang.org/docs/null-safety.html#the-operator) documentation. – Martin Zeitler Oct 25 '21 at 00:10

1 Answers1

4
japaneseAdapter = it.data?.let { it1 -> JapaneseAdapter(it1) }!!

!! is Kotlin's "crash the app" operator. It says that you want to crash the app if the value you are applying !! to is null. Your objective, as a Kotlin programmer, is to avoid using !!.

In this case, the fact that you are crashing on that line with that error means that !! is being applied to null. That will occur if it.data evaluates to null.

it.data appears to be a Resource object with a status of SUCCESS. So, presumably, you are calling success() with a value of null.

With all that in mind, you will need to use your debugger and see why val body = response.body()?.data is evaluating to null, or see where else you are getting a Resource with null data.

And, please, try to avoid using !!.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491