google 进入分屏后在横屏模式按home键界面错乱(二)

你确定你了解分屏的整个流程?

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

__biz=MzI1MjMyOTU2Ng==&mid=2247484263&idx=1&sn=031d1a44696364a75bbd173cf0cdb303&chksm=e9e42856de93a140d79686be8d92c067eecd6312f6584638e361ac9e46f9b80ebac52909403f&scene=21#wechat_redirect">Android 关机对话框概率没有阴影故障分析

android recent key长按事件弹起触发近期列表故障分析

__biz=MzI1MjMyOTU2Ng==&mid=2247484145&idx=1&sn=589d41467e4c8927057278dc271a6488&chksm=e9e429c0de93a0d67edd470622f1750d3735ffd7ba1e4000e25c99e6eafc563c2604d7914378&scene=21#wechat_redirect">google 分屏 popup无法显示故障分析

分享此文便是对代码GG的支持,也是爱的表达方式,所以让爱来的猛烈些吧。

代码阅读。请到此处http://androidxref.com 查看原生代码

前情回想:

google 分屏 横屏模式 按home键界面错乱故障分析(一)

上一节我们主要环绕着分屏的那个线进行展开。分析了状态栏出现问题的问题原因。同一时候我们深入定位,跟踪了systemui的启动过程

系统WMS AMS关于分屏的一些方法。同一时候systemUI通过Divider的服务端检測AMS WMS给回来的分屏当前状态。这边进行更新view

同一时候我们找到了AMS WMS里面关于分屏的关键方法attachstack以及detachStackLocked。关注了它的堆栈信息,以及怎么触发到systemUi的界面更新过程

上一讲后面出现了一个笔误。

详细为

我们继续跟踪detachStackLocked流程,会发现我们的notifyDockedStackMinimizedChanged 方法被触发了。

这里由于当时自己的失误,写错了。

notifyDockedStackMinimizedChanged这个是在最小化的时候触发的,我们能够在文章结尾看到。我说的这个就是最小化的流程。

关于detachStackLocked触发了哪个呢?我们看它代码:

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

看,我是写错了。好尴尬。好了,这个就此翻篇了。

我们此讲,開始环绕分屏的启动过程。

00

我们回到触发分屏的地方PhoneStatusBar.java 里面

(详细能够在android recent key长按事件弹起触发近期列表故障分析)进行阅读三个虚拟按键的代码。这里我们仅仅关心近期列表长按事件:



这里我们看到,长按receents键(也就是虚拟按键),代码逻辑为:

mRecents为空

不支持分屏

这里supportsMultiWindow方法为:

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

推断了一个系统属性config_supportsMultiWindow为真  以及非低内存版本号。则觉得系统能够支持分屏



isSplitScreenFeasible 推断当前分屏的大小。是否是满足系统要求的最小的分屏像素值。

当中mMinimalSizeResizableTask 值为



所以这里的代码含义为:

假设mRecents为空

不支持分屏

屏幕当前不够分屏的最小值

则直接返回。不进入分屏模式

否则。进入分屏。

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

01

我们来到分屏的代码位置。这里有一个推断

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

dockSide == WindowManager.DOCKED_INVALID 此状态表示当前没有处在分屏模式下,因此我们须要进入分屏

我们看下这里的WindowManagerProxy.getInstance().getDockSide()怎样处理的

这里能够看到,来到了WMS(WindowManagerServer.java)位置,看下getDockedStackSide方法



imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

这里怎样推断的呢?



找下当前的默认显示屏幕,然后推断下DockStack是否存在,假设存在。则在分屏模式。假设不存在。则当前不是分屏模式

我们这里在启动分屏,所以此时不在分屏模式模式,于是乎,我们来到代码:



千里之行,启程。

03

dockTopTask是由 mRecents调用的,那么 mRecents是谁呢?我们学习下。



这里我们要去找getComponent实现。然后我们记得之前讲过,SystemUIApplication里面有个services集合,系统会在启动systemui时候触发,创建每个的实例,

这里我们也能够看到有Recents.class,于是我们看下这个类(关注start方法,启动systemui会触发每个实例的start方法)。



仅仅看核心,其它忽略。

我们看到了有个putComponent动作。将自己增加进来,于是我们这里就能够通过getComponent拿到它了。

于是我们来到Recents.java。去看下dockTopTask方法。我们须要慢慢品尝:

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">



