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

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

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

故障解析系列文章列表:

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

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

Android 关机对话框概率没有阴影故障分析

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

google 分屏 popup无法显示故障分析

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

分享此文,便是对代码GG的最大支持,欢迎分享。

前情回顾:

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

上一节我们主要围绕了分屏的启动过程,我们深入跟踪,追出整个创建的流程出来,同时我们也和分屏的divider的相结合,讲解了启动分屏流程。

在流程中,我们也分析了app在编写分屏的时候,需要处理的配置(manifest.xml配置 resizeableActivity )以及需要实现的方onMultiWindowModeChanged。我们还知道了系统如何给分屏的那个分界线设置位置,初始化的地方。掌握了这个启动过程,我们这节开始分析关闭过程

注意:我不是整体分析完,然后写架构之类的文章。我就是把我如何一步步跟踪代码,去带你一起学习,如何跟踪代码,确定代码逻辑,流程的。因此,我写的必须要实践,要不然会觉得不知所云,兴趣索然。

00

我们从上一章节知道,我们长按recent按键的触发流程为:PhoneStatusBar.java -->(onLongClick)mRecentsLongClickListener-->toggleSplitScreenMode

我们核心的就在这里。

我们这一章节从此处来开始展开。

我们知道,这里如果当前处在分屏模式下,dockSide == WindowManager.DOCKED_INVALID则为假,我们进入else的逻辑代码

EventBus.getDefault().send(new UndockingTaskEvent());这里就是我们需要关注的代码。

于是我们先讲下EventBus,EventBus是什么呢?目标是解决我们系统到处去写接口类,导致我们修改非常繁琐。那么,EventBus如何来解决此问题的呢。我们简单描述下它的原理。

在app启动的时候,EventBus初始化,然后我们在需要EventBus来解决消息传递的时候,进行注册,这个时候EventBus框架做了一件事情,将你注册的这个类存储下来,并且解析出来,你们可以接收我发送的哪些消息。

主要就是找方法里面有没有onBusEvent这个方法,它的参数则指明可以接收的类型,这个作为EventBus发送时候,决定是否发给你的原因。如此一来,我们在EventBus里面将自己注册进来,通过去写onBusEvent方法,通过它的参数(EventBus在注册的时候,会反射此类,拿到onBusEvent方法)给到EventBus我们接收的类型

然后我们在应用里面,使用EventBus.send(参数类型),EventBus则会在自己的注册表里面去找,谁可以接收此参数,然后发给它即可。如此,完成了简单使用的一个流程。

所以我们这里需要搜索谁接收UndockingTaskEvent这个类型。

我们可以看到DividerView.java

此处接收了这个参数类型,于是我们来这里看下具体代码。

01

我们看到代码

首先判断了是否处在分屏下,并且当前mDockedStackMinimized不能是true(这里true的意思就是已经最小化了,这里就不要重复去走)

我们看下startDragging方法:

先清除动画。重新计算initializeSnapAlgorithm位置,这个方法主要完成我们的退出到哪个位置。(里面包含了系统支持的1:1分屏 4:3分屏,16:9分屏等一系列数据,主要为滑动拖动分屏的分割线的时候,进行确定最近的一个位置点,然后我们就会分屏最终定格在此位置)关于这个分割线的讲解,会单独放在一节,暂时放过。

mWindowManagerProxy.setResizing(true); 设置当前开始resize task了。具体为:

这里先简单说下,就是通知所有Windowstate,我们现在要变化分屏大小了,这个时候需要在分割线变化的时候,实时的给计算下上下两个窗口(一个在

DOCKED_STACK 一个在FULLSCREEN_WORKSPACE_STACK这个上面)我们此处重新设置下这个值。

继续startDragging方法:

EventBus send一个消息StartedDragingEvent ,我们看下这个在干什么,根据我们讲的,这里我们搜索StartedDragingEvent,看下谁在处理。

我们看到ForcedResizableInfoActivityController 处理了,撤掉了一个消息即可。

