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. 移动端(手机端)页面自适应解决方案—rem布局篇

    移动端(手机端)页面自适应解决方案-rem布局 假设设计妹妹给我们的设计稿尺寸为750 * 1340.结合网易.淘宝移动端首页html元素上的动态font-size属性.设计稿尺寸.前端与设计之间协作 ...

  2. 以替换为主的疯狂填词、sub()介绍

    去年接到一个任务,一直给拖到了今天,再这么下去可不行,今天我就要让你们看看我的厉害 任务是这样的:创建一个程序,读入文本文件,并让用户在该文本出现ADJECTIVE .NOUN.ADVERB或VERB ...

  3. 【Codeforces Round #420 (Div. 2) C】Okabe and Boxes

    [题目链接]:http://codeforces.com/contest/821/problem/C [题意] 给你2*n个操作; 包括把1..n中的某一个数压入栈顶,以及把栈顶元素弹出; 保证压入和 ...

  4. 【cdoj 1544】当咸鱼也要按照基本法

    [题目链接]:http://acm.uestc.edu.cn/#/problem/show/1544 [题意] [题解] 容斥原理题; 1..(2^m)-1枚举 设k为其二进制形式中1的个数; k为奇 ...

  5. Linux学习总结(9)——Linux 新手必知必会的 10 条 Linux 基本命令

    Linux 对我们的生活产生了巨大的冲击.至少你的安卓手机使用的就是 Linux 核心.尽管如此,在第一次开始使用 Linux 时你还是会感到难以下手.因为在 Linux 中,通常需要使用终端命令来取 ...

  6. Rapidjson的简单使用示例

    很早就想用用Markdown了,一直没机会.今天就来试一下 先放个目录: Rapidjson的简单使用示例 rapidjson官方教程 本示例所用环境 示例代码与注释 rapidjson官方教程 如果 ...

  7. Aizu - 2564 Tree Reconstruction 并查集

    Aizu - 2564 Tree Reconstruction 题意:一个有向图,要使得能确定每一条边的权值,要求是每个点的入权和出权相等,问你最少需要确定多少条边 思路:这题好像有一个定理之类的,对 ...

  8. HUE配置文件hue.ini 的filebrowser模块详解(图文详解)(分HA集群和非HA集群)

    不多说,直接上干货! 我的集群机器情况是 bigdatamaster(192.168.80.10).bigdataslave1(192.168.80.11)和bigdataslave2(192.168 ...

  9. Docker -- 系统整洁之道 -- 0

    在我的小 rmbp 256G的硬盘里,实在是装不下100多个G的虚拟机了,所以想把一些东西迁移到这两年很火的Docker下,Docker以前也有过一两次,只是按着别人给的用法用的,具体的一些细节并没有 ...

  10. java实习生的成长之路<转>

    首先初识语法的阶段,必须要学会怎么操作对象,操作if和for,操作list set map,然后是线程.IO和jdbc什么的,其余的,若是一时不理解,可以后边需要时再学. 这阶段完了,你可以写些能在控 ...