如何解决新问题

杰夫 · May 30, 2014

——记窗口Z轴位置切换问题的研究与解决过程

缘起

前几天由于工作的需要,解决了一个Android平台下与窗口Z轴位置切换有关的问题,老大让我写个总结。并且最近也碰到有同学问我是如何解决“棘手”问题的。索性我就把解决这个问题整个过程还原一下,总结出我平时解决问题时所用的一些“小技俩”以及一些心得,希望能对看到这篇文章的同学有所帮助。

任务描述

在支持层叠式(stacking)多窗口的操作系统(比如微软的Windows)中,同一时间可以打开并看到多个应用的窗口,你所看到的每个窗口,在某一时刻距离用户是有远近的。距离用户最近的窗口只有一个,就是用户当前操作的窗口,也就是获得焦点的窗口。其他窗口依次排列在当前获得焦点的窗口后面。这类多窗口操作系统中,窗口在从无限远处到用户这个所谓的Z轴上的次序就是z-order。

z-order

目前Google官方的Android版本是不支持多窗口的。也就是目前Android手机中常看到的情况:用户在一个时间只能在屏幕上看到一个应用。你打开了电子书阅读器,之前使用的浏览器界面就会消失。不过,即使是这样,依然存在管理z-order的问题。因为,电子书阅读器应用也可能弹出对话框,这个弹出的对话框距离用户是最近的,而之前打开的界面显示在对话框的后面,并且通常会变暗或者模糊。

App with dialog

我们现在做的多窗口开发任务就是要让Android系统支持类似微软Windows操作系统那样的层叠式多窗口特性。这样就会出现两个以上应用的窗口同时出现的情况。因此,用户在多个窗口之间切换时,就应该出现:

  1. 用户触摸一个不在最前面的应用窗口时,这个窗口要自然地(像我们平常使用的Windows那样)切换到最前面,并且从暗变亮。
  2. 触摸前位于最前面的应用窗口要自然地退后一层,并且从亮变暗。

前面说明了这么多文字,现在可以用一句话来描述分配给我的任务了:让我们平板上的小窗口应用也提供上面所描述的那种(类似Windows)在多个应用窗口间切换焦点时的动作与效果。

stacking

上面就是我在接到任务时获得的信息以及相关知识背景,对于这类问题,我当时也是没有任何经验。

启动信念 —— 解决问题的第一步

面对从没处理过的问题,我们开始都会感到无从下手。无从下手接着就会引起诸多负面的情绪,如失落、焦虑甚至自卑。不过即使如此,你依然要启动起一个信念,一个要解决问题的信念。因为这个信念将是你解决问题所要迈出的第一步。

上面这段文字所说的信念似乎很虚,让人无法衡量其对解决问题的帮助有多大。但根据我自己解决问题的经历,以及与其他善于解决问题的朋友的交流,我发现,启动这个信念是必须的,信念越强,你能够解决问题的可能性就越大。

具体说来,启动信念就是要:

  • 第一、要坚信自己要解决,也能解决所面对的问题;
  • 第二、要一直记念着要解决的问题,任何时候,直到问题被解决!

信念的力量

任务分析 —— 找到问题的根本原因

有了信念之后,首先要进一步明确你面对的任务或者问题的本质是什么?

root cause

针对上面所提到的小窗口应用焦点切换时界面显示的变化问题,我是这样做的:

  • 反复观察Widnows系统下相应场景的表现细节
  • 基于上面提到的z-order概念的知识背景,通过阅读相关书籍、在网络上搜索并阅读相关文档,大量地补充可能会有用的相关知识。
  • 与可能了解该问题的同事交流,虽然不一定会从同事那里获得准确的信息或者解决方案,但足够的交流可以让你的思路更加发散,也能缓解一定的负面情绪。

通过上面的努力,我对任务有了更深刻的认识:

