从Google Camera 的经验谈Software Engineering 之中Design 的重要性

从Google Camera 的经验谈Software Engineering 之中Design 的重要性 

[复制链接]
   

电梯直达
楼主
  发表于2014-6-21 00:20:57  只看该作者  只看大图 
 

2013年的秋天起,我担任Google Camera Android App 的架构设计负责人(Lead Architect)。Google Camera Android app经过几次的release之后终于在Apr. 16 2014于Google Play Store上开放下载。所有搭载Android KitKat (Android 4.4)的device都能够经由Google Play Store免费取得。这是一个很重要的里程埤,因为这代表着Google Camera不再只是为了Nexus品牌的device而设计,并且所有的最佳化都要考虑市面上各式各样不同的机型。

对我个人来说,这也是人生中一次极具代表性的挑战:我要替Google为这个极受瞩目的产品设计其程式架构。从开始设计并将其写成文件费时约三周,再和整个团队花了近半年的时间把整个设计完成。其中还需要协助其他人完成各元件的细部设计(但因为时间不足,我最后还是让一些我认为很糟的设计进了release)。直到此篇文章完成时,仍有一部份我心中的设计只停留在文件上(或我的脑子里)。毕竟一个团队的人力有限,我们总要做出取舍:一个产品究竟是程式码的品质重要,还是赶紧把feature带给使用者重要呢?这其中的平衡点是非常微妙的。 从Google Camera Android App的历史来看,过去的每次release都是和Android platform一起开发并只搭载在Google的自有品牌Nexus机种上,并没有在Google Play Store上架,因此以往的开发重点只限于在Nexus机种上的最佳化。这样的需求让整个程式相对简单。在确定要在Google Play Store上架之后,我就开始设计整个程式的新架构。透过整体架构设计来达到这三个目标 :稳定性(Stability),拓展性(Scalability)和效能(Efficiency)。经历了将近半年的时间,一直到四月我们release了第一版之后,改写的工作仍然在进行。 在Google Camera Android app的程式之中,我也是最复杂的两个部份: Filmstrip和CameraManager的负责人。Filmstrip是我们在app中提供的一个小型相片和影片浏览器。CameraManager则是整个程式之中负责控制camera device的部份。App的整体效能和这两个部份紧密相连,因此大部份的design也着重在这两部份上。 在我们谈design之前,我们先来聊聊Google Camera的第一版Google Play Store release上遇到了什么样的问题,再来谈design在这些问题之中的角色。开发重点以及问题重点1:流畅度最佳化所有的App在开发时都会考虑一个重点:流畅度。这个流畅度除了程式本身执行的顺畅以外,还有使用者操作的顺畅。任何和使用者有互动的程式都要在意程式对使用者操作的response time。在Android的framework之下,任何的UI event (使用者触控等)以及UI元件在画面上的更新都发生在UI thread,我们又称main thread。一旦UI thread上发生严重的delay就会导致app的response time变长,或者画面凝结,更新频率(framerate)也降低。我们要尽量把UI thread上不必要的运算降到最低。 Google Camera Andorid app面临到的问题更多,因为所有的camera device操作都有额外的等待时间,这个等待时间是晶片本身运作所需要的时间,就像磁碟的读写一样,是被硬体所限制而无法 ​​缩短的。然后这个等待时间会严重影响到程式运行的流畅度。例如使用者执行了Google Camera app,程式一开始必须将相机启动并且设定所有的启始参数,之后让相机把目前的画面显示在UI元件之中。之后才是使用者看到的,萤幕上有一个有live画面,并且可以操控的元件会依据现在相机的设定显示(例如目前的闪光是"关闭",目前的模式是"录影")。整个流程大致如下:












  • 使用者执行Google Camera app
  • 程式启动,把所有的UI 元件启始化
  • 计算出目前的相机参数
  • 依据目前的相机参数更新UI 元件(例如:将闪灯图示显示为"关闭")
  • 将参数经Android Camera API 传给底层
  • 开始捕捉live 画面并回传至画面

