1

I'm facing a weird RecyclerView lag in my Fragment after an async task. It freezes for a few milliseconds just before my web request is finished. I feel like the issue might be related to some kind of a ui thread blocking, but I can't figure out what should be changed in my code then to get it work. The following code works fine inside an Activity, I got this issue only when I'm using it inside my Fragment.

Here is my Fragment:

class MyFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        super.onCreateView(inflater, container, savedInstanceState)
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.my_fragment, container, false)
        setup(view)
        return view
    }

    private fun setup(view: View) {
        val myAdapter = MyAdapter(
            SharedData.dataViewModel,
            object :
                MyAdapter.Callback {
                //...
            })
        view.recyclerView.adapter = myAdapter
        val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(activity)
        view.recyclerView.layoutManager = layoutManager

        view.progressBarLayout.visibility = View.VISIBLE
        SharedData.dataViewModel.retrieve { success, error ->
            view.progressBarLayout.visibility = View.INVISIBLE
            if (success) {
                view.recyclerView.adapter?.notifyDataSetChanged()
            } else {
                error?.let {
                    Toast.makeText(
                        requireContext(), it.localizedMessage,
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }
        }
    }
}

Inside retrieve method I got:

Webservice.shared.getData()
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeOn(Schedulers.io())
    .subscribe ({ result ->
        //...
    }, { error ->
        //...    
    })

And getData is a simple retrofit2 GET request:

@GET("get_data")
    fun getData(): Observable<DataModelResult>

Edit

Here is my xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="@android:color/white"
    android:orientation="vertical"
    tools:context=".screens.sample.fragment.MyFragment">

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:paddingLeft="@dimen/_14sdp"
            android:paddingRight="@dimen/_14sdp"
            android:layout_marginTop="@dimen/_10sdp"
            android:layout_marginBottom="@dimen/_10sdp">
            <TextView
                android:id="@+id/topTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/sample_text"
                android:lineSpacingMultiplier="1.2"
                android:textColor="@android:color/black"
                android:textSize="@dimen/_17ssp"
                android:gravity="center_horizontal" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:paddingLeft="@dimen/_14sdp"
            android:paddingRight="@dimen/_14sdp"
            android:weightSum="1">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                tools:listitem="@layout/item_layout_child"
                android:layout_weight="1"/>

            <Button
                android:id="@+id/nextButton"
                android:layout_width="match_parent"
                android:layout_height="55dp"
                android:background="@drawable/button_rounded"
                android:backgroundTint="@color/CustomColor"
                android:layout_marginLeft="32dp"
                android:layout_marginRight="32dp"
                android:layout_marginTop="14dp"
                android:layout_marginBottom="22dp"
                android:textAllCaps="true"
                android:textColor="@drawable/button_text_color"
                android:textSize="@dimen/_16ssp"
                style="?android:attr/borderlessButtonStyle"
                android:elevation="5dp"
                android:translationZ="1dp" />
        </LinearLayout>

    </LinearLayout>

    <include
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        layout="@layout/indicator_layout" />

</FrameLayout>
coldembrace
  • 549
  • 8
  • 19
  • Take a look at https://developer.android.com/studio/profile/cpu-profiler for some debugging advice (that's what I'd use to debug this if it were in front of me). It's hard for us to help without actually seeing everything that happens when the request is returned. – Ryan M Apr 28 '20 at 07:59
  • Add XML files too – Ravi Apr 28 '20 at 10:49
  • @Ravi added my full xml file – coldembrace Apr 28 '20 at 11:27

3 Answers3

0

Try initiating the view (calling setup in this case) from overload of onViewCreated method instead of onCreateView method. Hopefully it'll resolve the lag.

Gulshan
  • 3,611
  • 5
  • 35
  • 46
0

In your situation it depends on what you are doing in your success callback from your retrieve method. Since you are observing that callback in AndroidSchedulers.mainThread() if you are doing some heavy calculations or saving data locally and etc it can create that lag which you are talking about. Try to observe and probably parse the result on background thread and only update the UI on main thread.

hardartcore
  • 16,886
  • 12
  • 75
  • 101
  • Inside my success block I'm doing only `viewModels = result.data.map { MyDataViewModel(it) }`. I've also tried to put that code on a background thread, but the freeze was still there. – coldembrace Apr 28 '20 at 17:03
  • Than I suggest that you should use the profiler to see what takes that much time inside that fragment. – hardartcore Apr 28 '20 at 20:05
0

Finally, I think I've found out what was causing that issue in my case. I set up some animations inside my navigation graph and most likely they where causing some kind of a thread lock. Putting code on a background thread did't help me at all. So in that case, I came up with 2 possible solutions:

  1. Set up your animations programmatically (Unfortunately, I haven't found a way to add a listener to my xml animation) and add the listeners. After that you can easily start doing your async task when the transition animation is finished. Example: https://stackoverflow.com/a/46362350/7110268

  2. If you got a lot of different animations, or you want to start your async task before the end of animations, you can also put your completion return inside some Handler(). That solution is not the best one for sure, and may cause some troubles in the future, but if you need just a quick fix, you can try it.

Handler().postDelayed({
    completion(true, null)
}, context.resources.getInteger(R.integer.fragment_animation_length).toLong())
coldembrace
  • 549
  • 8
  • 19