myth

Net search list optimization, most will suggest us from the following aspects of optimization, to improve the smoothness of the list.

  1. Lower the layout level
  2. Cache layout objects, or control memory allocation, etc
  3. Control list refresh scope

But we often face the following scenario:

  1. The business is complex and the layout hierarchy cannot be simplified
  2. The layout of the cache is done by the viewHolder, and the cache for that control is also controlled
  3. A quick full list swipe must refresh the full list, and the range is all items displayed by the list control.

This makes list frame rate optimization almost impossible to advance.

None of these suggestions are fundamentally wrong, but none of them get to the crux of the problem.

The problem

The problem is indeed “The layout of items is too complex “.

This “too complex” causes two Android Framework methods to be time-consuming:

  • inflate()
  • onMeasure()

We can confirm this with the method time detection tool. Methods The design scheme of time-consuming detection tool is shown here

These two methods, respectively, are called when a new item needs to be displayed during list scrolling.

If you are using a ViewHolder, then inflate() may not be called very often. But onMeasure will definitely call.

The list control needs this method to confirm the size of the item.

It is easy to understand that complex layout XML files consume a lot of time constructorates ().

But why does onMeasure() take time? There is another factor that affects the onMeasure() method time:

    android:layout_width="match_parent"
    android:layout_height="wrap_content"
Copy the code

Match_parent wrap_content means that the size of the layout is indeterminate and depends on the parent or child View.

This would require traversing the size of the parent layout or traversing the child View to confirm the size of the current Item.

This is a time-consuming process that can cause congestion.

This was the crux of the problem, but we were faced with a scenario where layout complexity could not be reduced any further, and any further reduction would reduce requirements.

How can demand be cut… Cut by demand is more like..

plan

Fortunately, two key problem points were identified

  • inflate()
  • onMeasure()

We can optimize for both methods.

  1. Stage to draw
  2. Size of the cache

Stage to draw

Time spent on the inflate(). We can split the inflate() process of Item into at least two stages.

  1. Only the ItemView frame is initialized while scrolling, ignoring the detailed layout
  2. Initialize/draw the ItemView detail layout after scrolling stops

When the list is scrolling quickly, the user can’t actually see the details of each Item. So just initialize a simple framework.

The frame layout is simple, and the inflate is fast

After the list stops, at this point, each Item that is being displayed is displayed in the de-inflate detail layout, addView() to ItemView, and the detail content is drawn.

There are many options for phased drawing, such as listening for scrolling (not recommended). Or using handler#sendMessage()/ #removeMessage() in ViewHolder. It doesn’t expand.

The difficulties in

The hard part here is when the list is scrolling at high speed. It is difficult to confirm whether there is any transition drawing.

The transition rendering is when the same viewHolder draws the contents of different items several times, but only the data for the last Item is displayed.

In this case, the previous drawing is wasted and prone to stalling. This problem needs to be carefully identified by logging.

Size of the cache

For list controls, the size of each Item is, in most cases, fixed.

Caching can be used to improve the efficiency of onMeasure().

@override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if(hasCache){ Execute cache results directly setMeasuredDimension(mWidthCache, mHeightCache); }else{onMeasure() super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidthCache = getMeasuredWidth(); MHeightCache = getMeasuredHeight(); HasCache = true}}Copy the code

The difficulties in

  1. If the contents of your Item might change, for example if you have a TextView set to GONE, it might change the size of the Item, update the cache as soon as possible.

  2. AddView /removeView, update the cache as well.

conclusion

The problem

The complexity of the Item layout results in the following method taking up frame time

  1. Inflate () time consuming
  2. OnMeasure () time consuming

plan

  1. Stage to draw
  2. Size of the cache

After this optimization, increase your RecycleView frame rate to 55 + frames

If not, suggestions:

  1. Continue to draw in N stages
  2. Check transition drawing