然后我们在回到出发点:(接收退出分屏的地方)

继续分析startDargging:

计算我们的分屏情况,左右还是上下,这个来定位计算我们分屏需要的动画位置,方向。我们先放过这个,纯计算确定下一个位置。(简单说下,我们一般的上下分屏,我们退出的时候,是不是有分割线移动的方向,然后上下界面大小该如何变化)

拿到当前的view(分割线)的位置

我们核心要看的为:

这里传入当前位置,最终位置,动画时长,动画几秒后开始,结束后延时,动画效果. 嗯,我们就要去看这个地方了

02

所以我们的关注点在stopDragging方法里面:

动画的我们不去关注,我们直接看下flingTo方法

我们看这个getFlingAnimator,关键位置:

这里用了动画,动画进行实时的更新,从当前位置,到最后目标位置,启动动画,在实时检测此动画,做一个动作:resizeStack(就是一直实时变化,改变DIVIDER_STACK的TASK大小,同时修正全屏栈的大小,触发刷新,实时更新)

在动画完成后,做endAction动作(resizestack走完后,这里有关键的处理,在做退出分屏动作)。

于是我们需要跟踪resizeStack动作,去看完成了什么。我们只关心我们的退出过程。

可以看到核心为:计算下一个需要resizetask的位置,通过resizeDockedStack设置下去,同时通过setResizeDimLayer改变下阴影(这个可以不关心)我们看主线,发现就是围绕着resizeDockedStack来展看的,于是我们进入这边来瞧一下:

WindowManagerProxy.java 里面为:

计算出位置来,然后执行mResizeRunnable

看到没,又走到ActivityManagerServer 的resizeDockedStack方法了。

继续跟进,我们再来看:

ActivityStackSupervisor里面的 resizeDockedStackLocked方法:

我们需要围绕这个 resizeDockedStackLocked来扩展,我们仔细来看:

我们首先看这里的stack如何获取的

final ActivityStack stack = getStack(DOCKED_STACK_ID);于是它指的是分屏的那个DICKED_STACK栈。然后我们调用resizeStackUncheckedLocked,来对这个栈进行调整。

这里核心的思想为:首先判断参数是否有效,然后进行遍历stack的task列表,进行通知updateOverrideConfiguration,修改值,具体为:

updateOverrideConfiguration 里面主要完成的是更新

mOverrideConfig(calculateOverrideConfig)配置值,然后我们这里看到,如果task从全屏分屏变化的时候,会走一个函数:

scheduleReportMultiWindowModeChanged(又是一个多窗口开发的关键触发点,可以接收系统当前的变化信息。),最后调用的为

我们这里还需要关注的一个变量为:

这里的mFullscreen是个关键变量,决定了是否退出分屏的状态。

03

通知完updateOverrideConfiguration的时候,然后进行resize动作。

这个动作主要完成stack的Bounds数据更新,然后通过performSurfacePlacement让系统重新更新屏幕,触发绘制动作。返回的为是否为rawFullscreen,这个值决定着我们是否退出了分屏。

我们总结下resizeStackUncheckedLocked的方法:

更新之前被resize的task,调用updateOverrideConfiguration通知过去,引发task中的activity进行对应修改。如果退出分屏,会通知MultiWindowModeChanged到对应的task中所有activity。

更新每个task的bounds,config,然后使用mWindowManager.resizeStack将对应栈的bounds更改,触发一次绘制,引起界面更新。

完成这个resizeStackUncheckedLocked后,我们看到了有两个分支,我们当前忽略第一个,直接看else,原因是stack.mFullscreen为真的时候,意义为当前的stack已经进入全屏了,所以分屏模式就会退出。

我们先看else,这条线完成的是动画过程,一直修正当前的task的大小,引起系统绘制,完成分屏退出前的动画过程。

这里使用getStackDockedModeBounds计算出来dockedMode的边界,然后使用resizeStackLocked将其他栈进行调整,这里我们看下它的具体代码:

