androidkotlinandroid-recyclerviewandroid-volleyjsonobjectrequest

Why does my kotlin android app crash after receiving response on JsonArrayRequest? Is there any error in my code?


I get no errors in building the app but when the app finally launches, it crashes right after response is gotten from the JsonArrayRequest() function.

these are my logcats: logcat 1 logcat 2

FATAL EXCEPTION: main
Process: com.example.scoremusplayer, PID: 19239
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object 
at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:122)
at com.bumptech.glide.Glide.get(Glide.java:128)
at com.bumptech.glide.Glide.getRetriever (Glide.java:510)
at com.bumptech.glide.Glide.with(Glide.java:564)
at com.example.scoremusplayer.scoreExplore.adapter.AdapterForScoreListForAllScoreFragmentForRecyclerView.onBindViewHolder 
at com.example.scoremusplayer.scoreExplore.adapter.AdapterForScoreListForAllScoreFragmentForRecyclerView.onBindViewHolder
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder (RecyclerView.java:7065)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder (RecyclerView.java:7107)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline (RecyclerView.java:6012)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline (RecyclerView.java:6279)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition (RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition (RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.GridLayoutManager.LayoutChunk (GridLayoutManager.java:561)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)

Below is the code in the fragment:

class AllScoresFragment : Fragment() {

    companion object {
        lateinit var tempList : ArrayList<Score>
        fun newInstance() = AllScoresFragment()
    }


    private lateinit var binding: FragmentAllScoresBinding
    private lateinit var viewModel: AllScoresViewModel
    private lateinit var progressDialog : ProgressDialog

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentAllScoresBinding.inflate(inflater)
        return binding.root
//        return inflater.inflate(R.layout.fragment_all_scores, container, false)
    }

    @Deprecated("Deprecated in Java")
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)


        viewModel = ViewModelProvider(this)[AllScoresViewModel::class.java]


        binding.include.recyclerViewForScoreContent.setHasFixedSize(true)
        binding.include.recyclerViewForScoreContent.layoutManager = GridLayoutManager(context, 3)

        retrieveTwo()


        binding.swipeToRefresh.setOnRefreshListener {
            retrieveTwo()
            binding.swipeToRefresh.isRefreshing = false
        }
    }

I can sense that the problem is coming from WHAT HAPPENS AFTER THE RESPONSE IS GOTTEN from the JsonArrayRequest in the retrieveTwo() function, but I cannot seem to know what I did wrong in there.

The following is the retrieveTwo() function:

fun retrieveTwo() {
    tempList = ArrayList()
    progressDialog = ProgressDialog(context)
    progressDialog.setMessage("Loading...")
    progressDialog.show()



    val pdfSiteUrl = "https://..."

    // Get a RequestQueue
    AllScoresFragment().context?.let { MySingleton.getInstance(it).requestQueue }

    val jsonArrayRequest = JsonArrayRequest(
        Request.Method.GET, pdfSiteUrl, null,
        { response ->
            
            //Toast the response to know if response is actually gotten
            Toast.makeText(context, response.toString(), Toast.LENGTH_LONG).show()

            var jo : JSONObject

            try {
                for (i in 0 until response.length()){
                    jo = response.getJSONObject(i)
                    val id = jo.getInt("id")
                    val name:String = jo.getString("name")
                    val composer:String = jo.getString("composer")
                    val style:String = jo.getString("style")
                    val theme:String = jo.getString("theme")
                    val album:String = jo.getString("album")
                    val lang:String = jo.getString("lang")
                    val thumbnailUrl:String = jo.getString("thumbnail_url")
                    val pdfUrl: String = jo.getString("pdf_url")

                    val score = Score(
                        id = id,
                        name = name,
                        composer = composer,
                        style = style,
                        theme = theme,
                        album = album,
                        lang = lang,
                        thumbnail_url = pdfSiteUrl + thumbnailUrl,
                        pdf_url = pdfSiteUrl + pdfUrl
                    )

                    tempList.add(score)

                    val adapter =
                        context?.let {
                            AdapterForScoreListForAllScoreFragmentForRecyclerView(it,
                                tempList) }

                    binding.include.recyclerViewForScoreContent.adapter = adapter

                    progressDialog.hide()

                }

            } catch (e : JSONException){
                Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
                progressDialog.hide()
            }

        },
        { error ->
            Toast.makeText(context,
                error.message,
                Toast.LENGTH_SHORT)
                .show()
            progressDialog.hide()
        }
    )

    // Access the RequestQueue through my singleton class.
    context?.let { MySingleton.getInstance(it).addToRequestQueue(jsonArrayRequest) }
}

Below is how my Recyclerview Adapter looks like:

class AdapterForScoreListForAllScoreFragmentForRecyclerView(
    private val context: Context,
    private val scoreList: ArrayList<Score>
) : RecyclerView.Adapter<AdapterForScoreListForAllScoreFragmentForRecyclerView.ViewHolder>() {
    class ViewHolder(binding: ScoreListForRecycleBinding) : RecyclerView.ViewHolder(binding.root){
        val pdfThumbnailImage = binding.thumbnailForScore
        val songTitle = binding.scoreTitleNameOfAllScoresFragment
        val composerName = binding.nameOfComposerForAllScoresFragment
        val genre = binding.styleForAllScoresFragment

        //val for root
        val root = binding.root

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
    : ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val scoreList = ScoreListForRecycleBinding.inflate(layoutInflater, parent, false)
        return ViewHolder(scoreList)
    }


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


    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
       
        val scoreName = scoreList[position].name
        val scoreComposer = scoreList[position].composer
        val genre = scoreList[position].style
        val pdfUrl = scoreList[position].pdf_url
        val thumbnailImage = scoreList[position].thumbnail_url

        holder.songTitle.text = scoreName
        holder.composerName.text = scoreComposer
        holder.genre.text = genre

        if (pdfUrl.isNotEmpty()){
            Glide
                .with(FragmentActivity())
                .load(thumbnailImage)
                .placeholder(R.drawable.scoremus_icon_slash)
                .into(holder.pdfThumbnailImage)
        }



        holder.root.setOnClickListener {
            holder.root.isLongClickable = true
            Toast.makeText(
                context,
                "opening \"${scoreName.uppercase()} composed by $scoreComposer\"...",
                Toast.LENGTH_SHORT)
                .show()

            val intent = Intent(context, PdfActivity::class.java)
            intent.putExtra("index", position)
            intent.putExtra("pdfPath", scoreList[position].pdf_url)
            ContextCompat.startActivity(context, intent, null)
        }
    }

}

Help me out please

This is what i wanna do:

  1. Request a json array from an https source
  2. Get the json objects for each element of the json array
  3. display the objects in recyclerview

but my app crashes after getting the response for my json array


Solution

  • if (pdfUrl.isNotEmpty()){
            Glide
                .with(FragmentActivity())
                .load(thumbnailImage)
                .placeholder(R.drawable.scoremus_icon_slash)
                .into(holder.pdfThumbnailImage)
    }
    

    You are creating and passing a Fragment Activity instance to glide which is not attached to the application context. When glide is trying to access the applicationContext, it's throwing an NPE.

    You can either pass any holder view or the context

    Using view

    Glide
        .with(holder.pdfThumbnailImage)
        .load(thumbnailImage)
        .placeholder(R.drawable.scoremus_icon_slash)
        .into(holder.pdfThumbnailImage)
        
    

    Using context

    Glide
        .with(holder.pdfThumbnailImage.context)
        .load(thumbnailImage)
        .placeholder(R.drawable.scoremus_icon_slash)
        .into(holder.pdfThumbnailImage)