假设userSetup返回false,则不进入分屏,里面是获取两个值而已。不做深入扩展。

假设没有默认的屏幕大小initialbounds。我们获取一下。

紧跟着一个推断

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

这里为:是否有执行Task(一般都有)。不在homestack(就是不要在桌面下瞎按,它不进入分屏的),是否在pinningActivity,这个是什么鬼呢?就是我们可锁定仅仅在这个当前栈里面,你要跑出去,必须通过其它方式触发(这个模式开启了,肯定不同意分屏。由于我就是要限定你在这个TASK内)

经过这几个条件筛选,我们来到了真正代码位置

这里又有一个条件runningTask.isDockable,这个值是什么呢?我们须要看看:(脑子回路不再扩散,这里我们直接来到TaskRecord.java,看看)

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">



这里有非常多条件。来决定能否够同意分屏。我们关注下一个线索:

ActivityInfo.isResizeableMode(mResizeMode)。我们找下这个值从哪来。于是我们追到了:(PackageParser.java),有例如以下代码:



这段代码的含义为:我们在manifest.xml配置的分屏參数,resizeableActivity ,假设为true,意思为支持,然后假设还配置了supportsPictureInPicture,那么还支持画中画。

否则,我们推断当前apk的targetSdkVersion。假设大于N,你之前没有配置resizeableActivity。在N平台向上,觉得你就不想支持分屏,其它的再进行推断,设置为强制分屏模式。

(这条线没追。不敢贸然下结论。兴许再扩展)

为什么将这个。原因是我们开发app在manifest.xml会配置分屏參数,这里就是代码的地方。

resizeMode都有哪些值呢?

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

我们能够看到都有哪些模式。

04

继续dockTopTask方法:



我们假设进入sSystemServicesProxy.isSystemUser(currentUser) 为true,对于其它用户的。不去关注。就是我们开机进入的默认用户,user0

于是我们看到代码走到mImpl.dockTopTask,我们直接过来(mImpl==RecentsImpl.java)



我们看到了代码走入了moveTaskToDockedStack,这里继续跟进。我们看到了:



这里mIam就是ActivityManagerServer的代理端。此时。此方法moveTaskToDockedStack则会通过binder,进入到ActivityManagerServer的相应方法里面。

看我们上一节打出来的attachstack 方法的栈信息。是否完美的匹配上了。

小有成就。继续狂奔:

我们来看下ActivityManagerService.java里面moveTaskToDockedStack方法的凝视:

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

參数为:

须要移动到docked stack的task id

createMode 创建横屏的还是竖屏的分屏

toTop 是否将这个task 和stack移动到最上面

animate 是否须要一个动画

initialBounds 初始化docked stack的边界值

我们看下这里的实际传递的參数:

taskId 这个不用管,仅仅须要知道当前正在执行的TASK的id值就可以。

dragMode = NavigationBarGestureHelper.DRAG_MODE_NONE

stackCreateMode=ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT

initialBounds = 屏幕大小信息,这里为 0 0 720 1280

moveHomeStackFront = true 

animate=false

onTop = true

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

于是我们来看moveTaskToStackLocked (ActivityStackSupervisor.java)这个地方代码:







我们这里须要慢慢看,于是停下歇息会,转个圈。跳个舞先。

05

我们完整的看一遍代码.发现代码量还是巨大的。须要你有耐心,继续听我扯代码,一段段来



 final TaskRecord task = anyTaskForIdLocked(taskId); 找到taskid的相应数据,找不到返回

task.stack != null && task.stack.mStackId == stackId(參数stackId= DOCKED_STACK_ID) 推断是否已经是进入了分屏模式了,假设是,返回

stackId == FREEFORM_WORKSPACE_STACK_ID这里推断是否进入了自由模式。可是系统又没有支持这个模式,报异常出来。

task.getTopActivity() 拿到栈最上面的activity信息

获取下task的相应栈值

mightReplaceWindow变量的意思 可能须要替换window(此分支未作关注,我们环绕主线走)

mWindowManager.deferSurfaceLayout(); 停止surface更新,我们须要更新数据。随后使用continueSurfaceLayout继续。我们能够理解成一个锁,锁住画布。

我们继续跟踪



来到moveTaskToStackUncheckedLocked方法处

我们看凝视:



移动特定的任务到传入的stack id(我们传入的为DOCKED_STACK_ID。移动的是当前最上面的那个TASK)

task 须要移入的task

stackId 移入的stackid (DOCKED_STACK_ID)

toTop =  true 

,默认取得反值

forceFocus =false(不须要强制Focus)

reason 就是个凝视,我们无论

返回我们终于移入 的stack信息

06

来。互相伤害,我们贴出moveTaskToStackUncheckedLocked的完整代码:



imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

stackId必须是多窗体的栈,而且系统要支持多窗体,否则出错。我们当前满足此情况,不会出错。





final ActivityRecord r = task.topRunningActivityLocked(); 获取task上的顶部Activity信息

final ActivityStack prevStack = task.stack; 获取相应的栈信息

final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);

是否是focus状态

final boolean wasResumed = prevStack.mResumedActivity == r; 是否是resume的

wasFront 是否是当前最前的栈



这里我们处理下,假设当前是须要移动到DOCKED_STACK_ID栈,可是当前task却是不可Resize的,我们须要将栈移动到自己的栈,或者全屏栈上



我们跳过stackId == FREEFORM_WORKSPACE_STACK_ID 这个case。我们当前不关注自由模式的状态处理

下来我们进入核心位置:

final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop); 获取这个栈,假设须要创建,创建它

mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop); 移动task到相应的stack上面

stack.addTask(task, toTop, reason);

然后我们当前的stack,存储下task

当中我们看下getStack的方法:

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

核心

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

这里我们看到的ActivityDisplay 为获取相应displayId的一个实例,所以我们系统是支持多种显示设备的。

创建一个ActivityContainer(stackId),用来实现stack栈信息。然后存储下来。

我们看下

activityContainer.attachToDisplayLocked(activityDisplay, onTop);



这里便是将这个stack挂在相应显示屏的列表上面(一般我们默认显示屏是手机)

我们继续深入去看:

我们看到了attachDisplay方法



关键方法attachStack,我们跟入看下:(首先看凝视)



创建一个taskstack放置在相应的显示容器内

stackId ==栈Id,我们这里觉得为DOCKED_STACK_ID

displayId =我们觉得为默认屏,手机就可以

onTop = true

这里接住了我们上节所讲

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

,我们创建了分屏。于是系统通知systemui,显示divider线。

07

下来我们继续追attachStack这种方法



这里又出现一个方法,stack.attachDisplayContent(displayContent);,我们细致看下它

getStackDockedModeBounds方法为:

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

这里直接有值了,我们直接赋值返回了,于是我们说下这个值从哪赋值的mDockedStackCreateBounds

我们之前看到的



moveTaskToDockedStack –> 方法里面有个 mWindowManager.setDockedStackCreateState(createMode, initialBounds); 这里给了赋值。(须要看的能够向上又一次回头阅读下这个信息)

我们继续跟踪。退回attachDisplay方法。看到例如以下代码:



我们须要调整task的大小信息。

我们这里不再深入细化,由于这里逻辑太多,我们当前须要了解的是:

系统这个时候,又一次将全部的task大小计算,我们一般应用所在的FULL_SCREEN_STACK 会又一次调整。然后给当前app通知进入分屏。

为什么讲这个呢?由于这里是系统向activity发出的回调。告知系统进入分屏模式。须要activity作出响应的地方。

我们看栈信息:



系统在此时发送了REPORT_MULTI_WINDOW_MODE_CHANGED_MSG消息出去

我们在ActivityStackSupervisor.java里面找到它的处理方法:

然后它调用了app.thread.scheduleMultiWindowModeChanged 向相应app转送消息



app.thread里面:



关于app.thread 我们临时不做分析,原因是你就理解成AMS和APP的桥梁,这个app.thread会将事件带给我们的ActivityThread.java

这个ActivityThread.java熟悉吧我们的框架里面核心类,在系统创建新的进程(也就是第一次启动新的app)的时候,会进行fork新的进程。然后载入了ActivityThread.java,作为主线程。嗯,就这么多,就此打住。

ActivityThread.java继续内容为:



r.activity.dispatchMultiWindowModeChanged 这个就是调用我们的activity的回调去了

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

看。我们又找到一个方法onMultiWindowModeChanged,我们在写分屏app时候须要自己实现的一个方法。

又来总结下:

