说明
本文所述解决方案具有 特殊性, 针对性, 需结合系统当前实现的屏幕方向设置方案.
仅供参考 请勿生搬硬套
*本文无动画流程解读说明, 请移步
*本文无固定屏幕方向方案解读说明, 请移步
平台
RK3288 + Android 7.1
概述
固定了屏幕方向后, 默认不旋转时, 所有的显示都正常, 但是, 在旋转后, 开机动画显示的方向要么不准确, 要么会跳变.
前提
屏幕旋转方向有四个值, 分别为 Surface.ROTATION_0 (默认) Surface.ROTATION_90 Surface.ROTATION_180 Surface.ROTATION_270
分析
|-- frameworks/base/cmds/bootanimation/BootAnimation.cpp
BootAnimation::BootAnimation(bool shutdown) : Thread(false), mClockEnabled(true), mTimeIsAccurate(false), mTimeFormat12Hour(false), mTimeCheckThread(NULL) { mSession = new SurfaceComposerClient(); // If the system has already booted, the animation is not being used for a boot. mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0); mShutdown = shutdown; mReverseAxis = false; mVideoFile = NULL; mVideoAnimation = false; if(mShutdown){ sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); // primary_display_token DisplayInfo dinfo; status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo); if (status == OK) { ALOGD("DISPLAY,W-H: %d-%d, ori: %d", dinfo.w, dinfo.h, dinfo.orientation); if(dinfo.orientation==1 || dinfo.orientation==3 ) mReverseAxis=true; else mReverseAxis=false; } } }
初步怀疑是屏幕旋转导致了问题存在, 于是, 从上面的代码中, 抽取了读取屏幕信息的方法, 并打印出信息.
代码到动画绘制的循环中
bool BootAnimation::android() { //... glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //加入打印显示信息代码 sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); // primary_display_token DisplayInfo dinfo; const nsecs_t startTime = systemTime(); do { //加入打印显示信息代码. status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo); if (status == OK) { ALOGD("DISPLAY,W-H: %d-%d, ori: %d", dinfo.w, dinfo.h, dinfo.orientation); } //... checkExit(); } while (!exitPending()); }
LOG内容如下:
01-03 01:17:31.965 327-376/? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 0
手上的设备是 1920x1080 15.6寸屏, 默认为横屏
当固定屏幕方向旋转90度(竖屏)时, 输出的LOG为:
2019-09-09 09:31:39.731 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 0 //还有很多 2019-09-09 09:31:47.471 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 1 //还有很多
通过观察, 旋转问题出现时确实是在屏幕方向变化时, 另外, 还有一个问题, 第一个动画的界面, 方向依然是默认的0度横屏方向.
总结下问题有两个:
动画启动界面方向为默认零度, 不随系统设置变化.
动画启动过程中, 会出来旋转并导致显示内容不完整, 如图:
解决动画启动界面方向:
|-- frameworks/base/cmds/bootanimation/BootAnimation.cpp
BootAnimation::BootAnimation(bool shutdown) : Thread(false), mClockEnabled(true), mTimeIsAccurate(false), mTimeFormat12Hour(false), mTimeCheckThread(NULL) { mSession = new SurfaceComposerClient(); // If the system has already booted, the animation is not being used for a boot. mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0); mShutdown = shutdown; mReverseAxis = false; mVideoFile = NULL; mVideoAnimation = false; //注释mShotdown判断, 启动时, 方向有可能设置为0以外的数值 //if(mShutdown){ sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); // primary_display_token DisplayInfo dinfo; status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo); if (status == OK) { ALOGD("DISPLAY,W-H: %d-%d, ori: %d", dinfo.w, dinfo.h, dinfo.orientation); //解决启动时方向错误, 其中, 变量 rot根据需要的方向设置[0, 3]; int rot = 1;//90度 if (rot >= 0) { int w = dinfo.w; int h = dinfo.h; if(rot == 1 || rot == 3){//竖屏时, xy轴需要对调 mReverseAxis=true; h = dinfo.w; w = dinfo.h; }else{ mReverseAxis=false; } Rect layerStackRect(w, h); Rect displayRect(0, 0, w, h); SurfaceComposerClient::setDisplayProjection(dtoken, rot, layerStackRect, displayRect); } } //} } status_t BootAnimation::readyToRun() { mAssets.addDefaultAssets(); sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); DisplayInfo dinfo; status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo); if (status) return -1; // create the native surface int curWidth = dinfo.w; int curHeight = dinfo.h; //注释 mShutdown, 屏幕旋转不只发生在关机动画时. if(/*mShutdown && */mReverseAxis){ curWidth = dinfo.h; curHeight = dinfo.w; } //...省略代码 }
解决动画绘制过程中方向跳变.
动画中打印的LOG如下:
//启动正常 2019-09-09 10:41:20.662 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 0 2019-09-09 10:41:20.818 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 1 //省略 //到这时转回默认方向, 2019-09-09 10:41:25.101 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 0 //省略 //最后转回正常方向 2019-09-09 10:41:29.438 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 1
|–frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private WindowManagerService(Context context, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { //... 省略代码 // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); //设置默认旋转方向, 与动画的默认方向保持一致 mRotation = Surface.ROTATION_90; SurfaceControl.openTransaction(); try { createWatermarkInTransaction(); } finally { SurfaceControl.closeTransaction(); } showEmulatorDisplayOverlayIfNeeded(); } /** * Updates the current rotation. * * Returns true if the rotation has been changed. In this case YOU * MUST CALL sendNewConfiguration() TO UNFREEZE THE SCREEN. */ public boolean updateRotationUncheckedLocked(boolean inTransaction) { //省略大量代码 if (DEBUG_ORIENTATION) { Slog.v(TAG_WM, "Selected orientation " + mLastOrientation + ", got rotation " + rotation + " which has " + (altOrientation ? "incompatible" : "compatible") + " metrics"); } //保持默认方向 /*if (mRotateOnBoot) { mRotation = Surface.ROTATION_0; rotation = Surface.ROTATION_90; }*/ if (mRotation == rotation && mAltOrientation == altOrientation) { // No change. return false; } //省略大量代码 }
转屏相关代码记录:
以下流程存在多次调用, 分析时需仔细
com.android.server.wm.WindowManagerService.displayReady(WindowManagerService.java:8781) com.android.server.am.ActivityManagerService.updateConfiguration(ActivityManagerService.java:19241) com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19255) com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19261) com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19393) com.android.server.wm.WindowManagerService.setNewConfiguration(WindowManagerService.java:4413) com.android.server.wm.WindowManagerService.onConfigurationChanged(WindowManagerService.java:4448) com.android.server.wm.WindowManagerService.reconfigureDisplayLocked(WindowManagerService.java:9952) com.android.server.wm.WindowManagerService.sendNewConfiguration(WindowManagerService.java:8035) com.android.server.am.ActivityManagerService.updateConfiguration(ActivityManagerService.java:19241) com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19255) com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19261) com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19424) com.android.server.wm.WindowManagerService.continueSurfaceLayout(WindowManagerService.java:5957) com.android.server.wm.WindowSurfacePlacer.continueLayout(WindowSurfacePlacer.java:173) com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:184) com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:235) com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementInner(WindowSurfacePlacer.java:320) com.android.server.wm.WindowSurfacePlacer.applySurfaceChangesTransaction(WindowSurfacePlacer.java:884) com.android.server.display.DisplayManagerService$LocalService.performTraversalInTransactionFromWindowManager(DisplayManagerService.java:1671) com.android.server.display.DisplayManagerService.performTraversalInTransactionFromWindowManagerInternal(DisplayManagerService.java:338) com.android.server.display.DisplayManagerService.performTraversalInTransactionLocked(DisplayManagerService.java:892) com.android.server.display.DisplayManagerService.configureDisplayInTransactionLocked(DisplayManagerService.java:983) com.android.server.display.LogicalDisplay.configureDisplayInTransactionLocked(LogicalDisplay.java:426) com.android.server.display.DisplayDevice.setProjectionInTransactionLocked(DisplayDevice.java:194) com.android.server.display.DisplayDevice.setDisplayProjection()
最终更新屏幕显示:
frameworks/base/services/core/java/com/android/server/display/DisplayDevice.java
/** * Sets the display projection while in a transaction. * * @param orientation defines the display's orientation * @param layerStackRect defines which area of the window manager coordinate * space will be used * @param displayRect defines where on the display will layerStackRect be * mapped to. displayRect is specified post-orientation, that is * it uses the orientation seen by the end-user */ public final void setProjectionInTransactionLocked(int orientation, Rect layerStackRect, Rect displayRect) { if (mCurrentOrientation != orientation || mCurrentLayerStackRect == null || !mCurrentLayerStackRect.equals(layerStackRect)