Android界面从里至外浅析

从刚刚开始接触Android开始,就一直在和界面打交道。越往后学就越感觉,从没有理解过它的原理。大多数情况下,这对开发并没有什么影响,但是有一些特殊的需求影响到性能、复杂实现的时候,去研究一下它背后的原理就非常重要了。

在Android上面,界面基本上是以Activity作为单位呈现的,我们习惯上会认为Activity就是一个容器,它是用来盛装Button,ListView等等组件的。OK,这样子理解并没有什么不妥,因为我们总是会写一个XML文件,这个文件有一个根,一般是各种Layout,然后通过setContentView方法在Activity的OnCreate方法中对Activity界面进行设置,最后展示出来的界面正是我们想看到的,所以,一切工作正常。那么,现在有下面这个问题:

1)Dialog是如何实现的呢?为什么它可以默认浮在所有的界面元素之上?(你实际上可以很轻易的使用普通的布局实现Dialog的样子,完全没有问题,那么系统是不是这么实现的呢?)

2)一个APP有很多的界面(Activity),那么你按下返回键或者Menu键,系统怎么就知道把这个事件传递给哪个界面呢?(因为这个界面就浮在所有界面之上么?)

3)很想实现悬浮窗口么?接触过WindowManager概念?(悬浮窗口浮在所有的View之上)

4)用过SufrfaceView么?使用它绘制过透明层吧?这个坑爹的组件是如何实现双缓冲的?又是为什么坑爹必须浮在界面所有的组件之上的?

上面的这些问题,尤其是最后一个的解答,都涉及到整个界面元素的绘制过程以及管理方式。我直接给出结论,后面再做分析:

a)每一个APP都有一个WindowManager维护所有的界面展现。Activity只是一个逻辑概念,并非容器,真正的容器是PhoneWindow,每一个Activity都有一个PhoneWindow。而PhoneWindow则引用着整个界面的最根本元素,称为DecorView,DecorView引用这最最根本的Layout(注:这个Layout还不是你写的那个XML文件的根元素),这个Layout是一个LinarLayout,包含两个部分:两个FrameLayout,一个是标题栏(你可以定制),一个则是用来容纳你写的XML文件(你写的在这里呢!)。

b)每次创建一个Activity,WindowManager都会产生一个对该Activity的DecorView的引用,并且产生一个Handler的子类ViewRoot在WindowManagerService(系统界面服务)注册,沟通当前的Activity和系统服务,用来接受系统事件(比如按下返回键);

a)b)两点结合大致如下图:

c)悬浮窗和SurfaceView的问题则涉及到Window, Surface,View,Canvas的概念和区别,后面详述。

下面详细解释一些概念,援引StackOverFlow上面一个问题(Understanding Canvas and Surface concepts)的答案:

1)A Surface is an object holding pixels that are being composited to the screen. Every window you see on the screen (a dialog, your full-screen activity, the status bar) has its own surface that it draws in to, and Surface Flinger renders these to the final display in their correct Z-order. A surface typically has more than one buffer (usually two) to do double-buffered rendering: the application can be drawing its next UI state while the surface flinger is compositing the screen using the last buffer, without needing to wait for the application to finish drawing.

什么是Surface?就是我们最终看到的界面,每一个Surface都有多个缓冲,很多情况下基本是双缓冲:应用程序绘制下一个将要展现的UI(放置在一个Buffer中),而同时SurfaceFlinger则通过另外一个缓冲展示界面(有点像流水线),这样就可以不用等待应用程序绘制完再去显示了。

2)A window is basically like you think of a window on the desktop. It has a single Surface in which the contents of the window is rendered. An application interacts with the Window Manager to create windows; the Window Manager creates a Surface for each window and gives it to the application for drawing. The application can draw whatever it wants in the Surface; to the Window Manager it is just an opaque rectangle.

什么是窗口?你可以把它想象成为桌面上的窗口(Windows),每一个窗口都一个Surface用来绘制窗口的内容,应用程序通过窗口管理器创建窗口。

3)A View is an interactive UI element inside of a window. A window has a single view hierarchy attached to it, which provides all of the behavior of the window. Whenever the window needs to be redrawn (such as because a view has invalidated itself), this is done into the window's Surface. The Surface is locked, which returns a Canvas that can be used to draw into it. A draw traversal is done down the hierarchy, handing the Canvas down for each view to draw its part of the UI. Once done, the Surface is unlocked and posted so that the just drawn buffer is swapped to the foreground to then be composited to the screen by Surface Flinger.

那什么又是View呢?View是我们所有交互元素(所写的布局XML文件中所有元素的父类)。每一个窗口都拥有一个视图继承树,每一次窗口需要被重绘,窗口的Surface就会被锁定,然后返回一个画布对象,重绘操作就会被从视图继承树上传递下去,每一个View都会绘制画布上属于它的部分。一旦绘制完成,Surface就会解锁。

4)A SurfaceView is a special implementation of View that also creates its own dedicated Surface for the application to directly draw into (outside of the normal view hierarchy, which otherwise must share the single Surface for the window). The way this works is simpler than you may expect -- all SurfaceView does is ask the window manager to create a new window, telling it to Z-order that window either immediately behind or in front of the SurfaceView's window, and positioning it to match where the SurfaceView appears in the containing window. If the surface is being placed behind the main window (in Z order), SurfaceView also fills its part of the main window with transparency so that the surface can be seen.

这个是我最感兴趣的,SurfaceView是一个View的特殊实现。这个View会自己创建自己的Surface来绘制自己的界面,而不是在正常的视图继承树中绘制(换句话说,它用的根本不是窗口的Surface),SurfaceView创建的时候要做的就是让窗口管理器给他创建一个新的窗口,告诉它把自己的Z维上和主窗口的层次关系,然后告诉它应该显示窗口哪里,如果Surface被放置在主窗口的后面,SurfaceView就会在主窗口它本应该占据的位置绘制透明像素,以便于它还是可以被看到。(如果你用过窗口管理器和SurfaceView,你对这边一定深有感触)

5)A Bitmap is just an interface to some pixel data. The pixels may be allocated by Bitmap itself when you are directly creating one, or it may be pointing to pixels it doesn't own such as what internally happens to hook a Canvas up to a Surface for drawing. (A Bitmap is created and pointed to the current drawing buffer of the Surface.)

Bitmap(或者所有的图片格式)其实都是像素的集合。

看完概念,问题的答案就有了:

1)Dialog其实本身就是一个Window(所以Window不一定是充满全屏的),它实际上是浮在PhoneWindow的Surface之上的,因此,不管你如何布局,只要弹出对话框,它就一定是浮动在所有的View之上的;

2)SurfaceView也必然是浮在所有的View之上的,不管添加顺序如何,SurfaceView一定是绘制在你的布局之上的,当然你也可以绘制在PhoneWindow之下,那样子的话,它所占据的区域就是透明的,也就是你的XML布局就完全不可见了。(这一点愁死我了)。

3)SurfaceView的绘制过程告诉我们,我们绘制的时候,基本采用如下的代码:

Canvas canvas = sfh.lockCanvas(null); 
canvas.drawColor(Color.BLACK);// 清除画布 
//绘制其他图片
sfh.unlockCanvasAndPost(canvas);

那么PhoneWindow拥有的Surface应该也是这样子的绘制的。lockCanvas是可以锁定一个区域的(传参数Rect进去),这样子就很方便用来绘制所需要的组件了,比如按钮(我只要锁定它应该显示的位置,然后绘制即可);

4)结论如下:窗口拥有Surface,Surface可以锁定得到Canvas,View不过是画布上绘制得到的图形而已;