애니메이션의 프레임과 비슷하게 안드로이드의 화면도 여러 개의 프레임들이 순차적으로 그려지고 완성되어 사용자에게 보이는 과정을 거칩니다. 초당 프레임 수가 많아질수록 사용자는 부드럽게 느낍니다. 그런데 이상하게 유명한 앱과 달리 자신이 만든 앱이 버벅거린다는 느낌을 받은 적이 있나요?
답은 뷰를 그리는 과정 속에서 여러분들의 불필요한 코드와 로직에 의해 하나의 프레임을 그리는 속도가 느려지게 되는 것입니다. 결과적으로 초당 프레임 수가 낮아지고 사용자는 버벅거린다는 느낌을 받게 됩니다. 이번 포스팅에서는 한 픽셀을 여러 번 그리는 오버드로에
대해서 알아보겠습니다.
문제점들
View Groub의 중첩으로 발생할 수 있는 오 버드로
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
tools:context=".ui.main.detail.MainDetailController">
<RelativeLayout
android:id="@+id/recently_Lotto_info_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
LottoLike 일부분 코드입니다.
자세히 보시면 자식인 RelativeLayout와 부모인 ConstaintLayout 모두 background를 지정하고 있습니다. 두 View Group 모두 match_parent
속성으로 인해 같은 크기를 뜻하기에 해당 앱의 배경은 불필요하게 2번 그려집니다. (여기서는 부모 배경색이 사용자에게 보이지 않게 되겠네요) 따라서 필요하지 않은 background 지정은 지우는 것이 좋습니다!
투명도 사용으로 발생하는 오버드로
색상을 16진수로 표현할 때,#00 FFFFFF8자리의 16진수라면 맨 앞 2자리는 투명도를 의미합니다. 위 그림을 보면 투명화를 적용하지 않고 rgb값을 조정한 왼쪽 이미지와 검은색 색상에 투명도를 조절해 적용한 오른쪽 이미지는 사용자에게 똑같은 색상으로 보입니다. 하지만 앱 내부적으로 해당 검은색상을 그린 후 Alpha 값을 적용하는 연산이 필요하기 때문에 같은 픽셀에 대해 오버드로우가 발생하는 것입니다.
해결 방법
GPU 오버드로 시각화 기능 사용
흰색 0회, 파란색 1회, 녹색 2회, 분홍색 3회, 빨간색 4회 이상
안드로이드 개발자 옵션 중 오버드로우를 시각화하는 기능이 있습니다. 같은 픽셀에 대해 그려지는 횟수에 따라 색상이 달라집니다. 시험삼아 저의 앱도 살펴보았습니다. 이렇게 GPU 오버드로 시각화
기능을 통해 버벅거리는 현상이 어느 뷰에서 일어나는지 쉽게 파악할 수 있습니다.
레이아웃 기본 배경색 지정
안드로이드 레이아웃을 직접 만들어 보신 분들은 앱을 빌드해보고 나면 레이아웃 기본 배경 색상이 완전 #fff
는 아니란 것은 아실 겁니다. 전체적으로 약간 어두운 흰색을 띠고 있어 디자이너 요구에 의해 background
속성 값을 흰색으로 지정해서 사용하는 사람이 많으실 겁니다.
이는 오버드로 관점으로 보면 레이아웃에 정의된 약간 어두운 흰색 배경을 그린 후, 개발자 지정한 흰색을 또 한 번 그리는 오버드로우가
발생한 상황입니다. 위의 문제를 해결하는 방법은 아래 코드와 같습니다.
<!-- Base application theme. -->
<style name="AppTheme" parent= ...>
<item name="android:windowBackground">@android:color/white</item>
...
뷰에 적용되는 스타일이 아닌 Activity 화면에 직접 적용되는 속성인 android:windowBackground
속성으로 흰색을 지정해주면 됩니다.
그러면 변경된 오버드로우 시각화를 살펴볼까요?
전체적으로 오버드로 횟수가 줄은 것을 확인할 수 있습니다!
투명도 비용을 줄이기
색상을 지정할 때, 투명도를 사용하면 색상을 직접 주입하는 것보다 연산이 필요하다는 것을 앞서 말했습니다. 하지만 어떤 앱들은 서서히 투명해지는 UI를 사용하는 것을 볼 수 있습니다. 서서히 줄어드는 즉 매 프레임마다 오버드로우가 발생하는 투명도 연산을 무리해가면서 뷰를 그리는 것일까요?
답은 하드웨어 레이어
를 사용하는 것입니다. 하드웨어 레이어는 투명도, 스케일 등 어떠한 원본 이미지에서 추가적인 연산이 지속적으로 필요할 때 사용하면 좋습니다. 앞에서 예시를 든 서서히 투명해지는 UI가 될 수 있습니다. 투명도 100 퍼에서부터 50 퍼까지 서서히 줄어드는 UI는 각 프레임마다 원본 이미지를 프레임이 그리기 + Alpah (99%),... , 원본 이미지를 프레임에 그리기 + Alpah (50)... 작업을 꾸준히 하게 되고 이는 불필요한 원본 이미지를 픽셀에 계속 그리기 되는 것입니다.
하드웨어 레이어는 버퍼에 처음 원본 이미지를 복사하고 난 뒤, 그 이후에 그려지는 프레임에 대해서 이미지에 필요한 연산을 수행한 후 뷰에 복사하는 작업을 수행합니다. 수식으로 표현하면 원본 이미지를 레이어의 그리기 + Alpah(99%) + 프레임에 복사, Alpah(98%) + 프레임에 복사,... , Alpah(50%) + 프레임에 복사하는 형태입니다. GPU는 복사하는 작업을 쉽게 할 수 있기 때문에 훨씬 효율적입니다. 하지만 초기 비용이 높기 때문에 예시처럼 연속적으로 투명도, 스케일 연산이 필요할 경우 사용하면 됩니다.
1) View Type 명시
//현재 뷰에 그려진 화면을 재사용을 명시합니다. 따라서 하드웨어 레이어 버퍼에 원본 이미지가 복사됩니다.
myView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
/*
* 여기서 같은 뷰에 대해 투명도 연산 (100 % ~ 0%), 스케일 연산등을 적용합니다.
*/
// 중요! 더이상 작업이 필요하지 않을 때, 버퍼 메모리를 해제해줍니다.
myView.setLayerType(View.LAYER_TYPE_NONE, null)
2) ViewPropertyAnimator 사용
API 16 이상으로는 보다 편리 위의 작업을 수행할 수 있습니다.
ViewPropertyAnimator.alpha(0.0f).withLayer();
참고
'Android > Common' 카테고리의 다른 글
[Android] MVP 적용해보기 - Model 말고 Repository (4) | 2020.11.23 |
---|---|
[Android] 뷰의 성능 개선 - RecyclerView (3) | 2020.11.05 |
[Android] Drawable color 속성을 코드로 변경하기 (0) | 2020.08.24 |
[Android] MVP 적용해보기 - View와 Presenter (0) | 2020.08.11 |
[Android] Executor - 기능 별 Thread 분리 (0) | 2020.07.17 |