整个的时间有可能会需要1.3秒甚至更多。想像一个Camera app需要1.3秒的时间开启,1.3秒之后才会对使用者的操作有反应并可以开始拍照,这样的app绝对不是使用者想要的。从身为一个Google工程师的角度对产品的要求,我认为这是无法release的。相反的,我的期待是能够带领整个团队做出一个全世界最稳定并且反应最迅速的Camera app (当然理想不见得总是能够达到,但至少是一个努力的目标)。 Android app的另一个特性是天生的Event-driven以及multi-thread执行环境。同时,Android Camera API 1之中有许多的错误设计,也让最佳化有额外的难度。E vent-driven的执行方式需要很灵活的架构,并且要永远假设不会发生的错误是会发生的。再加上multi-thread的环境,各thread之间的同步和各操作的相依性也必须在设计初期就考量进去。我的中心思想就是要避免使用java的synchronization使各thread能够不必为了同步而暂停。最后的结果就是在整个超过万行的原始码中,只有屈指可数的synchronization lock。重点2:相容性在Android上设计一个camera app是很不容易的。其中一个最主要的问题便是Android Camera Framework API的design并不健全。Android Framework是一个很大并且复杂的系统,其中许多部份彼此都由不同的团队负责,因此不是所有的design pattern都是一致的。再加上第一代Android Camera API在设计当下并没有考量到未来许多可能的需求,许多API的行为并没有定义完全。各家手机制造厂因此在实作时有许多差异,在Samsung手机上能跑的code不见得在HTC的手机上能动。 我们遇到过一个最严重的情况是camera在拍照的动作进行时,如果更动相机参数,在某些机型上会有严重的当机。举例来说,如果使用者按下快门钮之后再迅速切换闪光灯模式,在某些厂牌的手机上会导致程式终止。有时甚至需要重开机才能够再次使用相机。当然我们可以直接限制在拍照的过程中不容许使用者做任何参数调整,不过这牵扯到更多的细节,而且不是一个一劳永逸的解法。重点3:跨多组合作 Google Camera里有许多功能必须靠着Google内部多个团队的合作来完成。举例来说,Lens Blur (镜头模糊)模式 ​​就是一个Google Camera团队和Google Research合作的产品(Lens Blur的技术介绍请参考Google Research的blog )。然而为了让使用者经验达到最佳,不同的功能及模式之间必须要有相似的操作介面。例如快门钮的位置固定,参数调整的元件位置也要一致。要让程式有效率,就要想办法做到UI元件的重复使用。 除了 ​​程式效率以外,要让各组之间合作的效率提高也是一件很重要的事。有效率的合作并且维持程式的稳定度是一个大挑战。如果我们能够从架构上就考虑到这件事,所有的组都能够得益。要让各组的效率最大化,就要想办法让彼此之间的相依度降低,每个组能够单独开发。主要架构: MVP (Model-View-Presenter) Pattern 设计一个具有UI和使用者互动的程式时,MVC (Model-View-Controller)和MVP被广泛的使用。MVC的pattern把程式大致上划分为三块; Model (M), View (V), Controller (C)。其中Model是把资料抽像化之后的表示。View则是如何呈现data的逻辑。Controller是程式的控制逻辑。所有的event都交由controller来负责决定该如何respond。 MVP (Model-View-Presenter)在MVC的架构上更进一步把View的逻辑整理起来集中放在Presenter ,好处是Controller不晓得具体View的implementation所以把controller和view之间的关系更进一步抽象化为controller和presenter之间的关系。 有关MVC和MVP这两个极为重要的design pattern的细节,我不在本文之中讨论。很多blog以及网站都有相关的教学。以后或许有机会我会以专文探讨,这里就先假设大家都有MVC/MVP的背景知识。接下来我继续介绍如何运用MVC/MVP在Google Camera之中以及其带来的优点。MVC/MVP的好处把程式大致上分为M, V, C/P三个部份之后,首先是很清楚的了解到哪些事由model做,哪些事由presenter做以及View该处理什么。一旦分工清楚,也就清楚哪些事该发生在UI的thread上,哪些事该丢去背景处理并且等处理完之后再回UI thread去。要让程式更流畅的最佳化也就很清楚该怎么进行。 第二个好处是跨组合作时,负责UI的人和负责底层资料运算的人可以分头进行,并且达到极高的code reusability。我们在和Google Research合作时就因为MVC/MVP的架构使得许多工作事项彼此不互相干扰,让大家都更有效率。MVP的运用一个典型的MVC/MVP架构被运用在Google Camera的Filmstrip之中。Filmstrip是我们提供的一个小型多媒体浏览介面,任何使用者用Google Camera拍摄的照片或者录制的影片都会出现在这个Filmstrip介面上。它是非常典型的MVP架构: ImageData以及DataAdapter是所有Filmstrip之中的data的抽象化表示,FilmstripController则是Presenter,view的部份则由FilmstripView处理。在Filmstrip的设计上使用MVP是很直觉的做法。一旦你开始设计一个image/video viewer你会发现在不知不觉之中就导入了MVC/MVP。 除了​​Filmstrip之外,整个app也在各不同的level使用MVC。我们把各个不同的拍摄模式以module来表示,并把activity level能够提供的资源分为controller以及view ,再提供给module使用。每个module也建立自己的MVC架构,module controller从activity level的controller取得所需的资源, module view则和activity view结合。如此一来各组只需专注在自己的module之中,负责activity开发的人也只要关心activity level的细节。Design With Interface Java提供interface的概念。C++或多或少算是有,只是并不那么直接称作interface。Interface的大意是我只把这个物件上所有能够进行的运作都定义好,但不在乎如何做到。也就是说定义了一组的methods,其他都不管。而所有这个interface型态的物件才需要去定义实作的细节。 举例来说,我可以定义一个interface "Photo"来表示所有photo的一些共通操作,其中包含rotateLeft()及rotateRight()。假设我有两种物件型态支援Photo这个interface。第一种是bitmap格式的photo: BitmapPhoto,另一种是有EXIF header的JPEG格式的photo: JpegExifPhoto。那么BitmapPhoto.rotateLeft()和BitmapPhoto.rotateRight()都是靠着像素的转换完成的。JpegExifPhoto.rotateLeft()和JpegExifPhoto.rotateRight()则可以靠着更改EXIF的orientation资讯达成。 这两种物件型态虽然底层实作不同,但在程式使用时我们只在乎它是否为Photo一种实作。只要是Photo就能够做到rotateLeft()和rotateRight()。 在Google Camera之中,几乎我所有的design都是基于interface。Interface的好处在于底层的实作容易更换,所以灵活度高。Design with interface再和MVP结合,把三块都定义成interface,如果有测试的需求,只需将底层实作换成测试用的元件就能够轻易测试各部份的正确性。此外interface和decoration pattern还能够无缝接轨,code reusability更上层楼。 Design with interface还有一个好处是flexibility。一旦有新的功能需要加入时,必要的code更动的程度往往能到最小。Google Camera的design之中,最复杂的camera操作元件"CameraManager"就完全以interface作为design的单位。Google Camera后来的几个版本,为了让相容性提高,在CameraManager的实作上有很大的变动。但因为是design with interface,所以其他相关的部份几乎都没有变动,只抽换了底层的实作。结语 写到这里,我只提到了我的设计之中的两个核心: MVP和Design With Interface。这两件事也是我们解决最佳化,相容性以及跨组合作效率的最重要的方法。真要说完全部的design还能够再写成好几篇文章来和大家分享。我还会继续深入介绍有关design的许多细节和考量,以及几种常见的design pattern在Google Camera之中如何被使用。 在我负责整个App的架构时,我深深的体会到一件事: Design的重点是以结构性的方式解决问题。一个正确的design能够让整个团队快速往前进同时兼顾正确性。身为一个architect的任务则是远大于单纯的替整个团队找出合用的design ,更要进一步地说服整个团队,让每个人都了解你的长远目标。最后,让所有人都看到你心中的蓝图。如此,你的团队才能更快速地往同一目标推进。 而这一切,都从design开始。









































你可能感兴趣的:(设计模式学习)