Descripción
La solución descrita en el presente documento tiene la particularidad, específica para ser combinado con el sistema implementado actualmente esquema de ajuste de orientación de la pantalla .
Para referencia no hace mecánicamente
* No hay una explicación del flujo de animación en este artículo, muévalo
* No hay una explicación del esquema de orientación de la pantalla fija en este artículo, muévase
plataforma
RK3288 + Android 7.1
Visión de conjunto
Una vez que se fija la orientación de la pantalla, todas las pantallas son normales cuando el valor predeterminado no es girar, pero después de girar, la orientación de la pantalla de animación de inicio es inexacta o salta.
premisa
屏幕旋转方向有四个值, 分别为
Surface.ROTATION_0 (默认)
Surface.ROTATION_90
Surface.ROTATION_180
Surface.ROTATION_270
análisis
| - 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;
}
}
}
La sospecha inicial es que la rotación de la pantalla está causando el problema, por lo que a partir del código anterior, se extrae el método de lectura de la información de la pantalla y se imprime la información. El
código está en el bucle del dibujo de animación
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());
}
El contenido de LOG es el siguiente:
01-03 01:17:31.965 327-376/? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 0
El dispositivo en la mano es una pantalla de 1920x1080 de 15,6 pulgadas, el valor predeterminado es la pantalla horizontal
Cuando la dirección de la pantalla fija se gira 90 grados (pantalla vertical), el LOG de salida es:
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
//还有很多
A través de la observación, el problema de rotación es de hecho cuando cambia la orientación de la pantalla. Además, hay otro problema. La primera interfaz de animación, la orientación sigue siendo la orientación horizontal predeterminada de la pantalla de 0 grados.
En resumen, hay dos problemas:
- La dirección de la interfaz de inicio de la animación es el cero grado predeterminado, que no cambia con la configuración del sistema.
- Durante el proceso de inicio de la animación, rotará y hará que el contenido de la pantalla esté incompleto, como se muestra en la figura:
Resuelva la dirección de la interfaz de inicio de la animación:
| - 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;
}
//...省略代码
}
Resuelve el salto de dirección durante el proceso de dibujo de la animación.
El LOG impreso en la animación es el siguiente:
//启动正常
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;
}
//省略大量代码
}
Registros de códigos relevantes para el cambio de pantalla:
Hay varias llamadas a los siguientes procesos, así que tenga cuidado al analizar
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()
Finalmente actualice la visualización de la pantalla:
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)
|| mCurrentDisplayRect == null
|| !mCurrentDisplayRect.equals(displayRect)) {
mCurrentOrientation = orientation;
if (mCurrentLayerStackRect == null) {
mCurrentLayerStackRect = new Rect();
}
mCurrentLayerStackRect.set(layerStackRect);
if (mCurrentDisplayRect == null) {
mCurrentDisplayRect = new Rect();
}
mCurrentDisplayRect.set(displayRect);
//与Bootanimation中的函数类似
SurfaceControl.setDisplayProjection(mDisplayToken,
orientation, layerStackRect, displayRect);
}
}