Optimize view hierarchy

Park Ji Hong , ggikko
9 min readOct 23, 2016

View를 최적화 해보자.

내가 알고 있는 말들..

  1. hierarchy viewer 를 사용하라.
  2. merge를 사용하라. 이는 view hierarchy 를 단조롭게 할 수 있다.
  3. 될 수 있으면 relative layout을 사용하라. (= view 구조를 단조롭게 가져가라)

+ hierarchy viewer 를 사용하라.

참고로 hierarchy viewer은 매우 불편하다.

이유는 instant run을 못 사용하기 때문.. hierarchy viewer는 전체 decorview부터 차근차근 모두 보여준다.

Android Device Monitor

프로젝트의 뷰 레벨을 파악하고 수정하면서 어디가 복잡도가 높고 많은 비용을 먹고 있는지 판단할 수 있다. (솔직히 너무 복잡함)

+ merge를 사용하라.

merge는 view tree의 레이아웃 레벨을 낮출 수 있다. 왜냐하면

<merge>자체가 말 그대로 같은 레벨의 layout에 붙이는 거기 때문에..

include는 ++ 하는 것이기에 흔히들 merge는 하나의 view를 더하거나 layout을 쪼개서 붙여야할 때 쓰이고

include는 layout 단위로 반복되는 작업을 덧붙일때 쓰인다.

무식하게 예제를 만들어보았습니다.

merge안하고 레이아웃을 잡으면

이런식..

merge 하면 그냥 바로 customLinear에 붙는

이런 그림이 된다.

+ 될 수 있으면 relative layout을 사용하라. (= view 구조를 단조롭게 가져가라)

일반적인 사람들의 오해 : relative layout이 가벼워서 우선 시 써야해(?)

테스트 한것 부터

LinearLayout (Button, TextView)

2번씩 불린다..? linear -> button -> text -> linear -> button -> text

RelativeLayout (Button, TextView)

4번씩 불린다. relative -> button -> text -> relative -> button -> text -> relative -> button -> text -> relative -> button -> text

FrameLayout (Button, TextView)

2번씩 불린다. frame -> button -> text -> frame -> button -> text

음..? 내가 알기로는 Linear는 1번씩 불려야 정상 아닌가? 왜 2번씩 불러지지

어디선가 자식의 뷰를 측정하는데 2번 사용하는 것 같다.(정확한 이유를 모르겠음) Appcompat 문제는 아닌거같다

Appcompat을 사용하면 contentframelayout이 껴들기는 하는데

https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v7/appcompat/src/android/support/v7/widget/ContentFrameLayout.java

코드를 보면 그런 코드가 없다. 실제로 Appcompat을 빼더라도 2번 불린다. 음.. 나중에 해결하기로

일단 정리하자면

View level을 낮추는게 엄청난 성능 효과를 가져온다는 이유는 이렇게 비용이 곱하기가 되기 때문이다. 예시를 하나 보여주겠다.

예를 들어보면

구조가

frame ( linear (button, text) ) 이렇게 뷰를 그릴 때는

frame -> linear -> button -> text -> frame -> linear -> button -> text 이고,

반면에

frame ( linear (button, relative (text) ) 이렇게 뷰를 그리면

frame -> linear -> button -> relative -> text -> text -> frame -> linear -> button -> relative -> text -> text

만약에 relative를 4개를 쓰고 거기에 textview를 그릴 경우 2⁴ 만큼 불리게 된다. (비용이 커짐)

그래서 일반적으로 잘못된 생각을 가지고 있는 것은 RelativeLayout을 자주 쓰면 좋다! 라고 말하는데 그게 아니라..

RelativeLayout을 적절하게 잘써서 View tree를 간편하게 만드는게 중요한 거다.

만약 linear ( text, text ) vs relative (text , text) 라면

linear (text, text) 가 퍼포먼스 측면에서 승이다. (물론 코드 가독성이나 기타 등등 에서는 또 다르게 고려해봐야함. (for performance vs for maintenance)

하지만 여기서 예외가 있다.

LinearLayout에서는 이런 코드들이 있다.

float childExtra = lp.weight;

if(childExtras > 0) { // 자식들 measure Spec 구하기 }

즉 weight설정이 default가 0인데 default가 아니면 자식의 크기를 한번 더 구하게 된다.

weight을 사용하게되면 relativeLayout처럼 2번 구한다는 것

정리하자면..

상황에 따라 맞게 써야하는데 view tree는 가장 낮게 하는게 퍼포먼스 측면에서 제일 좋고 Linear와 Relative는 상황에 따라 적절하게 쓰면 된다. merge, include도 마찬가지.

코드분석 : onMeasure에서 child의 spec을 계산하는데 비용이 많이 듬.

onMeasure 안에 child의 spec을 요구하는 코드가 있음.

일반적으로 onMeasure의 패턴은 비슷함.

  1. 부모 아이들 리스트(뷰가 담겨있는 ArrayList)를 clear
  2. 자식이 있는지 for문으로 돌림.
  3. max width, height계산함.
  4. drawable, padding, 기타 등등을 함
  5. 그 다음 자식 뷰들의 measure를 요구함

각 Layout마다 차이가 조금 씩 있음.

아래의 글을 참조하면 더 효율적으로 그릴 수 있다.

그 외에..

https://developer.android.com/training/custom-views/optimizing-view.html

onDraw 를 최대한 적게.. payback이 큼(비용이 많이 듬)

onDraw 하면 invalidate가 불리기 때문에 invalidate()도 적게 사용하라

requestLayout() 도 적게..

참조

[android blogspot]

[optimizing layout google developer]

[layout efficient]

[Google I/O 2013 Custom Views for Android]

[뷰는 어떻게 그려지는 걸까?]

[Android 최적화 팁 40가지]

https://opensignal.com/blog/2013/07/30/40-developer-tips-for-android-optimization/

[Custom View]

--

--