事实上,Google官方的Android版本,在一个时间,多个应用的窗口依然存在于同一根z轴,只不过每个应用的大小都充满整个屏幕,因此用户在这个时间只能看见一个应用,其他应用的窗口被当前应用的窗口完全遮挡住了而已。新打开的应用窗口永远距离用户最近,位于z轴最前端。关闭当前应用后,上一个应用的窗口就位于z轴最前端,出现在用户眼前。

我们设计的多窗口实现方案也是充分利用了上面的特点,即新建一个不充满整个屏幕的小窗口,然后把原来应用窗口中的View放进前面新建的小窗口中。这样一来,多个应用就可以同时显示到屏幕里,达到了预期的目的。

Google官方的Android版本中,目前还没有同时显示多个应用窗口的场景,因此我们的小窗口不会随着焦点的获得自动移动z轴的位置,而只有最新创建的窗口会位于z轴的最前端。

有了上面的认知后,我的任务可以进一步简化为:焦点在多个窗口间移动时,将获得焦点的窗口移动到z轴最前端。

问题解决方案 —— 持续修炼

根据自己解决问题的经验,采用倒推的方法,我总结出:

  • 寻找问题X所对应解决方案S,存在一个必要的知识集合K{k1, k2, ..., kn}
  • 寻找问题X所对应解决方案S,需要动用一定的思维方法。
  • 知识集合K{k1, k2, ..., kn}和需要用到的学习、推理与分析能力分散在寻找问题根本原因与解决方案这两个过程中。

因此,用一句话提炼解决问题之道,就是:通过学习、推理与分析能力,找出并掌握知识集合K{k1, k2, ..., kn},进而找出解决方案。

结合上面提及的在z轴上移动窗口的问题,我自己使用Windows操作系统的经验,使用Android系统的经验,对Android应用开发方面的专业知识,以及通过搜索相关资料、阅读相关源代码所获得的信息等都是上面知识集合K中的k1, k2, ..., kn

知识集合是必须的,但必须同时动用一定的思维方法。

我拿到问题需求后,先是反复的体验参考机上别人实现好的效果,让自己有一个深刻的感性认识。然后,根据已有的知识与经验判断出问题与z-order这个关键字有联系,并初步推测问题是现有Android没有提供小窗口情况下z-order移动的功能,需要我们补充这个特性,接着对z-order进行搜索以及学习相关其他内容后了解到问题属于图形界面操作系统中的窗口管理系统的范畴,后面当然要继续补充窗口管理器,特别是Android系统的窗口管理器(WindowManagerService, WMS)相关的知识点。当然,就解决问题而言,完全没有必要将WMS中的所有细节都搞清楚,原则就是将与问题相关的内容弄明白。

深入WindowManagerService之后,我就确认了问题的本质:现有的Android系统没有对我们新增加的小窗口提供移动到最顶端的用户事件响应及相应z-order移动处理的代码。

如何为小窗口增加这个特性?对于没有相关经验的新手,我的做法就是模仿。之前了解到Activity虽然没有多个应用窗口间的z-order移动问题,但存在应用内的窗口z-order移动问题。因此我的初步方案就是借鉴Activity内用内窗口z-order移动问题。

然后顺着这个思路,我研究了WMS在Activity内移动z-order的流程,以及WMS可用的接口函数,最后发现其中moveAppTokensToTop()这个函数与自己所期望的功能相符。

经过编写相关接口函数、验证与测试,证明该函数与当初猜测的功能相符。因此这个方案可以工作,达到了预期的目的,当然,也许还存在更好的解决方案,但这就是持续修炼的问题了。因为解决方案的优劣也是相对的,取决于当下客观环境的不同以及认识的程度。

总结

最后,总结一下我对于解决问题的实践之道:

  • 坚定解决问题的决心与欲望
  • 广泛涉猎各种知识(知道是什么,能做什么?)
  • 深入学习所需知识(知道怎么用,为什么?)
  • 通过实践培养正确有效的思维方法

原发表于2013年6月14日

Twitter, Facebook