Android 7.1 解决开机动画旋转问题

简介: Android 7.1 解决开机动画旋转问题

说明


本文所述解决方案具有 特殊性, 针对性, 需结合系统当前实现的屏幕方向设置方案.

仅供参考 请勿生搬硬套


*本文无动画流程解读说明, 请移步

*本文无固定屏幕方向方案解读说明, 请移步


平台


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度横屏方向.


总结下问题有两个:


动画启动界面方向为默认零度, 不随系统设置变化.

动画启动过程中, 会出来旋转并导致显示内容不完整, 如图:

image.png


解决动画启动界面方向:


|-- 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)


相关文章
|
7月前
|
Java API 调度
Android系统 自定义开机广播,禁止后台服务,运行手动安装应用接收开机广播
Android系统 自定义开机广播,禁止后台服务,运行手动安装应用接收开机广播
337 0
|
7月前
|
安全 Shell Android开发
Android系统 init.rc开机执行shell脚本
Android系统 init.rc开机执行shell脚本
1181 0
|
4月前
|
存储 Shell Android开发
基于Android P,自定义Android开机动画的方法
本文详细介绍了基于Android P系统自定义开机动画的步骤,包括动画文件结构、脚本编写、ZIP打包方法以及如何将自定义动画集成到AOSP源码中。
79 2
基于Android P,自定义Android开机动画的方法
|
2月前
|
Android开发 UED
Android 中加载 Gif 动画
【10月更文挑战第20天】加载 Gif 动画是 Android 开发中的一项重要技能。通过使用第三方库或自定义实现,可以方便地在应用中展示生动的 Gif 动画。在实际应用中,需要根据具体情况进行合理选择和优化,以确保用户体验和性能的平衡。可以通过不断的实践和探索,进一步掌握在 Android 中加载 Gif 动画的技巧和方法,为开发高质量的 Android 应用提供支持。
|
7月前
|
Java Android开发 开发者
Android10 修改开发者选项中动画缩放默认值
Android10 修改开发者选项中动画缩放默认值
198 0
|
7月前
|
XML Java Android开发
android的三种动画
android的三种动画
38 0
|
5月前
|
XML Android开发 数据格式
Android 中如何设置activity的启动动画,让它像dialog一样从底部往上出来
在 Android 中实现 Activity 的对话框式过渡动画:从底部滑入与从顶部滑出。需定义两个 XML 动画文件 `activity_slide_in.xml` 和 `activity_slide_out.xml`,分别控制 Activity 的进入与退出动画。使用 `overridePendingTransition` 方法在启动 (`startActivity`) 或结束 (`finish`) Activity 时应用这些动画。为了使前 Activity 保持静止,可定义 `no_animation.xml` 并在启动新 Activity 时仅设置新 Activity 的进入动画。
121 12
|
5月前
|
XML Android开发 UED
Android动画之共享元素动画简单实践
本文介绍Android共享元素动画, 实现两Activity间平滑过渡特定UI元素。通过设置`transitionName`属性和使用`ActivityOptions.makeSceneTransitionAnimation`启动目标Activity实现动画效果。可自定义过渡动画提升体验。
73 0
|
6月前
|
Android开发 UED
Android Item平移动画
【6月更文挑战第18天】
109 8