如此一来。我们就创建出来DOCKED_STACK_ID的一个栈了,当中stack是维护task任务的,task是维护activity的。它们就是如此的关系。然后我们系统将创建好的stack关联到WMS。调整task的大小。然后通知当前的activity,我们当前进入分屏模式下了。你要在你的onMultiWindowModeChanged 里面做出响应。

(看到了吗?我们分屏在activity的一个生命周期方法,在此处出现了)

08

回退回来,我们整理下:

moveTaskToStackUncheckedLocked 里面主要做了几件事情

final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop); 获取DOCK_STACK。假设没有,就创建它

task.mTemporarilyUnresizable = false;

mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop); 移动当前的task进入DOCK_STACK里面,更新状态,传递divider显示与否的消息到systemui。传递给activity onMultiWindowModeChanged,来告知消息。

stack.addTask(task, toTop, reason); 增加当前的AMS的管理里面就可以。

后面触发了mWindowPlacerLocked.performSurfacePlacement();方法。引发绘制动作,我们的分屏启动完毕了。

09

我们再来看一个问题,就是我们的分屏,会在屏幕上画出一个切割线,这个线的位置怎样定义出来的呢?

我们回到DividerWindowManager.java 。我们之前讲过。我们的切割线是在分屏开启后进行显示,增加到WMS里面去,我们能够看到一个关键信息



这里我们看到 TYPE_DOCK_DIVIDER。是这个View的类型,非常特殊。

我们搜索这个keyword。能够看到非常多内容,我们简单说下里面一些:

WindowManagerService.java 里 addWindow,



这里为假设当前View的类型为TYPE_DOCK_DIVIDER 我们要增加到DockedDividerController里面,依照上一节的说法,这个DockedDividerController会在系统的Vsync里面。实时触发。这里则会推断是否有divider之类的状态。

PhoneWindowManager.java 里面的 layoutWindowLw 方法:

给TYPE_DOCK_DIVIDER 赋值绘制区域,系统边界值的信息。

我们再看一个类WindowState.java,里面的关键方法computeFrameLw

有段内容:

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

我们打个断点在这里:

我们惊奇的发现。我们的栈里面有performSurfacePlacementLoop,还有moveTaskToStackLocked–>mWindowManager.continueSurfaceLayout(); 这里触发了启动绘制。

看到这条线路。我们能够找到整个窗体的计算过程路径。



这里我们关心的是这个切割线的位置:(这里mFrame便是计算好的位置信息了,我们当前值为 Rect(0, 568 - 720, 664)。高96,看这里是不是在屏幕中间)

看代码:



根据我们的DOCKED_STACK的位置,去计算frame,这里我们为TOP

而这里的96怎样来的呢?我们知道创建切割线的地方为 Divider.java的addDivider。里面有个信息: 

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

我们这里的dp2px=2。所以会是96高。

10

如上,我们发现我们穿过层层阻碍,走完了分屏的创建过程的大半过程。

分屏过程错复杂,我们还有个触发近期列表的过程须要解说。

我们看到了这里,经历了dock的整个创建过程,我们再回到我们出发的起点位置,看个内容:

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" title="">

RecentsImpl.java的dockTopTask方法。我们启动分屏的開始部分。

我们看到了。假设创建成功,我们进入里面的方法EventBus的send我们不去关注了,想要了解的,去看看EventBus的总线方式,以及怎样解耦的。

核心便是通过注解,系统将须要接收的通过方法的參数类型进行匹配。

我们须要看以下的:showRecents ,这个便是我们进入分屏,下方出现的近期列表界面启动的地方。

此方法我们不详细扩展了,本质就是启动了一个activity就可以(startRecentsActivity)。

假设有收获,观赏鼓舞下作者。

很多其它内容,关注微信公众号:代码GG之家。

加微信 code_gg_boy  进入代码GG交流群

 

下一讲,主要环绕分屏的退出过程

