Android Support Library의 버전이 23.2.0으로 상승하면서 생긴 변경점중 하나로 RecyclerView.LayoutManager에 AutoMeasure라는 기능이 opt-in으로 도입됐다는 것이다. [1]

이는 RecyclerView의 부모 레이아웃 사이즈를 wrap_content로 둘 경우, 부모 레이아웃의 상황에 따라 RecyclerView에서 onMeasure 호출시기에 onLayoutChildren을 호출해 아이템의 위치를 자동으로 맞춰주는 기능이다.

기본으로 제공되는 LayoutManager의 경우 default값이 true이다. RecyclerView를 버전업하고 레이아웃의 동작이 바뀌었다고 생각된다면 이 옵션과 관련이 돼 있을 수 있으므로 확인해 보는것을 추천한다.

안드로이드 디벨로퍼 사이트에서 설명한 전체 동작은 아래와 같다. 일부 내용은 구체적으로 정리하지 않았다. [2]

  1. 레이아웃 매니저에서 해당 플래그가 true가 되어있음을 가정한다.
  2. onMeasure(int, int)가 호출되었을 때, MeasureSpec이 EXACTLY인경우 LayoutManager의 onMeasure만을 호출하며 그 외 다른 호출없이 종료된다.
  3. MeasureSpec이 EXACTLY가 아닌경우(AT_MOST, UNSPECIFIED), RecyclerView에서 자신의 onMeasure를 사용해 AutoMeasure 처리를 시작하게 된다. 이 과정에서 어댑터를 갱신하며 레이아웃이 예측 가능한지를 결정한다.
  4. 레이아웃 관련 계산이후, RecyclerView는 패딩값과 아이템의 박스 사이즈를 감안하여 자신의 width, height를 set한다. 여기서 LayoutManager는 setMeasuredDimension을 오버라이드 할 수 있다. 예를 들어 GridLayoutManager의 경우 아이템이 가로로 다 차지 않더라도 최대 width를 유지해야 하므로 오버라이딩이 필요하다.
  5. 아이템 뷰를 배치할 때 onLayoutChildren(Recycler, state)이 호출되어 state의 값이 isMeasuring() == true, isPreLayout() == false로 세팅된채로 내려온다. RecyclerView는 이 과정에서 실질적으로 아이템의 뷰 애니메이션을 준비한다.
  6. onLayout(boolean, int, int, int, int)이 호출되어 최종적으로 레이아웃 계산을 끝낸다.
  7. 앞에서 미리 작업해 둔 애니메이션이 동작하게 된다.

개인적인 경험을 이야기하자면, ListView로 된 소스를 RecyclerView로 마이그레이션 하던 도중 RecyclerView를 23.2.0 이상으로 버전업을 했더니 앱의 반응성이 지극히 떨어지는 현상이 있었다.

확인 해 보니 AutoMeasure가 동작하면서 알 수 없는 이유로 bindViewHolder가 현재 화면에 보이는 아이템 수 만큼만 호출되는게 아니라, 아직 화면에 노출되지 않는 아이템 position까지 미리 bind를 하고 있었다.

해당 문제를 해결하고자 임시 방편으로 AutoMeasure 옵션을 false로 주어 해결하였으나, 레이아웃 파일에 문제가 있었던걸로 추측된다. RecyclerView는 언제든지 버전업이 되면서 기존에 문제없던 부분도 변경 될 수 있으므로 주의가 필요해보인다.

참고자료

[1] Android Developers – Support Library Revision History
[2] Android Developers – setAutoMeasureEnabled