출처 : http://iam1492.tistory.com/33


조금 수정..


  안드로이드 리스트뷰는 가장 많이 쓰이는 인기 View 중 하나이다. 실제 프로젝트에 적용할때는 기본 리스트뷰를 사용하는 경우는 거의 없고 대부분의 경우 Custom View를 만들어서 사용하게 된다. 이때 이미지나 텍스트 등 한개의 리스트 아이템에 레이아웃이 복잡한 경우 리스트뷰를 스크롤할때 뚝뚝 끊기는 것을 볼 수 있다. 
  이것은 리스트 뷰가 메모리를 효율적으로 관리하기 위해 화면상에 보이지 않는 리스트 아이템을 재활용 하기 때문인데 자세한 내용은 생략하고(궁금하면 developer site에서 한번 읽어보자) 이렇게 뷰를 재활용하면서 UI를 갱신하면서 성능 저하가 발생 되는 것이다.
  대체적으로 이것을 해결하는 일반적인 방법은 xml 리소스에 접근하는 횟수를 최소화 하는 것이다. 그래서 대부분의 경우 ViewHolder 패턴을 적용한다. 패턴이라 하기엔 상당히 간단한 방법인데 예제를 보면서 확인해보자.

[View Holder 적용 전]
@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View v = convertView;
    if(v == null) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        v = inflater.inflate(R.layout.layout_list_item, null);
    }
    TextView txtName = (TextView)v.findViewById(R.id.txtName);
    TextView txtMail = (TextView)v.findViewById(R.id.txtMail);

    Contact entry = mList.get(position);
    txtName.setText(entry.getName());
    txtMail.setText(entry.getMail());

    return v;
}

ViewHolder 를 적용하기 전에는 항상 xml 리소스에 접근하게 되며 성능 저하의 원인이 된다.


[View Holder 적용 후] 

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    ViewHolder holder;
if(v == null) { LayoutInflater inflater = LayoutInflater.from(mContext); v = inflater.inflate(R.layout.layout_list_item, null); holder = new ViewHolder(); holder.txtName = (TextView)v.findViewById(R.id.txtName); holder.txtMail = (TextView)v.findViewById(R.id.txtMail); v.setTag(holder); }else{

holder = (ViewHolder)v.getTag();

    }
Contact entry = mList.get(position); if(entry != null) { holder.txtName.setText(entry.getName()); holder.txtMail.setText(entry.getMail()); } return v; } static class ViewHolder { TextView txtName; TextView txtMail; }


이런식으로 ViewHolder 클래스를 만들고 처음 한번만 xml 리소스의 접근할 경우 getView 가 불릴때 마다 findViewByID 를 할 필요가 없어져 성능이 좋아진다.

 (위 예제의 경우 성능 차이를 느낄 수 없겠지만 실제로 복잡한 레이아웃의 경우 체감 성능에 많은 개선이 있다.)


그런데 얼마전 회사 동기가 View Holer 는 죽었다며 한 블로그 포스팅을 공유해줬다. 포스팅 제목은: View Holder is dead. Long live setTag 였다. 


무슨 내용인가 유심히 읽어 보니 안드로이드에서 1.6이후에 setTag(int key, Object tag) 라는 메소드를 지원해준다는 것이다.즉, 따로 ViewHolder 클래스를 만들 필요가 없다는 것이다. 바로 적용해 보았다. 


[ setTag(int key, Object tag) 메소드 적용 후]

@Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView;

TextView txtName;

TextView txtMail; if(v == null) { LayoutInflater inflater = LayoutInflater.from(mContext); v = inflater.inflate(R.layout.layout_list_item, null); //View Holder 에 넣었던 부분을 setTag(key,tag) 메소드로 대체한다. v.setTag(R.id.txtName, v.findViewById(R.id.txtName)); v.setTag(R.id.txtMail, v.findViewById(R.id.txtMail)); }else{

txtName= (TextView)v.getTag(R.id.txtNam); txtMail= (TextView)v.getTag(R.id.txtMail);

} Contact entry = mList.get(position); if(entry != null) { txtName.setText(entry.getName()); txtMail.setText(entry.getMail()); } return v; }

성능 차이는 크게 나지 않는다~ 

결과적으로 View Holder is dead란 제목은 좀 적절해 보이지는 않으나, 이런것도 있구나 하는 걸 새로 알게됐다.(왜 그동안 이 메소드가 안보였는지...)


혹시 아직도 getView 혹은 bindView시에 항상 xml 리소스에 접근해 리소스 객체를 가져오고 있다면, ViewHolder 나 setTag(int key, Object tag) 둘중 하나의 방법으로 소스를 고쳐보자. 훨씬 부드럽게 스크롤되는 알흠다운 모습을 볼 수 있을 것이다.


+ Recent posts