글에 앞서 소개
안녕하세요? 룩핀에서 안드로이드 개발을 하고 있는 오경식이라고 합니다.
혹시 잘못 된 내용이나 궁금한 점이 있으실 경우에는 언제든지 메일이나 메시지 부탁드리겠습니다.
이글을 쓰게된 계기
안드로이드 개발을 하면서 List관련 화면에서 View를 재사용하지 않고 구현하는 경우에 findViewById
를 계속 호출 하게 되어 성능저하가 일어날 수 있다는 것으로 많이들 알고 있으실 겁니다.
예전 kmshack님의 ListView 성능 개선 글을 읽어보시면 글 중 ListView에 findViewById
의 값 비싼 호출을 반복하는 문제를 해결하기 위한 ViewHolder Pattern
및 그 외 문제점에 대한 글을 쓰셨습니다.
지금 현재 많이들 쓰고 계신 RecyclerView에서는 이 문제를 해결하기위해 ViewHolder Pattern
을 꼭 사용하도록 하였습니다만
최근 kunny님이 쓰신 kotlin Android Extensions - 리사이클러뷰의 뷰홀더에서 올바르게 사용하는 방법을 보시면 뷰홀더를 Kotlin Android Extensions를 이용해 저 처럼 잘못 작성하시는 경우에는 kotlin 코드의 바이트코드를 자바코드로 변환하시면 kunny님 글에 있듯이 호출 비용이 큰 findViewById
를 매번 호출하고 있었습니다.
해당 글을 읽고 기존 코드를 수정하면서 findViewById
가 왜 그런 호출 비용이 커서 성능 관련 이슈를 발생시키는지에 대한 궁금증이 생겨 그 범인에 대해서 알아보고 글을 쓰게 되었습니다.
범인을 찾아보자
findViewById
이 성능상의 문제가 될 수 있는 이유를 찾아서 설명하기 위해서 안드로이드 소스코드를 찾아 보게 되었습니다.
View
|
|
을 보시면 id가 0 이상인 경우 즉 유효한 경우에 findViewTraversal
을 호출 합니다.
|
|
findViewTraversal
을 보시면 단지 전달 된 id가 View의 id와 같은지 검사하고, 그렇지 않으면 null을 반환합니다.
딱히 성능에 영향을 끼칠만한 요소는 보이지 않습니다.
그렇다면 ViewGroup을 보시겠습니다.
ViewGroup
|
|
전달 된 id가 ViewGroup의 ID와 같은지 여부를 확인하고 같은경우에 자기자신을 반환하고 아닌 경우 자기 자신이 가진 Child View에 대해 루프를 돌려 각 Child View에 findViewById
를 호출하고 있습니다.
그렇다면 만약 ViewGroup안에 Child View가 10개가 있고 그 해당 뷰들을 찾아 넘길려면 최소 10번의 loop를 돌게됩니다.
리스트처럼 뷰가 반복적으로 사용되는 곳에서 위 메서드가 호출되는 상황이 반복된다면 꽤나 큰 리스크라고 생각이 됩니다.
결론
코드를 잘 짜도록 하자.