WindowManager为App提供了一个可以在指定的窗口下插入阴影图层或者模糊背景图层的方法,达到使该窗口之下的所有窗口变暗或者模糊的效果,本文首先分析一下这种效果的大致实现,接着探究一下实现过程中涉及到的相对Layer的设置流程。
1 DimLayer
1.1 dim和blur介绍
1.1.1 dim
为了将该窗口之下的所有窗口变暗,需要为该窗口设置窗口flag,FLAG_DIM_BEHIND。
/** Window flag: everything behind this window will be dimmed.
* Use {@link #dimAmount} to control the amount of dim. */
public static final int FLAG_DIM_BEHIND = 0x00000002;
并且App也可以设置变暗效果的程度:
/**
* When {@link #FLAG_DIM_BEHIND} is set, this is the amount of dimming
* to apply. Range is from 1.0 for completely opaque to 0.0 for no
* dim.
*/
public float dimAmount = 1.0f;
这个参数只有在窗口设置了FLAG_DIM_BEHIND的时候才会生效,用来表示窗口变暗的程度,从完全不透明的1.0到没有变暗效果的0。
1.1.2 blur
为了将该窗口之下的所有窗口变模糊,需要为该窗口设置窗口flag,FLAG_DIM_BEHIND。
/** Window flag: enable blur behind for this window. */
public static final int FLAG_BLUR_BEHIND = 0x00000004;
并且App也可以设置模糊效果的程度:
/**
* Specifies the amount of blur to be used to blur everything behind the window.
* The effect is similar to the dimAmount, but instead of dimming, the content behind
* will be blurred.
*
* The blur behind radius range starts at 0, which means no blur, and increases until 150
* for the densest blur.
*
* @see #setBlurBehindRadius
*/
private int mBlurBehindRadius = 0;
这个参数只有在窗口设置了FLAG_BLUR_BEHIND的时候才会生效,用来表示窗口模糊的程度,从没有模糊的0到最模糊的150。
1.2 DimLayer实现
1.2.1 WindowState.prepareSurfaces
@Override
void prepareSurfaces() {
mIsDimming = false;
applyDims();
updateSurfacePositionNonOrganized();
// Send information to SurfaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
if (isVisibleRequested()) updateScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
super.prepareSurfaces();
}
DimLayer的添加是在WindowSetate.prepareSurfaces中,在WMS向SurfaceFlinger提交Transaction之前。
1.2.2 WindowState.applyDims
private void applyDims() {
if (!mAnimatingExit && mAppDied) {
mIsDimming = true;
getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
} else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind())
&& isVisibleNow() && !mHidden) {
// Only show the Dimmer when the following is satisfied:
// 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested
// 2. The WindowToken is not hidden so dims aren't shown when the window is exiting.
// 3. The WS is considered visible according to the isVisible() method
// 4. The WS is not hidden.
mIsDimming = true;
final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
final int blurRadius = shouldDrawBlurBehind() ? mAttrs.getBlurBehindRadius() : 0;
getDimmer().dimBelow(getSyncTransaction(), this, dimAmount, blurRadius);
}
}
Dimmer类的定义是:
/**
* Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
* black layers of varying opacity at various Z-levels which create the effect of a Dim.
*/
class Dimmer {
实用程序类,用于WindowContainer实现添加“DimLayer”支持,DimLayer是在多种Z轴级别上的不同不透明度的黑色层,可以显示一个Dim效果。
这里说明了Dimmer要显示的情形需要满足的条件:
1)、相关窗口必须声明FLAG_DIM_BEHIND或者FLAG_BLUR_BEHIND。
2)、WindowToken没有隐藏,防止Dimmer不会在窗口正在退出的时候显示。
3)、根据isVisible方法,当前WindowState可见。
4)、当前WindowState没有隐藏。
如果以上情况均满足,那么调用Dimmer.dimBelow。
1.2.3 Dimmer.dimBelow
/**
* Like {@link #dimAbove} but places the dim below the given container.
*
* @param t A transaction in which to apply the Dim.
* @param container The container which to dim below. Should be a child of our host.
* @param alpha The alpha at which to Dim.
* @param blurRadius The amount of blur added to the Dim.
*/
void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha,
int blurRadius) {
dim(t, container, -1, alpha, blurRadius);
}
参数为:
- container,需要为处于其下的容器施加阴影效果的那个容器,应该是当前Dimmer的host的子容器。
- alpha,施加变暗效果的时候,需要设置的alpha值。
- blurRadius,施加模糊效果的时候,需要设置的模糊半径。
这里看一下Dimmer的host是什么。
成员变量mHost的定义为:
/**
* The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
* host, some controller of it, or one of the hosts children.
*/
private WindowContainer mHost;
Dimmer绑定的那个WindowContainer,我们可以对host,host的一些控制器,或者是host的子容器施加Dim效果。
mHost在Dimmer的构造方法中传入:
Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
mHost = host;
mSurfaceAnimatorStarter = surfaceAnimatorStarter;
}
目前Dimmer对象创建的地方主要有两处:
1)、Task中:
private Dimmer mDimmer = new Dimmer(this);
2)、DisplayArea的子类Dimmable中:
/**
* DisplayArea that can be dimmed.
*/
static class Dimmable extends DisplayArea<DisplayArea> {
private final Dimmer mDimmer = new Dimmer(this);
1.2.4 Dimmer.dim
private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
float alpha, int blurRadius) {
final DimState d = getDimState(container);
if (d == null) {
return;
}
if (container != null) {
// The dim method is called from WindowState.prepareSurfaces(), which is always called
// in the correct Z from lowest Z to highest. This ensures that the dim layer is always
// relative to the highest Z layer with a dim.
t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
} else {
t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
}
t.setAlpha(d.mDimLayer, alpha);
t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);
d.mDimming = true;
}
这里看到是通过为DimState.mDimLayer设置透明度和背景模糊半径来达到变暗和模糊的效果:
t.setAlpha(d.mDimLayer, alpha);
t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);
这里涉及到了relativeLayer的部分,这个放到后续分析,这里主要看一下DimState.mDimLayer是如何得到的。
在进行透明度等设置之前,首先是调用了getDimState方法。
1.2.5 Dimmer.getDimState
/**
* Retrieve the DimState, creating one if it doesn't exist.
*/
private DimState getDimState(WindowContainer container) {
if (mDimState == null) {
try {
final SurfaceControl ctl = makeDimLayer();
mDimState = new DimState(ctl);
/**
* See documentation on {@link #dimAbove} to understand lifecycle management of
* Dim's via state resetting for Dim's with containers.
*/
if (container == null) {
mDimState.mDontReset = true;
}
} catch (Surface.OutOfResourcesException e) {
Log.w(TAG, "OutOfResourcesException creating dim surface");
}
}
mLastRequestedDimContainer = container;
return mDimState;
}
这个方法主要做了两个工作:
1)、调用makeDimLayer创建一个Layer:
private SurfaceControl makeDimLayer() {
return mHost.makeChildSurface(null)
.setParent(mHost.getSurfaceControl())
.setColorLayer()
.setName("Dim Layer for - " + mHost.getName())
.setCallsite("Dimmer.makeDimLayer")
.build();
}
- parent设置为host的SurfaceControl,一般即Task的SurfaceControl。
- 设置该Layer是一个纯色Layer,默认颜色是黑色。
- 设置该Layer的名字为“Dim Layer for - ”加上host的名字。
2)、创建了一个纯色Layer后,再创建一个DimState对象,将该Layer保存在DimState.mDimLayer中:
DimState(SurfaceControl dimLayer) {
mDimLayer = dimLayer;
mDimming = true;
final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, (type, anim) -> {
if (!mDimming) {
dimAnimatable.removeSurface();
}
}, mHost.mWmService);
}
这样Dimmer可以通过getDimState方法获取到一个DimState对象,进而拿到一个DimLayer,Dimmer对象,DimState,DimLayer,这三者是一一对应的。
这样Dimmer.getDimState返回一个纯色Layer对应的SurfaceControl,后续就可以对其设置alpha和backgroundBlurRadius等属性,施加变暗和模糊效果。
1.3 小结
1)、DimLayer其实就是一个SurfaceControl,其对应的Layer是一个纯黑Layer,可以通过为其设置透明度来施加变暗效果,或者设置背景模糊半径来施加模糊效果。
2)、持有Dimmer对象的有Task和DisplayArea.Dimmable,这个Dimmer对象可以被它们的子容器通过WindowContainer.getDimmer方法拿到。
3)、Dimmer、DimState和DimLayer三者是一一对应的关系。
2 relativeLayer
可以实现Dim效果的DimLayer已经有了,那么我们要把这个DimLayer插入到哪个位置来实现dimBelow或者dimAbove的效果呢?
这里拿dimBelow的情况说明,回看1.2.4节的Dimmer.dim方法的内容:
if (container != null) {
// The dim method is called from WindowState.prepareSurfaces(), which is always called
// in the correct Z from lowest Z to highest. This ensures that the dim layer is always
// relative to the highest Z layer with a dim.
t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
} else {
t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
}
这里的container不为null,且为想要将其下的所有窗口都Dim的那个窗口,relativeLayer则是-1,跟一下setRelativeLayer是怎么实现的。
2.1 Transaction.setRelativeLayer
/**
* @hide
*/
public Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, int z) {
checkPreconditions(sc);
nativeSetRelativeLayer(mNativeObject, sc.mNativeObject, relativeTo.mNativeObject, z);
return this;
}
调到了JNI。
2.2 android_view_SurfaceControl.nativeSetRelativeLayer
static void nativeSetRelativeLayer(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject,
jlong relativeToObject, jint zorder) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
auto relative = reinterpret_cast<SurfaceControl *>(relativeToObject);
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
transaction->setRelativeLayer(ctrl, relative, zorder);
}
将Java层传入的对象转为Native层对应的对象,然后调用Transaction.setRelativeLayer。
2.3 SurfaceComposerClient.Transaction.setRelativeLayer
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer(
const sp<SurfaceControl>& sc, const sp<SurfaceControl>& relativeTo, int32_t z) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
s->what |= layer_state_t::eRelativeLayerChanged;
s->what &= ~layer_state_t::eLayerChanged;
s->relativeLayerSurfaceControl = relativeTo;
s->z = z;
registerSurfaceControlForCallback(sc);
return *this;
}
1)、为layer_state_s的what添加layer_state_t::eRelativeLayerChanged标记,表示是该Layer的相对Layer发生了改变。
2)、将相对Layer和Z轴信息保存在layer_state_t类型的ComposerState.state中。
2.4 SurfaceFlinger.setClientStateLocked
后续Transaction应用,最终来到了SurfaceFlinger.setClientStateLocked函数。
由于2.3节中为layer_state_t的what添加了layer_state_t::eRelativeLayerChanged标记,那么在这个函数中走的逻辑是:
if (what & layer_state_t::eRelativeLayerChanged) {
// NOTE: index needs to be calculated before we update the state
const auto& p = layer->getParent();
const auto& relativeHandle = s.relativeLayerSurfaceControl ?
s.relativeLayerSurfaceControl->getHandle() : nullptr;
if (p == nullptr) {
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
if (layer->setRelativeLayer(relativeHandle, s.z) &&
idx >= 0) {
mCurrentState.layersSortedByZ.removeAt(idx);
mCurrentState.layersSortedByZ.add(layer);
// we need traversal (state changed)
// AND transaction (list changed)
flags |= eTransactionNeeded|eTraversalNeeded;
}
} else {
if (p->setChildRelativeLayer(layer, relativeHandle, s.z)) {
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
}
这里的layer即之前1.2.5节中介绍的通过Dimmer.makeDimLayer方法创建的那个SurfaceControl对应的Layer,当时设置了该Layer的父Layer为Task对应的Layer,那么此时这里的p就不为空,走的是Layer.setChildRelativeLayer函数。
2.5 Layer.setChildRelativeLayer
bool Layer::setChildRelativeLayer(const sp<Layer>& childLayer,
const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
ssize_t idx = mCurrentChildren.indexOf(childLayer);
if (idx < 0) {
return false;
}
if (childLayer->setRelativeLayer(relativeToHandle, relativeZ)) {
mCurrentChildren.removeAt(idx);
mCurrentChildren.add(childLayer);
return true;
}
return false;
}
1)、首先判断childLayer是否在mCurrentChildren中。
2)、调用childLayer的setRelativeLayer函数。
3)、将childLayer从mCurrentChildren中原来的位置移除,然后重新添加进来。
2.6 Layer.setRelativeLayer
bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get());
if (handle == nullptr) {
return false;
}
sp<Layer> relative = handle->owner.promote();
if (relative == nullptr) {
return false;
}
if (mDrawingState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) &&
mDrawingState.zOrderRelativeOf == relative) {
return false;
}
mFlinger->mSomeChildrenChanged = true;
mDrawingState.sequence++;
mDrawingState.modified = true;
mDrawingState.z = relativeZ;
auto oldZOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote();
if (oldZOrderRelativeOf != nullptr) {
oldZOrderRelativeOf->removeZOrderRelative(this);
}
setZOrderRelativeOf(relative);
relative->addZOrderRelative(this);
setTransactionFlags(eTransactionNeeded);
return true;
}
1)、将从SurfaceComposerClient处传来的Layer句柄转为相对Layer。判断相对Layer的有效性,以及是否做了重复工作。
2)、设置当前Layer的Z轴大小为传入的relativeZ,注意这里直接把relativeZ作为了当前Layer在父Layer中的Z轴顺序,并没有一个相对Z轴顺序的概念。
3)、对当前Layer调用Layer.setZOrderRelativeOf。
4)、对相对Layer调用Layer.addZOrderRelative。
2.6.1 Layer.setZOrderRelativeOf
void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) {
mDrawingState.zOrderRelativeOf = relativeOf;
mDrawingState.sequence++;
mDrawingState.modified = true;
mDrawingState.isRelativeOf = relativeOf != nullptr;
setTransactionFlags(eTransactionNeeded);
}
对于当前Layer,设置以下两个成员变量的值:
// If non-null, a Surface this Surface's Z-order is interpreted relative to.
wp<Layer> zOrderRelativeOf;
bool isRelativeOf{false};
zOrderRelativeOf,如果非空,当前Surface的Z轴将被解释为相对于zOrderRelativeOf的Z轴。
isRelativeOf,一个表示当前Layer是否设置了相对Layer。
2.6.2 Layer.addZOrderRelative
void Layer::addZOrderRelative(const wp<Layer>& relative) {
mDrawingState.zOrderRelatives.add(relative);
mDrawingState.modified = true;
mDrawingState.sequence++;
setTransactionFlags(eTransactionNeeded);
}
对于相对Layer,将当前Layer加入到相对Layer的zOrderRelatives中,zOrderRelatives的定义是:
// A list of surfaces whose Z-order is interpreted relative to ours.
SortedVector<wp<Layer>> zOrderRelatives;
一个Surface的列表,列表中的Surface的Z轴顺序都被解释为相对于我们的Z轴顺序。
截止到这里,setRelativeLayer的工作算是做完了,接着看下相对Layer是如何发挥作用的。
2.7 Layer.traverseInZOrder
在Layer的遍历函数中探究一下:
/**
* Negatively signed relatives are before 'this' in Z-order.
*/
void Layer::traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor) {
// In the case we have other layers who are using a relative Z to us, makeTraversalList will
// produce a new list for traversing, including our relatives, and not including our children
// who are relatives of another surface. In the case that there are no relative Z,
// makeTraversalList returns our children directly to avoid significant overhead.
// However in this case we need to take the responsibility for filtering children which
// are relatives of another surface here.
bool skipRelativeZUsers = false;
const LayerVector list = makeTraversalList(stateSet, &skipRelativeZUsers);
// ......
}
这里主要说明了两种情况:
- 如果有其他Layer以我们为参照使用了相对Z轴顺序(也就是说我们作为了这些Layer的相对Layer),那么makeTraversalList将会为遍历产生一个新的列表,包括以我们为参照的Layer,但是不包括我们的子Layer中使用了相对Layer的Layer。
- 对于没有相对Layer的情况,makeTraversalList直接返回我们的子Layer来避免更多的开销。然而在这种情况下,我们需要负起责任来在这里过滤掉那些使用了相对层级的子Layer。
直接看下Layer.makeTraversalList的内容:
__attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::makeTraversalList(
LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers) {
LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
"makeTraversalList received invalid stateSet");
const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
const State& state = useDrawing ? mDrawingState : mDrawingState;
if (state.zOrderRelatives.size() == 0) {
*outSkipRelativeZUsers = true;
return children;
}
LayerVector traverse(stateSet);
for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
sp<Layer> strongRelative = weakRelative.promote();
if (strongRelative != nullptr) {
traverse.add(strongRelative);
}
}
for (const sp<Layer>& child : children) {
if (child->usingRelativeZ(stateSet)) {
continue;
}
traverse.add(child);
}
return traverse;
}
这个函数返回一个遍历列表。
1)、如果zOrderRelatives的size为0,表示没有Layer将我们视为相对Layer,那么直接返回子Layer列表。
2)、接着遍历zOrderRelatives,zOrderRelatives保存了设置我们为相对Layer的所有Layer,也就是说这些Layer想作为我们的子Layer插入到我们的Layer层级结构之中,那么将这些Layer加入到遍历列表中。
3)、最后遍历子Layer列表,usingRelativeZ函数返回true,表示当前Layer使用了相对层级,那么这些Layer将会被过滤,当它们的相对Layer进行makeTraversalList操作的时候,这些子Layer将会被添加到它们的相对Layer的遍历列表中。
再次返回Layer.traverseInZOrder:
void Layer::traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor) {
// ......
bool skipRelativeZUsers = false;
const LayerVector list = makeTraversalList(stateSet, &skipRelativeZUsers);
size_t i = 0;
for (; i < list.size(); i++) {
const auto& relative = list[i];
if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) {
continue;
}
if (relative->getZ(stateSet) >= 0) {
break;
}
relative->traverseInZOrder(stateSet, visitor);
}
visitor(this);
for (; i < list.size(); i++) {
const auto& relative = list[i];
if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) {
continue;
}
relative->traverseInZOrder(stateSet, visitor);
}
}
后续对从makeTraversalList返回的list进行两次遍历,从makeTraversalList的逻辑可知,该list前部分是相对Layer的部分(如果有的话),后部分是当前Layer的子Layer。
1)、第一次是先对list中Z轴顺序小于0的Layer进行遍历,只有相对Layer才能设置Z轴顺序为负数,子Layer的Z轴顺序都是大于等于0的。
2)、第二次是对list中Z轴顺序大于0的Layer进行遍历。
2.8 小结
再次看下Transaction.setRelativeLayer方法:
public Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, int z) {
checkPreconditions(sc);
nativeSetRelativeLayer(mNativeObject, sc.mNativeObject, relativeTo.mNativeObject, z);
return this;
}
再次分析一下setRelativeLayer方法的三个参数:
- sc,设置了相对Layer的那个Layer,叫他LayerA。
- relativeTo,相对Layer,是一个参照物的角色,叫他LayerB。
- z,LayerA的相对Z轴顺序。
根据2.6节我们得知:
1)、LayerA的isRelativeOf被标记为true,那么LayerA的父Layer后续对其Layer层级结构进行遍历的时候,就会过滤掉LayerA。
2)、LayerA被添加到了LayerB的zOrderRelatives中,那么LayerB在遍历其Layer层级结构的时候,就会把LayerA看作一个自己的子Layer。
那么setRelativeLayer实现的作用是,把LayerA从LayerA所在的Layer层级结构中剥离,并且作为LayerB的子Layer插入到以LayerB为父节点的Layer层级结构中,传参z即LayerA在新的LayerB层级结构中的Z轴顺序。
那么再回看Dimmer.dimBelow的作用,就是创建一个DimLayer,然后将其作为子Layer插入到某个WindowState对应的Layer层级结构中。
由于该DimLayer处于该WindowState对应的Layer的层级结构中,那么该DimLayer会Dim所有层级比该WindowState层级低的Layer,达到将该WindowState之下的所有窗口变暗或者模糊的效果。
3 实际验证
这里根据SurfaceFlinger打印出的信息来验证一下之前分析的内容都是否正确。
拿Google Files进行一下验证,Files显示如下:
此时Files有两个窗口:
Window #8 Window{f19c0b8 u0 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity}:
mDisplayId=0 rootTaskId=36 mSession=Session{4616615 14439:u0a10155} mClient=android.os.BinderProxy@a81971b
mOwnerUid=10155 showForAllUsers=false package=com.google.android.apps.nbu.files appop=NONE
mAttrs={(0,0)(fillxfill) gr=CENTER sim={adjust=pan forwardNavigation} ty=APPLICATION fmt=TRANSPARENT wanim=0x7f140007 surfaceInsets=Rect(64, 64 - 64, 64)
fl=DIM_BEHIND LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
Window #9 Window{c036971 u0 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity}:
mDisplayId=0 rootTaskId=36 mSession=Session{4616615 14439:u0a10155} mClient=android.os.BinderProxy@c43fb18
mOwnerUid=10155 showForAllUsers=false package=com.google.android.apps.nbu.files appop=NONE
mAttrs={(0,0)(fillxfill) sim={adjust=resize forwardNavigation} ty=BASE_APPLICATION wanim=0x10302fe
fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
-
Window#8,type为APPLICATION,子窗口。
-
Window#9,type为BASE_APPLICATION,主窗口。
由于Window#8声明了DIM_BEHIND,并且Window#8层级是高于Window#9的,那么就会在Window#9上盖上一层阴影,即上图显示的效果。
3.1 相对Layer为Window#8,相对Layer值为-1
此时的Dim逻辑是
dim(t, container, -1, alpha, blurRadius);
此处的container为Window#8,相对Layer为Window#8对应的Layer,相对Layer值为-1。
再看下SurfaceFlinger的信息:
+ EffectLayer (Task=36#0) uid=1000
layerStack= 0, z= 12
parent=DefaultTaskDisplayArea#0
zOrderRelativeOf=none
+ ContainerLayer (ActivityRecord{a555aba u0 com.google.android.apps.nbu.files/.home.HomeActivity t36}#0) uid=1000
layerStack= 0, z= 0
parent=Task=36#0
zOrderRelativeOf=none
+ ContainerLayer (c036971 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
layerStack= 0, z= 0
parent=ActivityRecord{a555aba u0 com.google.android.apps.nbu.files/.home.HomeActivity t36}#0
zOrderRelativeOf=none
+ BufferStateLayer (com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=10155
layerStack= 0, z= 0
parent=c036971 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
zOrderRelativeOf=none
+ EffectLayer (Dim Layer for - Task=36#0) uid=1000
layerStack= 0, z= -1
parent=Task=36#0
zOrderRelativeOf=f19c0b8 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
+ ContainerLayer (f19c0b8 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
layerStack= 0, z= 1
parent=ActivityRecord{a555aba u0 com.google.android.apps.nbu.files/.home.HomeActivity t36}#0
zOrderRelativeOf=none
+ BufferStateLayer (com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#1) uid=10155
layerStack= 0, z= 0
parent=f19c0b8 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
zOrderRelativeOf=none
转为更加直观的形式:
SurfaceFlinger打印的信息的规则是,越靠下的Layer层级值越高。
这里唯一不太明朗的是DimLayer:
EffectLayer (Dim Layer for - Task=36#0) uid=1000
的位置比较奇怪:
它的直接父Layer是Window#8对应的Layer:
ContainerLayer (f19c0b8 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
所以我预想中的层级顺序应该是:
+ ContainerLayer (f19c0b8 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
layerStack= 0, z= 1
parent=ActivityRecord{a555aba u0 com.google.android.apps.nbu.files/.home.HomeActivity t36}#0
zOrderRelativeOf=none
+ EffectLayer (Dim Layer for - Task=36#0) uid=1000
layerStack= 0, z= -1
parent=Task=36#0
zOrderRelativeOf=f19c0b8 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
+ BufferStateLayer (com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#1) uid=10155
layerStack= 0, z= 0
parent=f19c0b8 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
zOrderRelativeOf=none
即:
毕竟子Layer应该处于父Layer的下面,但是根据之前在Layer.traverseInZOrder看到的一段注释:
/**
* Negatively signed relatives are before 'this' in Z-order.
*/
那么也可以理解,SurfaceFlinger的信息打印规则是,打印的位置越靠下层级值越高。
以父Layer为基础:
- Z轴顺序为正,打印在父Layer下面,这些是正常Layer。
- Z轴顺序为负,打印在父Layer下面,层级比正常Layer都要低。
说实话,这里画的图也是基于之前的代码分析得到的,但是如果没有分析过代码,只看SurfaceFlinger的信息,还是难以确认DimLayer的直接父Layer是谁,我们还是需要一个更加强力的证据来支撑的我们的分析结果,即某个Layer设置了相对Layer之后,它的父Layer就变为了这个相对Layer。
3.2 相对Layer为Window#8,相对Layer值为100
修改Dim逻辑,将相对Layer值从-1改为100:
dim(t, container, 100, alpha, blurRadius);
那么根据我们之前分析的结果,此时DimLayer仍然处于Window#8对应的Layer的Layer层级结构中,DimLayer的层级值从-1变为100后,DimLayer层级值最高,能够盖住所有的窗口。
此时的Files表现为:
和我们预想的情况一致。
再看下SurfaceFlinger的情况:
+ EffectLayer (Task=9#0) uid=1000
layerStack= 0, z= 5
parent=DefaultTaskDisplayArea#0
zOrderRelativeOf=none
+ ContainerLayer (ActivityRecord{a401f58 u0 com.google.android.apps.nbu.files/.home.HomeActivity t9}#0) uid=1000
layerStack= 0, z= 0
parent=Task=9#0
zOrderRelativeOf=none
+ ContainerLayer (35b4d1 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
layerStack= 0, z= 0
parent=ActivityRecord{a401f58 u0 com.google.android.apps.nbu.files/.home.HomeActivity t9}#0
zOrderRelativeOf=none
+ BufferStateLayer (com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#1) uid=10156
layerStack= 0, z= 0
parent=35b4d1 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
zOrderRelativeOf=none
+ ContainerLayer (be13be5 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
layerStack= 0, z= 1
parent=ActivityRecord{a401f58 u0 com.google.android.apps.nbu.files/.home.HomeActivity t9}#0
zOrderRelativeOf=none
+ BufferStateLayer (com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=10156
layerStack= 0, z= 0
parent=be13be5 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
zOrderRelativeOf=none
zOrderRelativeOf=none
+ EffectLayer (Dim Layer for - Task=9#0) uid=1000
layerStack= 0, z= 100
parent=Task=9#0
zOrderRelativeOf=be13be5 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
转为图:
能够看到DimLayer:
EffectLayer (Dim Layer for - Task=9#0)
层级变为了最高盖在了其他窗口之上,导致所有窗口被施加了一层变暗效果。
但是如果只看SurfaceFlinger的信息的话,应该还是看不出来,此时DimLayer的直接父Layer可能是
parent=Task=9#0
也可能是
zOrderRelativeOf=be13be5 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
比如我也可以说此时DimLayer的直接父Layer是:
parent=Task=9#0
层级关系图为:
不管是它们两者中的哪一个,打印出来的信息都是一样的,显示的效果也是一样的,所以需要继续分析。
3.3 相对Layer为Window#9,相对Layer值为100
修改Dim逻辑,将相对Layer从Window#8替换为Window#9,同时相对Layer值仍然为100。
那么根据我们之前分析的结果,此时DimLayer处于Window#9对应的Layer的Layer层级结构中,DimLayer的层级值为100,那么DimLayer只能盖住层级值比Window#9低的窗口,Window#8是盖不住的。
此时的Files表现为:
和我们预想的情况一致。
再看下SurfaceFlinger的情况:
+ EffectLayer (Task=12#0) uid=1000
layerStack= 0, z= 5
parent=DefaultTaskDisplayArea#0
zOrderRelativeOf=none
+ ContainerLayer (ActivityRecord{7878ca6 u0 com.google.android.apps.nbu.files/.home.HomeActivity t12}#0) uid=1000
layerStack= 0, z= 0
parent=Task=12#0
zOrderRelativeOf=none
+ ContainerLayer (c50b695 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
layerStack= 0, z= 0
parent=ActivityRecord{7878ca6 u0 com.google.android.apps.nbu.files/.home.HomeActivity t12}#0
zOrderRelativeOf=none
+ BufferStateLayer (com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#1) uid=10156
layerStack= 0, z= 0
parent=c50b695 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
zOrderRelativeOf=none
+ EffectLayer (Dim Layer for - Task=12#0) uid=1000
layerStack= 0, z= 100
parent=Task=12#0
zOrderRelativeOf=c50b695 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
+ ContainerLayer (83cf2f0 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
layerStack= 0, z= 1
parent=ActivityRecord{7878ca6 u0 com.google.android.apps.nbu.files/.home.HomeActivity t12}#0
zOrderRelativeOf=none
+ BufferStateLayer (com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=10156
layerStack= 0, z= 0
parent=83cf2f0 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
zOrderRelativeOf=none
转为图:
果然,此时的Dimlayer:
EffectLayer (Dim Layer for - Task=12#0)
被插入到了Window#9对应的Layer:
ContainerLayer (c50b695 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0)
的Layer层级结构中,所以它能够盖住Window#9,即主窗口,却盖不住子窗口Window#8,所以显示结果如上图所示那样。
这里就绝对能说明,此时DimLayer的父Layer肯定不再是
parent=Task=12#0
而是
ContainerLayer (c50b695 com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0)
3.4 相对Layer为Window#9,相对Layer值为-1
这里最后看下,相对Layer为Window#9,把相对Layer值从100恢复为-1的情况。
那么根据我们之前分析的结果,此时DimLayer处于Window#9对应的Layer的Layer层级结构中,DimLayer的层级值为-1,那么DimLayer连Window#9对应的窗口也无法盖住。
此时的Files表现为:
果然如此,查看SurfaceFlinger信息:
+ EffectLayer (Task=15#0) uid=1000
layerStack= 0, z= 5
parent=DefaultTaskDisplayArea#0
zOrderRelativeOf=none
+ ContainerLayer (ActivityRecord{40c40f6 u0 com.google.android.apps.nbu.files/.home.HomeActivity t15}#0) uid=1000
layerStack= 0, z= 0
parent=Task=15#0
zOrderRelativeOf=none
+ EffectLayer (Dim Layer for - Task=15#0) uid=1000
layerStack= 0, z= -1
parent=Task=15#0
zOrderRelativeOf=84626fe com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
+ ContainerLayer (84626fe com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
layerStack= 0, z= 0
parent=ActivityRecord{40c40f6 u0 com.google.android.apps.nbu.files/.home.HomeActivity t15}#0
zOrderRelativeOf=none
+ BufferStateLayer (com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#1) uid=10156
layerStack= 0, z= 0
parent=84626fe com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
zOrderRelativeOf=none
+ ContainerLayer (9be415a com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=1000
layerStack= 0, z= 1
parent=ActivityRecord{40c40f6 u0 com.google.android.apps.nbu.files/.home.HomeActivity t15}#0
zOrderRelativeOf=none
+ BufferStateLayer (com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0) uid=10156
layerStack= 0, z= 0
parent=9be415a com.google.android.apps.nbu.files/com.google.android.apps.nbu.files.home.HomeActivity#0
zOrderRelativeOf=none
转为图:
由于DimLayer现在在Window#9对应的Layer层级结构中,且层级为-1,所以DimLayer现在的层级值最低,无法盖住任何窗口,所以没有在屏幕上显示。
这一节主要是为了和3.1节呼应,证明层级为-1时的DimLayer,的确是先于父Layer打印的。
3.5 小结
这一节从手机的实际表现出发,分析了设置了relativeLayer的效果带来的效果,验证了我们在2.8节总结的内容。
另外我们能够看到,虽然WindowManager为App提供的接口只能实现把DimLayer添加到WindowState这一层,也就是说,DimLayer随着窗口的移除就跟着移除了,但是我们也可以修改代码,设置DimLayer的相对Layer为Task或者ActivityRecord,从而实现把DimLayer添加到Task或者是ActivityRecord这一层之中。具体如何选择,则要看你的Dim效果是想要对整个App生效,还是对某一个Activity生效,或者只是在某个子窗口显示的时候才生效。