OverDraw
는 한 프레임 내에 동일한 픽셀에 두 번 이상 그리는 경우를 의미합니다.
일반적으로 OverDraw는 불필요
하기 때문에 특별한 이유가 없다면 제거하는 것이 권장됩니다.
예를 들어, View가 겹겹이 쌓여 있으면 최종적으로 사용자에게 보이는 화면은 맨 위에 쌓여 있는 뷰입니다.
그렇기 때문에 하단에 있는 View는 GPU 렌더링이 필요하지 않은 경우라면 그려지지 않아도 됩니다.
공식문서에서 OverDraw에 대해 다음과 같이 생각하고 있습니다.
저사양 기기의 GPU 성능이 계속해서 개선되고 있지만 디스플레이의 해상도는 여전히 낮은 편입니다. 성능이 낮다고 알려진 GPU 기기에 맞춰 최적화하지 않는 한, 원활한 앱 성능을 보장하기 위해 UI 스레드 작업을 최적화하는 데 집중하는 것이 좋습니다. 이 외에도 OS를 최적화하면 대부분의 경우(예: 창 배경을 오버드로하는 Fragment 배경) 앱 내에서 오버드로를 방지할 수 있습니다.
정리하자면, 성능이 극히 낮은 GPU 기기가 아닌 이상, OverDraw 최적화는 애플리케이션 성능을 크게 개선하지 않는 듯 합니다.
하지만 불필요하게 Background를 그리는 것 또한 비용라고 판단하였고, Kerdy에서는 이를 해결하기 위해 OverDraw 최적화를 진행하였습니다.
![]() | ![]() |
---|
안드로이드 기기의 개발자 옵션에는 GPU 오버드로 디버그
라는 옵션을 제공합니다.
3가지 옵션 중에 오버드로 영역 표시
를 선택하면 오른쪽 사진과 같이 화면에 색상이 입혀져 보입니다.
색상에 대한 설명은 다음과 같습니다.
흰색 : OverDraw 없음
파란색 : 1회 OverDraw
초록색 : 2회 OverDraw
분홍색 : 3회 OverDraw
빨간색 : 4회 OverDraw
다음과 같은 방법으로 오버드로를 줄이거나 없앨 수 있습니다.
첫 번째 방법만 적용해도 불필요한 OverDraw를 제거할 수 있었습니다.
처음 OverDraw 최적화를 해보려고 하면, 어디부터 어떻게 해야할지 난감합니다.
Android에서는 친절하게도 레이아웃 인스펙터(LayoutInspector)
라는 최고의 도구를 제공합니다.
레이아웃 인스펙터(LayoutInspector)
: 레이아웃의 구조를 쉽게 파악할 수 있도록 제공해주는 도구
OverDraw를 하기 전 커디의 행사 목록 화면입니다.
빨간색과 분홍색이 많기 때문에 그만큼 OverDraw가 많이 발생한다는 사실을 알 수 있습니다.
![]() | ![]() |
---|
레이아웃 인스펙터를 확인해보면, 흰색 배경
이 불필요하게 많이 존재한다는 것을 알 수 있습니다.
사실, 가장 뒷 배경
만 있으면 전체적인 화면을 커버할 수 있습니다.
원하는 View를 우클릭하면 위와 같이 선택할 수 있는 옵션창이 나타납니다.
Go To Declaration
을 클릭하여 해당 뷰가 선언된 XML 상으로 편리하게 이동할 수도 있습니다.
다만, id가 선언되지 않은 View는 자동으로 이동할 수 없습니다.
이동해보면 인스펙터에 나타나는 것처럼 background가 지정되어 있습니다.
불필요한 배경이므로 지워보겠습니다.
![]() | ![]() |
---|
전체적인 OverDraw가 1회 감소하였고,
레이아웃 인스펙터에서도 정상적으로 배경을 그리지 않음을 알 수 있습니다.
이러한 과정을 반복하면 최종적으로 위와 같은 상태를 만들 수 있습니다.
가장 좌측에 있는 흰색처럼 보이는 배경은 window background
입니다.
문제는, 적용한 테마에 따라 배경 색상이 완전한 흰색이 아니라는 점
입니다.
위 코드는 Material3
를 적용하고 있고, 연분홍 빛
을 띄는 배경입니다.
이를 해결하기 위해서 window의 background를 명시적으로 지정해줄 필요가 있습니다.
// PhoneWindow.java
protected ViewGroup generateLayout(DecorView decor) {
...
if (a.hasValue(R.styleable.Window_windowBackground)) {
mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground);
}
}
참고로, 이렇게 적용한 배경은 애플리케이션 전체의 기본 배경으로 설정되며, PhoneWindow
클래스에서 사용됩니다.
만약, 상태바(StatusBar)
까지 최적화하고 싶다면, statusBarColor
속성의 값을 투명(transparent)
하게 지정하면 됩니다.
하지만 상태바를 투명하게 만드는 설정은 자칫하면 부자연스러운 UI 로 이어질 수 있음을 주의해야 합니다.
예를 들어, 화면에서 SoftKeyboard가 보여질 때 화면이 위로 올라가는 경우 문제가 될 수 있습니다. (adjustPan)
Kerdy에서는 채팅 화면에서 문제를 겪었고, statusBarColor
는 흰색을 유지하기로 결정했습니다.
프로젝트별로 상황을 잘 고려하여 선택하면 좋겠습니다.
누군가는 Too Much 라고 할 수 있는 요소이긴 하지만, 해당 포스팅에서는 가능한 방법들을 모두 소개하고자 합니다.
위 그림을 살펴보면 연필 아이콘
이 핑크색
을 띄고 있습니다.
background
: 초록색 동그라미
src
: 연필 아이콘
XML을 살펴보니 background
와 src
모두 그려지고 있습니다.
즉, 배경이 그려진 후에 같은 픽셀에 연필 아이콘이 또 다시 그려집니다.
![]() | ![]() |
---|
문제를 해결하기 위해, 연필 아이콘을 XML에 명시하여 한 번에 그려지도록 변경해보았습니다.
다시 빌드해보면 초록색
으로 변경된 모습을 확인할 수 있습니다.
Tip. ImageView에 특정 리소스를 지정하면 어쩔 수 없이 OverDraw가 1회 이상 발생합니다.
기존 배경에 새로운 이미지를 그려야 하기 때문입니다.
![]() | ![]() |
---|
Dialog
또한 OverDraw 최적화 대상입니다.
Dialog의 background를 제거하는 것은 동일하지만, 이를 위해 style을 정의해 줄 필요가 있습니다.
Dialog는 기본적으로 FrameLayout
를 가지고 있습니다.
우클릭하여 이동해보면 아래와 같은 코드를 확인할 수 있습니다.
해당 코드는 Kerdy에서 작성한 코드가 아닌, BottomSheetDialog가 자체적으로 정의하고 사용하는 XML 일부입니다.
이를 통해, bottomSheetStyle
속성을 재정의하면 FrameLayout의 배경을 지정할 수 있음을 유추할 수 있습니다.
// bottom_sheet_dialog_style.xml
<style name="RoundBottomSheetDialogStyle" parent="Theme.Design.BottomSheetDialog">
<item name="bottomSheetStyle">@style/RoundBottomSheetStyle</item>
</style>
<style name="RoundBottomSheetStyle" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">@drawable/bg_all_bottom_dialog</item>
</style>
class MyBottomSheetDialog : BottomSheetDialog() {
...
override fun getTheme(): Int = R.style.RoundBottomSheetDialogStyle
}
Kerdy는 공통적으로 모서리가 둥근 BottomSheetDialog를 사용합니다.
커스텀한 background를 지정한 modal style
을 만들고, Dialog의 bottomSheetStyle 속성을 재정의합니다.
그리고, 각 BottomSheetDialog의 getTheme() 메서드를 override하여, 정의한 커스텀 style을 반환하도록 합니다.
최종적으로 OverDraw가 1회 감소한 결과를 얻을 수 있었습니다.
Kerdy 서비스는 OverDraw 최적화
를 무사히 마칠 수 있었습니다.
불필요한 GPU 렌더링을 피하기 위해 직접 최적화를 해보는 경험은 꽤나 유의미했습니다.
앞으로는 View에 Background를 지정할 때 한 번 더 생각해볼 듯 합니다.
글을 마무리하며, 최적화된 일부 화면을 공유합니다.
![]() | ![]() |
---|
![]() | ![]() |
---|