我们当前不是DOCKED_STACK_ID,于是乎我们往下继续看:

主要完成两个动作:

resizeStackUncheckedLocked(前面分析了,主要完成task的更新,然后调用resizestack执行调整栈大小,通知绘制)

于是我们看下面的代码:

ensureVisibleActivitiesConfigurationLocked,这里核心完成为:遍历stack上面的所有task,config是否变化,是否需要重启activity(ensureActivityConfigurationLocked)

如果需要updatedConfig的时候,我们调用resumeFocusedStackTopActivityLocked唤醒最上面的activity

这里我们不详细去看,因为变量太多,没法说全每个变量的变更状态。

我们这里需要描述的为:这里做了关键的几个事情:通知config变化(scheduleConfigurationChanged),如果需要重启,调用relaunchActivityLocked来实现,它完成了什么事情呢?

我们看下,(因为这个牵扯了activity的生命周期,所以我们看一下)

我们看到了这里有个scheduleRelaunchActivity,我们继续跟踪,看下代码

scheduleRelaunchActivity-->requestRelaunchActivity(ActivityThread.java),详细的自己关注下这里的handleRelaunchActivity方法即可。

完成了config的更新,我们可以看到代码会来到:

if(updatedConfig) resumeFocusedStackTopActivityLocked ,这里不仔细阅读了,因为又会走大量的ams ,wms的生命周期,导致我们分析无法继续下去,留个线,有兴趣的自己跟踪下。

04

我们来个总结:

我们在分屏的情况下,长按recent按键,引起退出分屏动作,有个动画,我们前面一直在看这个动画过程,主要实现方案,一直变更docked_stack的大小,然后变更其他栈的大小 ,通知acitvitys我们的task config有变化,需要重新reluncher的acitivty进入relauncher的状态

唤醒最上面的栈和acitivty(resumeFocusedStackTopActivityLocked ),于是,我们继续回来,看resizeDockedStackLocked函数。

05

resizeDockedStackLocked 经历了resizeStackUncheckedLocked --> resizeStackLocked --> ensureVisibleActivitiesConfigurationLocked,完成了这个动画过程的大量生命周期,紧挨着这里,我们再看个内容

这个在干什么呢?主要是防止我们resize过程出错,我们能够保证异常纠正过来,就是做这个事情的了。

这里代码流程为:10秒触发一个mTimeoutRunnable,将之前保留的bound信息,设置进去(resizeDockedStackLocked)。

这里我们要注意,这个是10s触发,但是有个关键地方hasTempBounds,如果这里为false,则会撤掉这个mTimeoutRunnable,返回。之后就不会触发mTimeoutRunnable的动作了。

整体来说,这段代码做这件事情:实时保存下当前分屏退出动画的bound值,然后我们10s后看下,如果这个mTimeoutRunnable还在,就表示系统当前在分屏动画过程出现问题了,于是我们想纠正这个错误,用之前保存的信息,再次触发resizeDockedStackLocked

分屏退出的动画流程则说完了,我们看下最终分屏栈是如何退掉的呢?

06

如上面所说,我们讲解resizeDockedStackLocked方法的时候,忽略了一个条件,具体为:

这里if我们之前跳过去了,因为我们想看下分屏动画过程,那么动画完了,是不是就会退出呢?我们回到触发分屏退出的原点,再次切入。

流程简单为:

DividerView.java 里面 :onBusEvent-->stopDragging-->flingTo-->getFlingAnimator

这里我们前面分析了resizeStack的过程,结束动画的时候,我们看到了endAction会触发,我们这里关注commitSnapFlags,看下它是做了什么。(endaciton其他的我们不去管,都是扫尾的变量而已)

这里我们看到当前分屏在退出的时候,是自身task的边界变大,直到屏幕大小,于是我们这里关注maximizeDockedStack这个方法。

于是,我们看到了核心为:resizeStack 这里参数是关健,我们继续去看:

