KotlinAndroid -在后台线程中通过互联网获取Json对象/数组的正确方法,并在UI的回收器视图中显示结果

7z5jn7bk  于 2022-12-09  发布在  Android
关注(0)|答案(1)|浏览(111)

**这是我的问题:**当应用程序启动时,甚至在滑动刷新时,UI中没有显示任何内容(没有项目),但我希望UI在应用程序启动时在recyclerView中显示一些项目。

检索到的jsonArray不是空的,也不是null。当我运行应用程序或使用我的应用程序时,我没有得到任何应用程序崩溃或logcat中的错误。

**有趣的是:*当我点击设备上的light/dark mode按钮切换到暗/亮模式时, 我的项目列表会正确显示在应用程序的UI中 *。但是,当我点击设备上的light/dark mode按钮时,我注意到logcat中出现以下错误消息。

logcat 1logcat 2显示器

这是我的片段

class AllScoresFragment : Fragment() {

    companion object {
        var ScoreListASF : ArrayList<Score> = ArrayList()
    }

    //configure private lateinit vars
    private lateinit var binding: FragmentAllScoresBinding
    private lateinit var viewModel: AllScoresViewModel
    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter : AdapterForScoreListForAllScoreFragmentForRecyclerView

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

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

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

        //create the observer which updates the UI
        val liveScoreObserver = Observer<ArrayList<Score>>{
            
            //update the UI
            ScoreListASF = it
        }

        //let the observer observe my livedata through viewModel
        viewModel.liveScores(context).observe(this.viewLifecycleOwner, liveScoreObserver)

        //configure recyclerview for layout in this Fragment
        recyclerView = binding.include.recyclerViewForScoreContent
        recyclerView.setHasFixedSize(true)
        recyclerView.setItemViewCacheSize(100)
        recyclerView.layoutManager = LinearLayoutManager(this.context)
        recyclerView.isVerticalScrollBarEnabled = true
        adapter =
            context?.let { AdapterForScoreListForAllScoreFragmentForRecyclerView(it, ScoreListASF) }!!
        recyclerView.adapter = adapter

        //configure what happens when the layout is swiped to be refreshed
        val swipeToRefresh = binding.swipeToRefresh
        binding.swipeToRefresh.setOnRefreshListener {
            viewModel.liveScores(context).observe(this.viewLifecycleOwner, liveScoreObserver)
            swipeToRefresh.isRefreshing = false
        }
    }
}

这是视图模型:

class AllScoresViewModel () : ViewModel() {
    private val getOnlineData: GetOnlineData = GetOnlineData()

    fun liveScores(context: Context?): LiveData<ArrayList<Score>> = liveData {
        val data = getOnlineData.retrieveScore(context)
        emit(data)
    }
}

这是GetOnlineData类别:

sealed class Result<out R> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
}

class GetOnlineData {

    suspend fun retrieveScore(context: Context?) : ArrayList<Score> {
        val tempList = ArrayList<Score>()
      
        //create and show a progress dialog; hide it when the process is done
        val progressDialog = ProgressDialog(context)
        progressDialog.setMessage("Loading...")
        progressDialog.show()

        // Get a RequestQueue
        AllScoresFragment().context?.let { MySingleton.getInstance(it).requestQueue }
        
        //run newtwork jsonRequest on background thread to avoid ANR
        //retrieve json objects and add each object-set to the ArrayList<Score>()
        return withContext(Dispatchers.IO){

            val url = "https://..."
            val jsonArrayRequest = JsonArrayRequest(
                Request.Method.GET, url, null,
                { response ->

                    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 = url + thumbnailUrl,
                                pdf_url = url + pdfUrl
                            )

                            tempList.add(score)

                        }

                        //just to show/know that i get the response: all 'scores' added
                        //this toast shows up every single time, meaning that i get the response
                        Toast.makeText(context,
                            "success getting scores: $tempList",
                            Toast.LENGTH_LONG).show()

                        Result.Success("success getting scores: $tempList")
                        
                        //hide the progress dialog upon getting response
                        progressDialog.hide()

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

                        //hide the progress dialog upon getting error
                        progressDialog.hide()
                    }

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

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

            return@withContext tempList
        }
    }
}

怎么回事?我做错了什么?
这就是我要做的:
1.在后台线程中从https源获取jsonArray
1.将所有检索到的jsonObjects添加到ArrayList
1.当我的应用程序启动或滑动刷新时,使用RecyclerView将此ArrayList显示给UI
请帮帮我。我做错了什么?我会非常感激得到一个解决方案

目前编辑次数:
我编辑的片段要容纳DiffUtil

override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

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

        ScoreListASF = ArrayList()

        //create the observer which updates the UI
        val liveScoreObserver = Observer<ArrayList<Score>>{ scoreArrayList ->

            //oldList
            ScoreListASF = ArrayList()

            //get the new list from the observer and call the adapter.insert()
            // for DiffUtil to do its thing
            val newList = scoreArrayList

            adapter.insertItem(newList)

            //tried using notifyDataSetChanged(), did not work
            //adapter.notifyDataSetChanged()

        }

        //observe the livedata
        viewModel.liveScores(context).observe(this.viewLifecycleOwner, liveScoreObserver)

        //configure recyclerview for layout in SongFragment
        recyclerView = binding.include.recyclerViewForScoreContent
        recyclerView.setHasFixedSize(true)
        recyclerView.setItemViewCacheSize(100)
        recyclerView.layoutManager = LinearLayoutManager(this.context)
        recyclerView.isVerticalScrollBarEnabled = true

        adapter =
            context?.let { AdapterForScoreListForAllScoreFragmentForRecyclerView(it, ScoreListASF) }!!
        recyclerView.adapter = adapter

        //what happens when swipe to refresh is initiated
        val swipeToRefresh = binding.swipeToRefresh
        binding.swipeToRefresh.setOnRefreshListener {
            viewModel.liveScores(context).observe(this.viewLifecycleOwner, liveScoreObserver)
            swipeToRefresh.isRefreshing = false
        }
    }

这是recyclerView的适配器:

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
    }
    
    //the function to insert the new list to let DiffUtil do its thing
    fun insertItem(newItemList: ArrayList<Score>){

        //should i clear the list before adding newList?
        //scoreList.clear()

        val diffUtil = MyDiffUtil(scoreList, newItemList)
        val diffResult : DiffUtil.DiffResult = DiffUtil.calculateDiff(diffUtil)

        scoreList.addAll(newItemList)
        diffResult.dispatchUpdatesTo(this)
    }

    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
        Glide
            .with(holder.pdfThumbnailImage)
            .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)
        }
    }

}

这是DiffUtil类别:

class MyDiffUtil(
    private val oldListYeah : ArrayList<Score>,
    private val newListYeah : ArrayList<Score>
) : DiffUtil.Callback() {
    override fun getOldListSize(): Int {
        return oldListYeah.size
    }

    override fun getNewListSize(): Int {
        return newListYeah.size
    }

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldItemPosition == newItemPosition
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldListYeah[oldItemPosition] == newListYeah[newItemPosition]
    }
}
uxhixvfz

uxhixvfz1#

所以我用RetrofitDiffUtil计算出了这个问题。任何想知道这个问题的完整详细解决方案的人都可以给我发邮件:jditechnet@gmail.com .
它现在工作得很完美!!感谢所有做出贡献的人。

相关问题