본문 바로가기

구글과인터넷/안드로이드

안드로이드 ListView의 성능개선을 해보자.

Android의 위젯중 가장많이 사용한다고 해도 과언이 아닌 위젯이 ListView이다. 

그 ListView를 활용하는대에 성능개선을 해보려고 한다. 

ListView ?
ListView는 Android내에 있는 View중에 하나로서 ViewGroup으로 형성이 되어있다. 
ViewGroup은 말그대로 View의 집합이라고 볼수 있다. 그만큼 많은 View들이 생성이 될수록 성능에 대해서 고려를 해봐야되는게 정상이다. 그래서 ListView에 ViewGroup을 컨트롤을 하고 정의하는 Adapter 객체를 성능고려를 하여서 작성하는법에 대해 알아보겠습니다. 

성능고려 코딩에 앞서 기본적인 Adapter의 성능에 대한 객체 생성법을 3가지로 나누워서 설명을 해보겠습니다. 

1. 기본적인 생성법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ItemAdapter extends ArrayAdapter<string> {
       final Activity context;
       ItemAdapter(Activity context, ArrayList<string> list) {
                 super(context, R.layout.listrow, list);
                 this.context = context;
       }
 
       public View getView(int position, View convertView, ViewGroup parent) {
                 LayoutInflater inflater = context.getLayoutInflater();
                 View row = inflater.inflate(R.layout.listrow, null);
                 TextView label = (TextView)row.findViewById(R.id.label);
                 label.setText(getItem(position));
                 return row;
       }
}
</string></string>

getView메소드는 각 View가 보여질때마다 호출이 된다. 
그래서 getView에서 각각의 View들을 컨트롤을 하고 정의를 해야한다. 
여기서 중요한게 Inflater라는 객체 이다. Inflater는 XML 레이아웃에 정의된 내용을 분석해서 View의 객체트리구조로 만드는 역활을 하고 있다. 
이제 생성이 된 View단에 findViewById를 이용하여 TextView를 생성하고 getItem메소드를 사용해서 TextView에 셋팅을 해주고 있다. 

* getItem 메소드는 ArrayAdapter의 Memeber Method로서 생성자에서 셋팅한 배열을 position를 이용하여 불러와주는 역할을 한다. 

2. convertView 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ItemAdapter extends ArrayAdapter<string> {
       final Activity context;
       ItemAdapter(Activity context, ArrayList<string> list) {
                 super(context, R.layout.listrow, list);
                 this.context = context;
       }
 
       public View getView(int position, View convertView, ViewGroup parent) {
                 View row = convertView;
                  if (row == null) {
                      LayoutInflater inflater = context.getLayoutInflater();
                      row = inflater.inflate(R.layout.listrow, null);
                 }
                 TextView label = (TextView)row.findViewById(R.id.label);
                 label.setText(getItem(position));
                 return row;
       }
}
 
</string></string>

2번째 방법으로는 covertView를 사용하여 View객체 생성을 줄이는것이다. 
처음 getView가 호출이 되면 convertView는 null값이 들어가는데 그 이후로는 null값이 들어가지 않는다. 
이점을 생각해보니 View에 Inflater이 생성을 줄이기 위해 재귀호출 형식으로 리턴 된 VIew를 넣어주는것같다.
그럼 위와 같이 코딩을 하면 만약 10개의 아이템을 가진 list를 출력하고자 하면 10번의 inflater의 생성을 방지할수 있게 되는것이다. 이것이 10번이라면 많은 성능이 차이가 나지 않겠지만 점점 많아진다고 하면 엄청난 View단의 객체 생성 자원을 절약할수 있게된다. 

여기서 ListView의 성능개선법이 끝이아니고 마지막으로 Adapter를 생성할때의 가장좋은 효과를 볼수 있는 홀더 패턴을 이용하여서 코딩을 해보겠다. 

3. 홀더패턴
2번의 코딩법도 1번의 코딩법에 대해서 엄청난 성능의 효과를 볼수 있엇는데 2번의 코딩법중 findViewById라는 메소드도 엄청난 성능을 떨어지게 한다. 
findViewById 메소드는 인플레이션으로 생성한 위젯 가운데 ID를 이용하여 찾아오는 기능을 하는데 위젯 내부의 객체 트리에서 원하는 객체를 찾는데 필요한 연산의 양은 무시하지 못할수준이다. 그래서 이것을 적용하기 위해 객체를 하나 생성을 해서 View에서 제공하는 setTag와 getTag를 이용하여 findViewById의 호출을 절약해보겟다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class ItemAdapter extends ArrayAdapter<string> {
       final Activity context;
       ItemAdapter(Activity context, ArrayList<string> list) {
                 super(context, R.layout.listrow, list);
                 this.context = context;
       }
 
       public View getView(int position, View convertView, ViewGroup parent) {
                 View row = convertView;
                 ItemWrapper wrapper;
                  if (row == null) {
                      LayoutInflater inflater = context.getLayoutInflater();
                      row = inflater.inflate(R.layout.listrow, null);
                      wrapper = new ItemWrapper(row);
                      row.setTag(row);
                 else {
                      wrapper = (ItemWrapper)row.getTag();
                 }
                 wrapper.getTextView().setText(getItem(position);
                 return row;
       }
 
       class ItemWrapper {
            View base;
            TextView label;
             
            ItemWrapper(View base) {
                this.base = base;
            }
           
            TextView getTextView() {
               if (label == null)
                      label = (TextView)base.findViewById(R.id.label);
               return label;
            }
             
}
 
</string></string>
ItemWrapper라는 클래스를 만들어서 View에 tag메소드를 이용하여 처리를 하니 엄청난 자원을 절약을 할수 있게된다. 

이렇게 3가지의 유형으로 Adapter를 생성을 알아봤는데 1번과 3번은 데이터의 양이 많아 질수록 성능에 대한 차이를 눈으로 확인을 할수 있게 될것이다. 하지만 데이터의 양이 많지 않은곳은 굳이 코딩이 더 길어지는 홀더패턴을 이용하여 처리를 안해도 될듯하다. 하지만 확실히 데이터양이 많아지면 차이가 난다는거 !