google 分屏 横屏模式 按home键界面错乱故障分析(二) 分屏的启动过程的更多相关文章

  1. 查看,设置,设备的 竖屏-横屏模式 screen.orientation

    <body> <div id="doc"></div> <div id="model"></div> ...

  2. js判断手机 横屏模式

    js判断手机 横屏模式 方法名称:orientation 实例: if(window.orientation!=0){ var obj=document.getElementById('orienta ...

  3. [Android] 关于系统工具栏和全屏沉浸模式

    随着应用程序的一些深入设计,大家总想要更好的界面和体验,所以有些东西并不能只是知道方法就结束了,是得要去深入研究研究的.通过这个过程我觉得,从应用层面来讲,想实现一个功能很简单,但若想实现的好,就要去 ...

  4. 分布式中的分库分表之后,ID 主键如何处理?

    面试题 分库分表之后,id 主键如何处理?(唯一性,排序等) 面试官心理分析 其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定 ...

  5. Linux关闭休眠和屏保模式

    本人因为特殊需求,想让某台Linux主机始终显示某个程序,显示器不能关机或者休眠或进入屏保模式. 环境:Ubuntu 11.10 最小化模式安装并安装有轻量级桌面openbox(非gnome).因为X ...

  6. Ubuntu下Vim 如何进入全屏编辑模式

    如题:F11进入全屏编辑模式,再按F11则退出全屏编辑模式.

  7. CentOS关闭休眠和屏保模式

    CentOS关闭休眠和屏保模式   本人因为特殊需求,想让某台Linux主机始终显示某个程序,显示器不能关机或者休眠或进入屏保模式. 环境:Ubuntu 11.10 最小化模式安装并安装有轻量级桌面o ...

  8. vim normal 模式下L键

    vim normal 模式下L键总是到一行的最后一个字符,而不是最后一个字符的下一个字符,这样进入插入模式,就还得往右移动一下,就很费劲? 怎么解决 更新: a键进入插入即可

  9. uwp - 禁用屏幕翻转/禁用屏幕旋转/禁用横屏模式

    原文:uwp - 禁用屏幕翻转/禁用屏幕旋转/禁用横屏模式 解决方案目录 > Package.appxmanifest 双击打开,把支持的旋转:纵向勾上,只勾这一个其他不勾,就可以了.同理,想让 ...

随机推荐

  1. Linux-批量添加用户stu01..stu03,并设置固定的密码123456 (要求不能使用循环for while)

    最终目标: useradd stu01;echo 123456|passwd --stdin stu01 useradd stu02;echo 123456|passwd --stdin stu02 ...

  2. Vue和vue-template-compiler版本之间的问题

    今天把远程仓库拉下项目,运行'npm run dev'时,报错 Module build failed: Error: Cannot find module 'vue-template-compile ...

  3. Git 服务器更换了IP的解决方法

    1.找到项目根目录中的.git文件夹 2..git文件夹里有一个config文件 3.用记事本打开后,修改为服务器的新ip就行了.

  4. ArcGIS api for javascript——地理处理任务-服务区域任务

    描述 本例展示了如何发送一些输入值到ArcGIS Server上的GIS模型并获得结果反馈给web应用.本例中,模型计算自在地图上单击点起始的驱动时间多边形. 例如,这个分析类型可用于想要知道多少人生 ...

  5. [Recompose] Stream a React Component from an Ajax Request with RxJS

    Loading data using RxJS is simple using Observable.ajax. This lesson shows you how to take the ajax ...

  6. Install the IIS 6.0 Management Compatibility Components in Windows 7 or in Windows Vista from Control Panel

    https://technet.microsoft.com/en-us/library/bb397374(v=exchg.80).aspx Install the IIS 6.0 Management ...

  7. Delegates, Events, and Anonymous Methods 委托、事件与匿名方法

    http://www.cnblogs.com/r01cn/archive/2012/11/30/2795977.html

  8. RPC和Socket

    RPC和Socket的区别 rpc是通过什么实现啊?socket! RPC(Remote Procedure Call,远程过程调用)是建立在Socket之上的,出于一种类比的愿望,在一台机器上运行的 ...

  9. vue --- cli build 后的项目,图片路径出错

    今天在插入背景图片过程中,遇到了路径错误的问题,通过网上查询,找到了解决的办法,但是大部分都没有讲造成这种问题的原因,故我简单地总结了一下,并加入了一些自己的理解,欢迎共同探讨~ 当用vue-cli自 ...

  10. POJ 1330 Nearest Common Ancestors 倍增算法的LCA

    POJ 1330 Nearest Common Ancestors 题意:最近公共祖先的裸题 思路:LCA和ST我们已经很熟悉了,但是这里的f[i][j]却有相似却又不同的含义.f[i][j]表示i节 ...