参数情况: stackId = DOCKED_STACK_ID bounds = null allowResizeInDockedMode = true preserveWindows = true animate = false animationDuration = -1

于是我们逻辑走到mStackSupervisor.resizeStackLocked里面(前面分析的也是走到这里的),我们去看看:(我们关注下它里面的关键位置,我们这里stackId = DOCKED_STACK_ID)

然后resizeDockedStackLocked-->resizeStackUncheckedLocked 。我们又来到了resizeDockedStackLocked这里,此时我们要记住,这里的参数dockedBounds=null,这个是关健。我们关注下resizeStackUncheckedLocked里面的核心语句:

mWindowManager.resizeStack(stack.mStackId, bounds, mTmpConfigs,mTmpBounds, mTmpInsetBounds);

我们关注下bounds为null,看下这里为:(setbounds又调用了另一个setbounds方法)

这里我们看高亮的地方,mFullscreen为true了。因为我们传入的bounds=null

这里是我们需要关注的最核心的变化,我们回到resizeDockedStackLocked,去看这里退出来的条件:

根据我们之前的说法,此处mFullscreen为true,于是我们走入第一个状态,moveTasksToFullscreenStackLocked,此过程完成重要的使命,退出分屏。

07

经过层层分析,我们终于来到退出的地方,于是我们详细看下moveTasksToFullscreenStackLocked方法的代码:

我们需要关注的,都给你高亮了哦。resizeStackLocked完成的内容,之前详细讲过了,我们带过。这里需要将除了Docked_STACK栈,其余的都更新自己的TASK大小,STACK大小,然后通知activitys,通知绘制。然后我们走入moveTaskToStackLocked,完成最核心的代码:(当前我们moveTaskToStackLocked的目地为,将DOCKED_STACK上面的所有Task移动到全屏栈上),我们看个内容,按照我们之前第一节所讲,退出的时候,会走detachStackLocked方法,于是我们打断点,看下流程:

我们清晰的看到流程关系,根据这里的原因为,当你将一个task从一个栈移动到新的栈的时候,在旧的里面,则需要移除掉它。

我们直接看栈信息吧,这里判断,我们task移除之后,对应的stack为没有task了,于是我们需要将这个stack也一并移除掉,所以走到我们的detachStackLocked里面来了。

于是我们看下detachStackLocked的代码:

detachDisplay完成移除掉此stack里面的所有task对应的windows。然后我们进入notifyDockedStackExistsChanged,通知分屏退出了。

那我们就来看下这个notifyDockedStackExistsChanged方法:

这里主要做两件事情:

1:onDockedStackExistsChanged 通知退出,这个就会调用到我们systemui进程的Divider.java里面的DockDividerVisibilityListener类里面的onDockedStackExistsChanged方法,我们看下:

这里我们看下,都是用的post,为什么呢?原因为跨进程调用,过来是在binder线程,所以要更改view的变化,需要在主线程UI线程里面,于是我们使用了post将事件消息扔到主线程处理了。这里的动作先不去看了,关于ForcedResizableInfoActivityController,后续有时间,专门去扩展这个。

2:setMinimizedDockedStack,完成当前状态的变更通知,是否最小化,这里为false,于是是会最大化初始化一次变量。根据我们前面讲解,在系统将DOCKED_STACK上面的TASK 移动到FULL_STACK时候,我们DOCKED_STACK上面的TASK为空了,所以会移除掉DOCKED_STACK

08

我们延伸个地方,DockedStackDividerController.java类里面的一个关键方法checkMinimizeChanged ,触发调用它的地方为:notifyAppVisibilityChanged 和notifyAppTransitionStarting ,也就是显示与否和动画与否的时候,调用。 我们看下它的代码:

getDockedStackVisibleForUserLocked 具体指什么呢?

这里判断是否存在DOCKED_STACK_ID并且是显示状态,如果返回null,则说明当前没有分屏,因此我们直接返回去了。代码核心的意义,注释如下:

