IT_Programming/Android_Java

[펌] 안드로이드 RecyclerView 성능 개선팁

JJun ™ 2017. 3. 13. 09:50



 출처

 : http://www.kmshack.kr/2017/03/안드로이드-recyclerview-사용팁/





안드로이드 RecyclerView 성능 개선팁

RecyclerView는 제한된 화면에서 큰 데이터 세트를 제공하기 위한 유연한 View입니다. RecyclerView는 안드로이드 앱 개발에 있어서 가장 중요한 위젯 중 하나인 ListView를 좀 더 발전시킨 버전입니다. 뉴스 피드나 연락처 목록을 구현 시 사용자가 빠르게 스크롤할 때 성능 문제 또는 불필요한 지연을 방지하기 위해 ListView를 사용했습니다. RecyclerView는 ListView의 성능과 지연을 100% 방지 못하는 문제점을 해결한 버전입니다.

이 글은 RecyclerView 사용방법에 관한 글이 아닙니다.
RecyclerView를 사용하면서 유용한 정보와 중요한 규칙, 절대 하지 말아야 할 것을 하나씩 살펴보겠습니다.

피할 수 있는 문제

1. 제대로된 View 재사용
: ViewHolder 내부에서 View 애니메이션을 절대 사용하면 안됩니다. (ex. itemView.animate()호출)
ItemAnimator는 View 애니메이션을 처리할 수 있는 유일한 구성 요소입니다. (ex. RecyclerView.setItemAnimator() )

ListView에서는 아이템 View 애니메이션 처리를 위해 getView()에서 작업을 하였습니다.
이는 View의 재사용 문제가 있으며 RecyclerView에서 동일한 패턴으로는 사용해서는 절대 안됩니다.

2. 세분화된 Adapter 업데이트
: 변경이 된 데이터에 대해서만 Adapter 업데이트를 하세요. (ex. notifyItemChanged(4))
NotifyItemRangeChanged()는 필요로 하지 않는 View를 업데이트하기 때문에 불필요하게 사용하지 마세요.
(ex. notifyItemRangeChanged(0, getItemsCount() )

DiffUtil을 사용하여 Adapter의 성능을 측정 해 볼 수 있습니다.

3. onBindViewHolder position != final
: 절대 onBindViewHolder 내부에서 View.OnClickListener를 셋하지 마세요.
  onBindViewHolder는 데이터를 View에 바인딩하기 위해서만 사용해야 합니다.

아래와 같이 사용합니다.

class ColorViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ColorViewHolder(View itemView, ItemClickListener clickListener) {
super(itemView);
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
removeAtPosition(getAdapterPosition())
}
}

이러한 간단한 규칙 3가지만으로도 RecyclerView의 성능은 보장됩니다.
데이터 변경에 따른 쉬운 애니메이션 처리방법.

  • ● 위에서 언급한 RecyclerView.setItemAnimator()를 직접 구현해도 되지만
       notifyItemChanged(), notifyItemRangeChanged(), notifyItemInserted(), notifyItemMoved(), notifyItemRemoved()를
       사용하기만 해도 기본적인 애니메이션이 적용됩니다.

  • ● getItemId(int position)와 함께 setHasStableIds(true)를 사용하면 RecyclerView.notifyDataSetChanged()로
       모든 애니메이션을 자동으로 처리할 수 있습니다. (참고: http://wp.me/p4L8WU-MF)


더보기


Stable Id를 이용한 RecyclerView 성능 향상법

RecyclerView는 ListView를 완전히 대체할 수 있을 만큼 기능과 성능이 크게 향상되었다. ListView에서도 어떻게 하면 끊김 없이 빠른 스크롤을 지원할까라는 고민을 해왔었고, RecyclerView를 사용하다 보면 똑같은 고민을 또 하게 될 것이다.

ViewHolder라는 패턴을 통해 ListView의 성능을 RecyclerView에서 크게 향상할 수 있었다. 재활용하는 뷰들의 클래스를 View 태그 또는 Array에 저장하고 필요할 때 바로 가져와서 사용하는 방법으로 성능을 크게 향상하였다. ListView에서 재활용되는 뷰를 해당 포지션에 맞게 가져오는 곳에서 성능을 향상할 수 있었다면, RecyclerView는 가져온 View에 데이터를 바인드 시 최적화할 수 있는 방법을 제공하고 있다.

HasStableIds사용을 통해 데이터 바인드 시 onBindViewHolder()를 최적화 되게 호출할 수 있다. 아래 2가지 중 하나만이라도 해당한다면 성능을 크게 향상할 수 있다.

  • 똑같은 데이터가 반복적으로 나타는 리스트이다.
  • notifyDataSetChanged를 자주 호출한다.

HasStableIds는 Adapter.setHasStableIds(boolean)을 통해 설정할 수 있으며, 사용하는 경우 어댑터의 getItemId(int)를 반드시 구현해야 작동한다.

getItemId(int)를 통해 해당 아이템은 고정된 상태로 설정된다. 예를 들어 아래와 값을 반환되게 구현했다면 어떤 성능적인 변화가 일어날까?

position
return
0
100
1
200
2
300
3
100
4
400
5
500

onBindViewHolder(view, int)는 포지션이 0, 1, 2, 4, 5 만 호출된다. 3번은 0번째 포지션에서 같은 고정된 ID를 반환했기 때문에 같은 데이터로 인식하여 onBindViewHolder(view, int)가 호출되지 않는다. 같은 데이터임을 알고 데이터 바인드를 할 필요가 없기 때문에 호출되지 않으며 그만큼 성능은 향상된다.

position
return
0
100
1
200
2
600
3
300
4
100
5
400
6
500

포지션 2번에 데이터를 추가하고 notifyDataSetChanged()를 호출하였다. 이때 onBindViewHolder(view, int)는 현재 보이고 있는 포지션이 모두 호출되지만 StableId를 사용하게 된다면 이미 호출된 고정된 ID를 제외한 위치가 호출된다. notifyDataSetChanged()를 하였음에도 변경되는 ID만을 골라 해당 포지션만 onBindViewHolder(view, int)를 호출하게 됨으로 그만큼 성능은 향상된다.

 

 성능 팁!

부드러운 스크롤을 원한다면 아래의 간단한 규칙을 지키면 됩니다.

  • ● 프레임 당 모든 작업을 수행하는데 16ms내로 작업하도록 해야 합니다.
       개발자 옵션에서 프로필 GPU 렌더링 옵션을 사용하여 성능을 모니터링하세요.

  • ● 레이아웃 구조를 최적화 및 간단한 구조를 유지하세요.

  • ● 깊은 레이아웃 계층을 피하기 위해 HierarchyViewer를 사용하세요.

  • ● 오버 드로우 문제를 피하고 시스템 도구로 모니터링 하세요.

  • ● TextView에 긴 텍스트를 설정하지 마세요. 텍스트 줄을 계산하기위해 많은 연산이 필요하여 성능을 떨어집니다.
       텍스트 끝 말줄임표나 최대 줄수를 설정 해두는 것도 하나의 방법입니다.

  • ● 렌더링 퍼포먼스를 향상하기위해 LayoutManager.setItemPrefetchEnabled()를 사용하세요.

RecyclerView는 안드로이드의 강력한 위젯이며 모든 결과를 얻을 수 있습니다. 하지만 잘못된 작은것 하나가 앱의 품질을 좌우 합니다.

참고: https://medium.com/master-of-code-global/recyclerview-tips-and-recipes-476410fa12fd#.enkpoi5ir