Android

Android界面绘制

2018-03-10  本文已影响61人  三十二蝉
Android界面绘制整体框图

任何一个操作系统实现界面绘制,都需要处理应用层、系统层和硬件层的分工协作:

应用层

除了系统窗口(Toast),主要在Activity中绘制界面,需要解决两个问题

系统Framework层

对于应用开发来说,最重要的是系统层中的Framework层,主要包括WMS和SurfaceFlinger两个系统服务,都运行在SystemServer进程中:

系统HAL层,系统Linux Kernel层和硬件层

把系统层的这两部分和硬件层放在一起说,是因为他们联系更紧密,更偏底层,平时做应用开发时也基本不涉及到。
HAL层:是个抽象接口,处理界面的是Gralloc接口,HAL是为了解决linux硬件驱动的版权问题(Android开源,但是有些厂商的硬件驱动不开源,用HAL可以规避这些问题)。
Linux Kernel层:Linux 内核使用帧缓冲FrameBuffer来实现显示功能,作为内存缓冲区(有32个Slot),既是操作硬件设备的接口,又可以缓解画面流畅和完整性的问题(队列、生产和消费)。
硬件层:利用驱动把数据输出到显示设备上。

Activity与Framework层的合作

Activity需要与Framework层的SurfaceFlinger和WMS合作,才能实现界面绘制,这个合作机制需要解决这样几个问题:

Window的创建与使用

Window的管理核心在WMS,所以Window的创建和使用都需要与WMS建立通信,并交给WMS管理和调度。

具体流程如下:

框图

所以,App内部的所有窗口由WindowManagerGlobal统一管理,而android系统的所有窗口由WMS统一管理。

Surface的获取与使用

Surface是ViewRootImpl通过Bindler机制从SurfaceFlinger中通过Ashmem共享内存获取到的。
实际上,ViewRootImpl持有一个Surface对象,所以问题在于,ViewRootImpl中如何为Surface关联到了SurfaceFlinger中的对象。
具体流程如下:

框图

View的创建与绘制前获取Canvas

我们定义的ViewTree其实是DecorView中R.id.content那一部分,所以View的创建与绘制,核心在于建立与Window的关联,并能访问Surface。

具体流程如下:

总结

除了Activity之外,最重要的就是ViewRootImpl、PhoneWindow和WindowManagerGlobal。

扩展

场景1

自定义系统开机画面,虽然没有启动Android,但是可以操作硬件驱动、或操作linux的framebuffer实现界面绘制,这也是各厂商自己定制系统时的修改方法。

场景2

侧滑App,为什么可以向一侧滑动整个App界面,考虑到App实际上是向DecorView添加了ViewTree,这就可以在DecorView和ViewGroup中间插一层透明的View,这样就能滑动原有的ViewTree,达到侧滑效果。

public class SwapeBackLayout extends FrameLayout{
     ...
     //用当前ViewGroup代替ViewTree的根节点
     ViewGroup decorView= (ViewGroup) activity.getWindow().getDecorView();
     View child=decorView.getChildAt(0);
     decorView.removeView(child);
     addView(child);
     decorView.addView(this);
     ...
}
场景3

我们知道事件分发是从Activity开始的,但是悬浮窗也可以设置为允许响应事件,但是悬浮窗是没有Activity的,只做了addView,那么悬浮窗的事件是如何响应的?
硬件层拦截到事件,会从WMS传递到ViewRootImpl,而ViewRootImpl是WindowManagerGlobal在addView时创建的,所以悬浮窗虽然只做了addView,也有ViewRootImpl,也能响应事件。
有Activity的情况下,ViewRootImpl的mView是DecorView,所以会把事件传递给DecorView,DecorView处理事件的函数为:

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback();
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

其中,mWindow是持有DecorView的PhoneWindow对象,而这个PhoneWindow对象的Callback,是在Activity的attach中,创建出PhoneWindow后,把Activity作为了Callback。

//Activity源码
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        ...
        mWindow = new PhoneWindow(this, window);
        ...
        mWindow.setCallback(this);

所以,在有Activity的情况下,事件传递是ViewRootImpl-->DectorView-->PhoneWindow.Callback也就是Activity,然后再传递给PhoneWindow-->DectorView-->ViewTree,这也可以解释为什么DectorView里同时存在dispatchTouchEvent和superDispatchTouchEvent两个函数。
而在没有Activity的情况下,ViewRootImpl的mView是我们addView时传入的ViewTree,事件就直接传递给ViewTree了。

参考

从系统角度理解Android的界面绘制

上一篇 下一篇

猜你喜欢

热点阅读