这里是个关键地方,起着操作docked_stack的状态变更修正,是个核心代码方法,我们在学习分屏流程时,可以在此处设置断点,多次来回跟踪流程使用。

09

我们前面说过,系统在处理完后,会触发绘制,这里具体为:performLayoutLockedInner(WindowSurfacePlacer.java)我们关心下这里的代码,主要是最后一句:

这里我们会send一个msg出来,我们看下这个消息,用来更新DOCKED_STACK_DIVIDER.处理的地方在WindowManagerService.java

里面。

这里adjustForImeIfNeeded不做分析,主要完成输入Ime的调整。我们要关注上面的方法:reevaluateVisibility

这个在DockedStackDividerController.java文件里面,参数false。我们关心这里的notifyDockedDividerVisibilityChanged,完成分割线的显示隐藏

于是我们又要去systemui那边看响应了。

更新显示隐藏状态

如上,分屏的退出过程讲完。

原文发布于微信公众号 - 代码GG之家(code_gg_home)

原文发表时间:2017-03-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏代码GG之家

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

google 进入分屏后在横屏模式按home键界面错乱(二) 你确定你了解分屏的整个流程? ? Android 关机对话框概率没有阴影故障分析 android ...

285100
来自专栏GIS讲堂

wms常用操作

自从换了工作就很少有时间上网了,新的单位不让上网的,所以博客也有好久没有更新了,虽然博文的质量一般般吧,但是觉得还是坚持写下去比较好,今天,北京,雨天,写点最近...

22440
来自专栏数据魔术师

词云图:论一个精致猪猪男孩的数据修养

“词云”就是对网络文本中出现频率较高的“关键词”予以视觉上的突出,形成“关键词云层”或“关键词渲染”,从而过滤掉大量的文本信息,使浏览网页者只要一眼扫过...

17740
来自专栏CodingToDie

百篇(十):批量生成微信祝福卡片并发送好友

百篇(十):批量生成微信祝福卡片并发送好友 最近一直被支付宝账单刷屏,我也有一张 想了想微信还没有出类似卡片,也快过年了,能不能自己制作图片卡片,发给好友呢,这...

36750
来自专栏FreeBuf

一种绕过限制下载论文的思路

注:本文下面的内容仅讨论绕过思路,作为技术交流之用。大家下载论文还是应该通过正规渠道,付费下载,尊重各位站长的劳动成果。敏感图片和代码中涉及站点的内容均已打码。

15920
来自专栏韩东吉的Unity杂货铺

零基础入门 43:InputField虚拟键盘激活状态如何检查?

Hello,大家好,我是Jimin 韩东吉,因为公司的项目在前一段时间处于紧急开发阶段和紧接着的一测二测,导致又是时隔很久不更,现在节奏稍微慢了下来,还是抽空回...

25430
来自专栏地方网络工作室的专栏

MAC版画图软件 paintbrush 推荐,类似 windows 上系统自带的画图软件

不想开photoshop这么重的软件,但是对于屏幕截图有需要有一点处理。这时候我想起 windows上画图的好了。 搜索了一下,知道了 paintbrush 这...

36080
来自专栏瓜大三哥

在Vivado中实现ECO功能

应用场景:如何利用Tcl 在已完成布局布线的设计上对网表或是布局布线进行局部修改,从而在最短时间内,以最小的代价完成个别的设计改动需求。 什么是ECO? ECO...

51770
来自专栏维C果糖

GitHub 主页介绍及修改个人信息

接着「敲开 GitHub 的大门 - 注册账号」一文的内容,我们继续往下介绍: ? 标注 1:View profile and more,更多选项视图; 标注 ...

24770
来自专栏DHUtoBUAA

基于Python实现matplotlib中动态更新图片(交互式绘图)

  最近在研究动态障碍物避障算法,在Python语言进行算法仿真时需要实时显示障碍物和运动物的当前位置和轨迹,利用Anaconda的Python打包集合,在Sp...

97260

扫码关注云+社区

领取腾讯云代金券