Android强制系统横屏的原理和实现

一、Android屏幕相关的知识

Android横竖屏设置

Android设置横竖屏的入口多种多样,如下:

1、Activity窗口:应用Mainfest中配置screenOrientation参数、Activity中调用setRequestedOrientation(),这是通过设置Activity的screenOrientation属性

2、系统栏屏幕自动旋转:使用systemui包中的RotationLockTile类的handleClick()方法设置的

3、非Activity窗口:指定WindowManager.LayoutParams.screenOrientation属性

注:屏幕旋转会触发onConfigurationChanged(),如果Activity不设置忽略屏幕方向改变,Activity生命周期会重走,可以按项目需求做相应操作

       虽然设置的入口多种多样,中间经过流程也各不相同,但最终的都是调用了PhoneWindowManager的rotationForOrientationLw(int orientation, int lastRotation)的方法(到这一步,已经是最终的切换了,前面还有一些步骤),具体源码就不在贴出,其实大家可以想到,如果修改framework源码,在这里就可实现强制横屏了

Android的屏幕绘制简介

       关于android绘制原理比较复杂,特别是具体到View层面,大致就是通过ViewRootImpl与WMS通信(Bind通信),完成performTraversals -> performMeasure() -> performLayout() -> performDraw()流程,最终通过canvs在surface上绘制,生成FrameBuffer去显示,具体细节与本文主题关系不大,暂不深入。时序图如下:

                                          

        这里我们主要说下view、window、surface、surfaceFlinger的关系:

       View:android 视图组成的基本控件

       Window:屏幕上的某块显示区域,用来承载 View(PhoneWinow就是window的实现类),主要用来提供添加,移除,渲染的能力(继承ViewManager),事件响应,如Toast,状态栏,Activity界面,Dialog,包括锁屏界面(这个有点特殊),window是Z-order的,即window在Z轴是有层级关系的

      Surface:对应一块屏幕缓冲区,每个 window 对应一个 Surface

      SurfaceFlinger:管理surface,Android 的一个服务进程,负责管理 Surface

      WindowManagerService(WMS):Android 框架层的一个服务进程,用来管理 Window

      下面三张图可以帮助大家了解下:

                                                             

              

                                     

                                        

Window的层级

通过上面知识,大家了解到window是有层级的,在系统中,WindowManager中使用WindowManager.LayoutParams的type字段来存储window的层级,由于window本身存在父子关系,具体层级关系如下: 

窗口层级(type)

 层级值

Application windows(应用窗口)

1~99

Sub-windows(子窗口)

1000~1999

System windows(系统窗口)

2000~2999

 

注意:从表面上看系统窗口 > 子窗口 > 应用窗口,从数值上来说是正确的,但是如果说 Window Layer的层级关系(大部分情况都是按照层级关系),这个说法就是错误的,举个很简单的例子,壁纸层 属于系统窗口管理,但是Window Layer的层级关系 却是最底端;WindowType 的值划分是为了更好的管理Window的类别、权限、依赖关系,和实际的Window Layer层级并没有关系。

从层级到Z-order(android定义的z轴界面层级,有1-33,33级)android有一套对应关系,具体在WindowManagerPolicy的getWindowLayerFromTypeLw()方法中,源码就不贴出了,部分对应和z轴定义如下截图

最终在Framework中计算方式:Layer = Z-Order * 10000 + 1000,即SurfaceFlinger中的混合顺序,我们可以通过adb shell dumpsys SurfaceFlinger来看下层级,下面截图是我在强制横屏demo下场景下截的图(如果window没有长宽,是不会在SurfaceFlinger中显示)

横竖屏设置选项

 这里我们介绍下横竖屏的设置选项。默认是unspecified,即由系统决定

               https://img-blog.csdn.net/20170401105803415?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXF3dXlfbXV6aQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

注意:虽然Android只提供了几种旋转角度,但其实framework可以设置任意角度

二、强制横屏实现

       通过前面知识介绍,我们知道了几种设置横竖屏的方法,这里使用添加高layer的window并设置横竖屏

申请在其他应用上层显示权限

1.判断权限

2.申请权限

          

添加window并进行横竖屏设置

由于window本身并不是一个真正view,仅仅是个载体,我们需要添加一个长宽均0的view,代码如下:

1.创建布局

2.添加window

这里有个小技巧,我们使用service去实现,并且stopself(),这样状态栏不会有notification。

可能大家会疑惑,为什么我只是使用WindowManger.addview(),并没有创建新的window,其实使用WindowManager 的addview方法,系统会自动创建一个window,可以通过 Android Studio的layout inspector工具查看页面

                                           

三、原理介绍                                

屏幕方向的确定的过程

从上到下遍历窗口堆栈,在可见的窗口中确定一个基准screenOrientation

1、非Activity窗口的screenOrientation如果不是(SCREEN_ORIENTATION_UNSPECIFIED、SCREEN_ORIENTATION_BEHIND)两个值,则结束遍历

2、一旦遍历到一个Activity窗口,接下来仅在Activity窗口中遍历,不再关心非Activity窗口总结来讲就是根据窗口的优先级高低来确定屏幕旋转方向,这也是我们方案的基本原理

 

 

转屏的准备

大家可能考虑到,系统检测到要改变屏幕方向,接下来屏幕上能看到的所有窗口都要以新的尺寸来重新绘制,由于各个窗口的重绘不可能在同一时刻完成,也不可能同时刷新,如果我们什么都不做,在屏幕上将会看到各种闪烁。这个应该怎么办?

系统解决方案:

1、截屏保存屏幕状态 (WindowManagerService.screenRotationAnimation())

2、用第一步的截屏生成一个非常高层级的,完全不透明的Layer,让用户在视觉上一直停留在竖屏状态,由于是完全不透明,所以在它下方的所有内容完全不可见,那么就尽情地绘制吧(WindowManagerService .updateRotationUncheckedLocked())

3、方向改变前哪些窗口可见,就必须等待那些窗口绘制完成,这个很好理解

4、截屏Layer为整体,施加旋转并淡出的动画,重绘的所有横屏界面为另一个整体,也施加一个旋转动画,这样当动画结束时看到的就是重绘后的横屏界面。还有一点需要注意,转屏过程中系统会禁用所有其它动画,避免动画的叠加。  

(WMS.startAnimation()->
WMS.performSurfacePlacementInner->
WindowAnimator.animateLocked()->
WindowStateAnimator.computeShownFrameLocked())
过程演示图如下:

                 

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值