0

I am currently working with two RecyclerViews, one acting as the parent and the other as the child. The parent RecyclerView consists of child items representing different videos, while the child RecyclerView contains frames within each video. My objective is to adjust the frame rate per second based on user actions such as zooming in or zooming out.

However, when I attempt to submit the updated list to the child RecyclerView after zooming in, the application becomes unresponsive and triggers an Application Not Responding (ANR) error.

Parent RV

 <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/parentRv"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:orientation="horizontal"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:listitem="@layout/parent_recycler_view_item" />

Child Rv

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="50dp"
    android:id="@+id/childRv"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    android:orientation="horizontal"
    tools:listitem="@layout/single_frame"/>

Child Item

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="50dp"
    android:layout_height="50dp"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:scaleType="centerCrop"
    android:orientation="vertical">


    <View
        android:id="@+id/dividerLeft"
        android:layout_width="0dp"
        android:layout_height="1dp"
        android:layout_gravity="center"
        android:layout_marginTop="5dp"
        android:background="?android:attr/listDivider"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_percent="0.25" />

    <View
        android:id="@+id/dividerRight"
        android:layout_width="0dp"
        android:layout_height="1dp"
        android:layout_gravity="center"
        android:layout_marginTop="5dp"
        android:background="?android:attr/listDivider"

        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_percent="0.25" />


    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="1dp"
        android:gravity="center"
        android:text="1"
        android:textSize="8sp"
        app:layout_constraintEnd_toStartOf="@+id/dividerRight"
        app:layout_constraintStart_toEndOf="@+id/dividerLeft"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="SmallSp" />


    <ImageView
        android:id="@+id/img"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:src="@drawable/track"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textView" />


</androidx.constraintlayout.widget.ConstraintLayout>

Parent Adapter


import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.funsol.vidzedit.Utils.debug
import com.funsol.vidzedit.databinding.ParentRecyclerViewItemBinding


class ParentAdapter(
    private val noOfVideos: Int,
    private val currentPosition: (Int) -> Unit
) :
    RecyclerView.Adapter<ParentAdapter.MyViewHolder>() {

    companion object {
        val timeLineAdapter by lazy {  TimeLineAdapter() }
    }

    class MyViewHolder(private val binding: ParentRecyclerViewItemBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun setAdapterToRv(){
            binding.root.apply {
                adapter = timeLineAdapter
                setRecycledViewPool(RecyclerView.RecycledViewPool())
                (layoutManager as LinearLayoutManager).initialPrefetchItemCount = 12
            }

        }

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(
            ParentRecyclerViewItemBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )
    }

    override fun getItemCount(): Int = noOfVideos

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.setAdapterToRv()
        currentPosition(position)
    }


}

Child Adapter


import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.request.CachePolicy
import com.funsol.vidzedit.Utils.debug
import com.funsol.vidzedit.databinding.SingleFrameBinding
import java.io.File
import kotlin.math.ceil

class TimeLineAdapter : RecyclerView.Adapter<TimeLineAdapter.MyViewHolder>() {


    companion object{
        var listOfFile = mutableListOf<File?>()
    }

    class MyViewHolder(private val binding: SingleFrameBinding) : RecyclerView.ViewHolder(binding.root) {


        fun bind(file: File?) {
            binding.img.load(file){
                memoryCachePolicy(CachePolicy.ENABLED)
                diskCachePolicy(CachePolicy.ENABLED)
            }
            binding.textView.text = if (adapterPosition % 2 == 0) "${adapterPosition}f" else "○"
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(
             SingleFrameBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        )
    }

    fun updateListOfFile(list: List<File?>) {
        val diffCallback = DiffCallback(listOfFile, list)
        val diffResult = DiffUtil.calculateDiff(diffCallback)

        listOfFile.clear()
        listOfFile.addAll(list)

        diffResult.dispatchUpdatesTo(this)
    }
    override fun getItemCount(): Int  = listOfFile.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {

        holder.bind(listOfFile[position])
    }

    class DiffCallback(private val old:List<File?>,private val new:List<File?>) : DiffUtil.Callback(){

        override fun getOldListSize(): Int {
           return old.size
        }

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

        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
           return old[oldItemPosition]?.absolutePath == new[newItemPosition]?.absolutePath
        }

        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
             return  old[oldItemPosition] == new[newItemPosition]
        }
    }
}

Main Activity

Here i an updating fps value based in condition but when zoom in/out trigger the APP Freeze

fun onZoomInOut(zoom:Int){
   
                    if (zoom < 3)
                        mainViewModel.increaseFpsValue(10)
                    else mainViewModel.decreaseFpsValue()
}

Updating Adapter

 launch {
                    mainViewModel.currentFpsValue.combine(mainViewModel.currentPosition) { fps, position ->
                        Pair(fps, position)
                    }.collect { pair ->

                        if (files.isNotEmpty())
                            withContext(Dispatchers.IO) {
                                createFrameList(lister = files[pair.second - 1], fps = pair.first) {
                                    dealAdapter(it)
                                }
                            }
                    }

                }

CreateFrameList

 private fun createFrameList(
        lister: List<File?>,
        fps: Int,
        files: (MutableList<File?>) -> Unit
    ) {

        CoroutineScope(Dispatchers.IO).launch {
            val frameList = mutableListOf<File?>()
            val steps = findSteps(fps)
            var index = 1.0
            while (index < lister.size - 1) {

                val frame = lister[ceil(index).toInt()]
                frameList.add(frame)
                index += steps
            }

            files(frameList)
        }
    }

    private fun findSteps(fps: Int): Double = when (fps) {
        1 -> 30.0
        10 -> 3.0
        20 -> 1.5
        else -> 1.0
    }

Deal Adapter

 private fun dealAdapter(it: MutableList<File?>) {
        CoroutineScope(Dispatchers.Main).launch {
            ParentAdapter.timeLineAdapter.updateListOfFile(it)

        }
    }

I was expecting that child rv will be simply updating on zoom in and out. But it cuasing ANR as the app doing too much work on Main thread.

0 Answers0