WindowManagerService一个解决界面闪屏的例子
问题描述:手机插入USB后点击切换USB连接模式,出现闪屏。
首先分析WMS的问题,手机是默认没有开启相关Log的,需要开启相关Log来进行调试,开启方法:
1.adb shell命令动态打开,不需要build codebase
adb shell dumpsys window -d enable a
PS:不需要用adb shell stop/start重启adb,重启手机或者重启adb后都需要重新开启
2.修改WindowManagerService.java中相关的code
将windowManagerService.java中的所有带DEBUG标志的flag(比如DEBUG,DEBUG_XX)都修改为true,然后重新build frameworks/base/services/core 模块
首先在Settings->Developer Options->Window animation AppTransition animation Animation scale 这3个item,都调为off,关闭动画再做测试,发现问题依然存在,排除动画切换方面的因素。
然后我们怀疑dim layer的z-order值发生了变化,但是按上叙方法打印的Log中dim layer的layer值没有打印出来,需要在DimLayer.java中添加log如下:
void setLayer(int layer) {
Slog.d(TAG,"setLayer: new layer = "+layer+“,mLayer=”+mLayer);// add log
if (mLayer != layer) {
mLayer = layer;
mDimSurface.setLayer(layer);
}
}
加了Log以后,从log从发现如下信息:
usbmode的activity从visible到invisible到过程中,dim layer的z-order值有发生异常变化,z-order被加了1000
01-01 12:40:08.464 AppWindowToken{8f2e46a token=Token{8931255 ActivityRecord{9b9205e u0 com.android.settings/.deviceinfo.UsbModeChooserActivity t28}}} Visible
01-01 12:40:08.457957 998 1030 D DimLayer-1-AnimBg: setLayer: new layer = 21059, mLayer = 21054
01-01 12:40:17.410351 998 1030 D DimLayer-1-AnimBg: setLayer: new layer = 21039, mLayer = 21059
01-01 12:40:17.632218 998 1030 D DimLayer-1-AnimBg: setLayer: new layer = 22039, mLayer = 21039 //在这一次发生跳变
01-01 12:40:17.722028 998 1030 D DimLayer-1-AnimBg: setLayer: new layer = 22039, mLayer = 22039
01-01 12:40:17.725 AppWindowToken{8f2e46a token=Token{8931255 ActivityRecord{9b9205e u0 com.android.settings/.deviceinfo.UsbModeChooserActivity t28}}} Invisible
这就算引起切换模式时闪烁的原因.找到地方后继续添加一些log,把调用信息也打印出来:
void setLayer(int layer) {
Slog.d(TAG,"setLayer: new layer = "+layer+“,mLayer=”+mLayer,new Throwable("setLayer"));//add log
if (mLayer != layer) {
mLayer = layer;
mDimSurface.setLayer(layer);
}
}
打印出完整的log:
Line 8085: 07-03 19:54:26.980 981 3846 D ActivityManager: ACT-AM_FINISH_ACTIVITY ActivityRecord{d5725e7 u0 com.android.settings/.deviceinfo.UsbModeChooserActivity t19 f} task:TaskRecord{593a7a6 #19 I=com.android.settings/.deviceinfo.UsbModeChooserActivity U=0 StackId=1 sz=1} app-request
Line 8132: 07-03 19:54:26.996 981 3846 D ActivityManager: at com.android.server.am.ActivityStack.finishActivityLocked(ActivityStack.java:3793)
07-03 19:54:27.267 981 5365 V WindowManager: **** GOOD TO GO, Callers=com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementInner:341 com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop:239 com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement:187 com.android.server.wm.WindowManagerService.executeAppTransition:4442 com.android.server.am.ActivityStackSupervisor.reportResumedActivityLocked:3020 com.android.server.am.ActivityStack.completeResumeLocked:1547
07-03 19:54:27.270 981 5365 V WindowManager: applyAnimation: anim=android.view.animation.AnimationSet@20a299c animAttr=0xb transit=TRANSIT_TASK_CLOSE isEntrance=false Callers=com.android.server.wm.WindowManagerService.applyAnimationLocked:3508 com.android.server.wm.WindowManagerService.setTokenVisibilityLocked:4716 com.android.server.wm.WindowSurfacePlacer.handleClosingApps:1338
07-03 19:54:27.271 981 5365 I WindowManager: Loaded animation android.view.animation.AnimationSet@20a299c for AppWindowToken{a663983 token=Token{7429832 ActivityRecord{d5725e7 u0 com.android.settings/.deviceinfo.UsbModeChooserActivity t19}}}
07-03 19:54:27.271 981 5365 I WindowManager: java.lang.RuntimeException
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.wm.WindowManagerService.logWithStack(WindowManagerService.java:2866)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.wm.WindowManagerService.applyAnimationLocked(WindowManagerService.java:3512)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.wm.WindowManagerService.setTokenVisibilityLocked(WindowManagerService.java:4716)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.wm.WindowSurfacePlacer.handleClosingApps(WindowSurfacePlacer.java:1338)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.wm.WindowSurfacePlacer.handleAppTransitionReadyLocked(WindowSurfacePlacer.java:1215)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementInner(WindowSurfacePlacer.java:341)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:239)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:187)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.wm.WindowManagerService.executeAppTransition(WindowManagerService.java:4442)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.am.ActivityStackSupervisor.reportResumedActivityLocked(ActivityStackSupervisor.java:3020)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.am.ActivityStack.completeResumeLocked(ActivityStack.java:1547)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.am.ActivityStack.resumeTopActivityInnerLocked(ActivityStack.java:2787)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.am.ActivityStack.resumeTopActivityUncheckedLocked(ActivityStack.java:2251)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.am.ActivityStackSupervisor.resumeFocusedStackTopActivityLocked(ActivityStackSupervisor.java:1870)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1450)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.am.ActivityStack.activityPausedLocked(ActivityStack.java:1276)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.am.ActivityManagerService.activityPaused(ActivityManagerService.java:7502)
07-03 19:54:27.271 981 5365 I WindowManager: at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:573)
07-03 19:54:27.271 981 5365 I WindowManager: at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2949)
07-03 19:54:27.271 981 5365 I WindowManager: at android.os.Binder.execTransact(Binder.java:570)
07-03 19:54:27.271 981 5365 V WindowManager: Setting animation in AppWindowToken{a663983 token=Token{7429832 ActivityRecord{d5725e7 u0 com.android.settings/.deviceinfo.UsbModeChooserActivity t19}}}: android.view.animation.AnimationSet@20a299c wxh=1440x2272 isVisible=true
07-03 19:54:27.271 981 5365 V WindowManager: Updating layer Window{7538942 u0 com.android.settings/com.android.settings.deviceinfo.UsbModeChooserActivity}: 22005
Line 9656: 07-03 19:54:27.277 981 5365 V WindowManager: Assign layer Window{7538942 u0 com.android.settings/com.android.settings.deviceinfo.UsbModeChooserActivity}: mBase=21000 mLayer=21005 mAppLayer=1000 =mAnimLayer=22005
Line 9854: 07-03 19:54:27.308 981 1306 I WindowManager: SURFACE 7538942: controller=Surface b0888f9 com.android.settings/com.android.settings.deviceinfo.UsbModeChooserActivity (0): shown=true layer=21005 alpha=1.0 528.0,3539.0 384x384 crop=[0,0][384,384] opaque=false (1.0,0.0,0.0,1.0)alpha=0.0 layer=22005 matrix=[1.0*1.0,0.0*1.0][0.0*1.0,1.0*1.0] / com.android.settings/com.android.settings.deviceinfo.UsbModeChooserActivity
Line 9854: 07-03 19:54:27.308 981 1306 I WindowManager: SURFACE 7538942: controller=Surface b0888f9 com.android.settings/com.android.settings.deviceinfo.UsbModeChooserActivity (0): shown=true layer=21005 alpha=1.0 528.0,3539.0 384x384 crop=[0,0][384,384] opaque=false (1.0,0.0,0.0,1.0)alpha=0.0 layer=22005 matrix=[1.0*1.0,0.0*1.0][0.0*1.0,1.0*1.0] / com.android.settings/com.android.settings.deviceinfo.UsbModeChooserActivity
Line 9856: 07-03 19:54:27.309 981 1306 V WindowManager: setLayer(22005): OLD:Surface b0888f9 com.android.settings/com.android.settings.deviceinfo.UsbModeChooserActivity (0): shown=true layer=21005 alpha=0.0 528.0,3539.0 384x384 crop=[0,0][384,384] opaque=false (1.0,0.0,0.0,1.0). Called by com.android.server.wm.WindowSurfaceController.prepareToShowInTransaction:320 com.android.server.wm.WindowStateAnimator.prepareSurfaceLocked:1691 com.android.server.wm.WindowAnimator.animateLocked:774
//finish过程中明显看到有layer调整比之前高了。
Line 8665: 07-03 19:54:27.125 981 1306 V WindowManager: destroy: Surface 794f78e com.android.settings/com.android.settings.deviceinfo.UsbModeChooserActivity (0): shown=false layer=21015 alpha=0.0 -92.0,303.0 1624x1857 crop=[92,0][1532,1857] opaque=false (1.0,0.0,0.0,1.0). Called by com.android.server.wm.WindowSurfaceController.destroyInTransaction:161 com.android.server.wm.WindowStateAnimator.destroySurface:2154 com.android.server.wm.WindowStateAnimator.destroySurfaceLocked:947
Line 10401: 07-03 19:54:27.416 981 5365 V WindowManager: destroy: Surface b0888f9 com.android.settings/com.android.settings.deviceinfo.UsbModeChooserActivity (0): shown=false layer=22005 alpha=0.0 528.0,1040.0 384x384 crop=[0,0][384,384] opaque=false (1.0,0.0,0.0,1.0). Called by com.android.server.wm.WindowSurfaceController.destroyInTransaction:161 com.android.server.wm.WindowStateAnimator.destroySurface:2154 com.android.server.wm.WindowStateAnimator.destroySurfaceLocked:947
可以看出导致闪屏的原因是因为finish该UsbModeChooserActivity的时候,由于其是半透明的,并且它有两个不同layer 的window,WMS会将其中一个window的Layer调整比之前高,进而做窗口切换时导致的闪屏。
解法:在window做退出动画前,提前将animLayerAdjustment 提高避免闪屏。
/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
import static com.android.server.wm.AppTransition.TRANSIT_TASK_CLOSE; // add this
import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN_BEHIND;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
@Override
public void setAppVisibility(IBinder token, boolean visible) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppVisibility()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
AppWindowToken wtoken;
synchronized(mWindowMap) {
wtoken = findAppWindowToken(token);
if (wtoken == null) {
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
return;
}
if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION || !IS_USER_BUILD)
Slog.v(TAG_WM, "setAppVisibility(" +
token + ", visible=" + visible + "): " + mAppTransition +
" hidden=" + wtoken.hidden + " hiddenRequested=" +
wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
//add begin
if(wtoken!=null&&visible==false&&mAppTransition.getAppTransition()==TRANSIT_TASK_CLOSE
&&wtoken.hidden==false){
Slog.v(TAG_WM,wtoken +"going to do exist animation so adjust animLayerAdjustment to 1000");
wtoken.mAppAnimator.animLayerAdjustment =1000;
} //add end
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
编译版本后问题不再复现。本文是基于Android M的一个例子。后面的Android版本代码可能有变动,但是基本思路不变。