The proper use of View.setAlpha()
작업중
Romain Guy님이 처음에 공유함 (2012년의 포스트이므로 현재는 개선된 내용일 수도 있다. 2013년의 포스트를 참고하자)
나는 View.setAlpha(float)의 부작용에 대해 더 많은 정보를 주고 싶다. 이 메소드는 매우 편리하지만 적절히 사용되지 않으면 성능에 부정적인 영향을 줄 것이다.
How does it work?
이 메소드는 3가지의 다른 방식으로 동작할 수 있다:
(*drawing command는 drawRect(), drawBitmap()등의 명령을 말하는 듯하다.)
- 만약 뷰에 중첩된 drawing commands가 없으면 setAlpha()는 최적화되어 간단하게 Paint 객체에 적절한 alpha를 적용할 수 있다. 이것은 View.hasOverlappingRendering()이 true를 반환할 때 일어난다. 백그라운드 drawable이 없는 ImageView들과 TextView들이 흔한 최적화 될 만한 대상이다. 만약 이런 경우에는 setAlpha()를 원하는 대로 사용해도 된다.
2. 만약 View에 중첩된 drawing commands가 있으면 setAlpha()는 Canvas.saveLayerAlpha() 동작이 된다. 이 특별한 command는 렌더러에게 뒤이어 일어나는 드로잉 작업들을 offscreen 버퍼의 목표로 하게 한다. (소프트웨어 렌더링으로 Bitmap을, OpenGL 텍스처/레이어/FBO를 하드웨어 렌더링으로) 이 작업은 여러 가지 이유로 매우 손실이 크다.
(2. If your View contains overlapping drawing commands, setAlpha() becomes a Canvas.saveLayerAlpha() operation. This particular command instructs the renderer to target subsequent drawing operations to an offscreen buffer (a Bitmap in software rendering, an OpenGL texture/layer/FBO in hardware rendering.) This operation is very expensive for several reasons )
a. 버퍼를 할당(소프트웨어 렌더링은 매번)하거나 재활용된 버퍼를 비워야(하드웨어 렌더링) 할 것이다.
b. 사실상 당신이 그리고 있는 픽셀의 수를 두배로 늘리기 때문에 앱이 fillrate limited이면 이슈가 발생 할 수 있다.
c. 프레임 중간에 렌더링 목표를 다시 잡는 것은 GPU에 따라 매우 손실이 클 수 있다.
3. View가 하드웨어나 소프트웨어 레이어를 가지고 있다면 (View.setLayerType()) setAlpha()는 레이어에 직접 적용될 것이고 이것은 거의 완전 무료이다. 메모리 사용량은 늘어나지만 app은 layer가 없을 때 보다 훨씬 잘 동작할 것이다. 너무 자주 업데이트되는 view에 레이어를 사용하는 것은 피하도록 한다.
Not sure if your app suffers from this problem?
The easiest way to detect problematic calls to setAlpha() is to load your app in Hierarchy Viewer, select the root node of the tree and press the “Dump Display Lists” button at the top (its looks like a tree of blue nodes on the right side of the toolbar.) This will dump, in logcat, all the drawing commands that will get processed by our renderer. Simply look for instances of “SaveLayer.”
How to fix it?
Here is what you can do to avoid performance problems with setAlpha():
1. Avoid using setAlpha() on Views with overlapping rendering for long periodes of time. If you need to draw translucent text on top of a translucent black background simply, do so. When you specify a color for your text or your background you can set the alpha channel (for instance android:background=”#7f000000" will give you a 50% translucent black background.)
2. Sometimes #1 is just not practical. In this case use View.setLayerType(). The renderer has special optimizations for layers. The most important one is that these layers are kept from frame to frame (unlike Canvas.saveLayer*().) This reduces the amount of overdraw when your View is not updating.
Framework will help
I have a few ideas on how to optimize setAlpha() automagically for you in some situations. For instance, future versions of Android may be able to reorder your application’s drawing commands to avoid re-targeting rendering partway through the frame and thus keep the GPU happy.