View显示原理

Android设备的显示屏被抽象成一个帧缓冲区(frame buffer,fb)。SurfaceFlinger通过向这个帧缓冲区写入内容来绘制界面。在硬件抽象层被封装成Gralloc???。

用户空间的应用进程先假装gralloc模块,用gralloc模块申请图形缓冲区,并映射入本进程空间,写入要绘制的内容。然后把图形缓冲区、通过fb、写入帧缓冲区

分配图形缓冲区,有两种方式,一种是直接在帧缓冲区上分配图形缓冲区,这样的话写入的内容就相当于直接写入帧缓冲区。第二种是在进程空间分配图形缓冲区,这使用的是匿名贡献内存。一般情况下,只有SurfaceFlinger会在帧缓冲区直接分配,而用户程序和SurfaceFlinger都会用匿名共享内存。

在内存中分配图形缓冲区的方法,也是需要通过SurfaceFlinger,由其创建一块匿名共享内存,然后和请求分配的进程共享。

Surface

何时创建

Surface的创建过程就是Activity的显示过程,在 ActivityThread.handleResumeActivity() 中调用了 Activity.makeVisible()具体实现:

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();//此处 getWindowManager 获取的是 WindowManagerImpl 对象
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

WindowManagerImpl中的addView

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

WindowManagerGlobal中的addView

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    ...
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    //创建 ViewRootImpl
    ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
    view.setLayoutParams(wparams);
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    //设置 View
    root.setView(view, wparams, panelParentView);
    ...
}

ViewRootImpl中的构造函数

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    ...
    final Surface mSurface = new Surface(); //创建 Surface,此时 Surface 创建完什么都没有,详见下面分析
    ...
    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        //获取 IWindowSession 的代理类
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mThread = Thread.currentThread(); //主线程
        mWindow = new W(this);
        mChoreographer = Choreographer.getInstance();
        ...
    }
}

在创建ViewRootImpl的时候会创建Surface,也会创建WindowSession用于和WMS通信,同时也会创建Choreographer他是存储在ThreadLocal中的也就是每一个线程都有一个

WindowManagerGlobal中的getWindowSession()

public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                //获取 IMS 的代理类
                InputMethodManager imm = InputMethodManager.getInstance();
                //获取 WMS 的代理类
                IWindowManager windowManager = getWindowManagerService();
                //经过 Binder 调用,最终调用 WMS
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {...},
                        imm.getClient(), imm.getInputContext());
            } catch (RemoteException e) {
                ...
            }
        }
        return sWindowSession
    }
}

WindowManagerService.openSession:

public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client, IInputContext inputContext) {
    //创建 Session 对象
    Session session = new Session(this, callback, client, inputContext);
    return session;
}

ViewRootImpl.setView()

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  synchronized (this) {
 
    requestLayout(); //详见下面分析
    ...
    //通过 Binder调用,进入 system 进程的 Session
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
          getHostVisibility(), mDisplay.getDisplayId(),
          mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
          mAttachInfo.mOutsets, mInputChannel);
    ...
  }
}
final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {

    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
        //调用 WMS.addWindow
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
}

WindowManagerService的addWindow

public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
    ...
    WindowToken token = mTokenMap.get(attrs.token);
    //创建 WindowState
    WindowState win = new WindowState(this, session, client, token,
                attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
    ...
    //调整 WindowManager 的 LayoutParams 参数
    mPolicy.adjustWindowParamsLw(win.mAttrs);
    res = mPolicy.prepareAddWindowLw(win, attrs);
    addWindowToListInOrderLocked(win, true);
    // 设置 input
    mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
    //详见下面分析
    win.attach();
    mWindowMap.put(client.asBinder(), win);
    
    if (win.canReceiveKeys()) {
        //当该窗口能接收按键事件,则更新聚焦窗口
        focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                false /*updateInputWindows*/);
    }
    assignLayersLocked(displayContent.getWindowList());
    ...
}

//WindowState.java
void attach() {
    mSession.windowAddedLocked();
}

ViewRootImpl的requestLayout()

public void requestLayout() {
   checkThread();
   mLayoutRequested = true;
   scheduleTraversals();
}
public void scheduleTraversals() {
    if(!mTraversalScheduled) {
       mTraversalScheduled = true;
       sendEmptyMessage(DO_TRAVERSAL); //发送 DO_TRAVERSAL 消息
    }
}

会调用scheduleTraversals,这里方法里会设置一个标记位防止多次调用requestLayout(),然后设置一个消息屏障屏蔽普通消息,丢一个异步消息到Choreographer,在下一次Vsync信号来的时候执行Runnable调动doTraversals,然后调用performTraversals,开始执行performMeasure,performLayout,performDraw。这里会调到draw方法

private void draw(boolean fullRedrawNeeded) {
    Surface surface = mSurface;//mSurface 是 ViewRoot 的成员变量
    ......
    Canvascanvas;

    try {
       int left = dirty.left;
       int top = dirty.top;
       int right = dirty.right;
       int bottom = dirty.bottom;

       //从 mSurface 中 lock 一块 Canvas
       canvas = surface.lockCanvas(dirty);
       ......
       mView.draw(canvas);//调用 DecorView 的 draw 函数,canvas 就是画布
       ......
       //unlock 画布,屏幕上马上就能看到 View 的样子了
       surface.unlockCanvasAndPost(canvas);
    }
    ......
}

此时的Surface是空的,什么都没有。需要向WMS汇报,然后绑定到native层的Surface
performTraversals()会调到relayoutWindow(),在 relayoutWindow() 中会调用 IWindowSession 的 relayout(),这是一个跨进程方法会调用到 WMS 中的 Session.relayout(),最后调用到 WindowManagerService.relayoutWindow()

public int relayoutWindow(Session session,IWindow client,
           WindowManager.LayoutParams attrs, int requestedWidth,
           int requestedHeight, int viewVisibility, boolean insetsPending,
           Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
            Configuration outConfig, SurfaceoutSurface){
        .....

    try {
         //win 就是 WinState,这里将创建一个本地的 Surface 对象
        Surfacesurface = win.createSurfaceLocked();
        if(surface != null) {
            //先创建一个本地 surface,然后在 outSurface 的对象上调用 copyFrom
            //将本地 Surface 的信息拷贝到 outSurface 中,为什么要这么麻烦呢?
            outSurface.copyFrom(surface);
        ......
}

WindowManagerService.java::WindowState:

Surface createSurfaceLocked() {
    ......
    try {
        //mSurfaceSession 就是在 Session 上创建的 SurfaceSession 对象
        //这里,以它为参数,构造一个新的 Surface 对象
        mSurface = new Surface(mSession.mSurfaceSession, mSession.mPid, mAttrs.getTitle().toString(), 0, w, h, mAttrs.format, flags);
    }
    Surface.openTransaction();//打开一个事务处理
    ......
    Surface.closeTransaction();//关闭一个事务处理
    ......
}

构造Surface对象

 public Surface(SurfaceSession s,//传入一个SurfaceSession对象
    int pid, String name, int display, int w, int h, int format, int flags) throws OutOfResourcesException {
        ......
        mCanvas = new CompatibleCanvas();
        //又一个 native 函数
        init(s,pid,name,display,w,h,format,flags);
        mName = name;
    }
static void Surface_init(JNIEnv*env, jobject clazz, jobject session, jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jintflags) {

    //从 SurfaceSession 对象中取出之前创建的那个 SurfaceComposerClient 对象
    SurfaceComposerClient* client = (SurfaceComposerClient*)env->GetIntField(session, sso.client);
    sp<SurfaceControl> surface;//注意它的类型是 SurfaceControl
    if (jname == NULL) {
        //调用 SurfaceComposerClient 的 createSurface 函数,返回的 surface 是一个 SurfaceControl 类型
        surface = client->createSurface(pid, dpy, w, h, format, flags);
    } else{
        ......
    }

   //把这个 surfaceControl 对象设置到 Java 层的 Surface 对象中
   setSurfaceControl(env, clazz, surface);
}

WMS向SurfaceFlinger通过SurfaceControl申请一块Buffer然后将它和将它和Java层的Surface绑定在一起
SurfaceControl 类可以看作是一个 wrapper 类,它封装了一些函数,通过这些函数可以方便地调用 mClient 或 ISurface 提供的函数。最后会执行 copyFrom() 返回给 App 客户端

copyFrom 期间一共有三个关键对象,它们分别是:SurfaceComposerClient、 SurfaceControl、Surface,这个 Surface 对象属于 Native 层,和 Java 层的 Surface 相对应其中转移到 ViewRoot 成员变量 mSurface 中的,就是最后这个 Surface 对象了。

在 SurfaceFlinger 进程中,Client 的一个 Layer 将使SharedBufferStack 数组中的一个成员,并通过 SharedBufferServer 结构来控制这个成员, SurfaceFlinger 是消费者,所以可由 SharedBufferServer 来控制数据的读取。与之相对应,客户端的进程也会有一个对象来使用这个 SharedBufferStack,可它是通过另外一个叫 SharedBufferClient 的结构来控制的??突Ф宋?SurfaceFlinger 提供数据,所以可由 SharedBufferClient 控制数据的写入


Surface显示过程

如图所示,在 App 进程中创建 PhoneWindow 后会创建 ViewRoot。ViewRoot 的创建会创建一个 Surface,这个 Surface 其实是空的,通过与 WindowManagerService 通信 copyFrom() 一个NativeSurface。在与 SurfaceFlinger 通信时,会创建 SharedClient 一段共享内存,里面存放的是 SharedBufferStack 对应 SurfaceFlinger 中的 SurfaceLayer 每个 Layer 其实是一个 FrameBuffer,每个 FrameBuffer 中有两个 GraphicBuffer 记作 FrontBuffer 和 BackBuffer。
  在SurfaceFlinger 中 SharedBufferServer 来管理 FrameBuffer。同时在 App 端 copyFrom() 出来 NativeSurface 时会创建一个 SharedBufferClient 与 SharedClient 这块共享内存关联。当客户端 addView() 或者需要更新 View 时,会通过 SharedBufferClient 写入数据到 ShareClient 中,SurfaceFlinger 中的 SharedBufferServer 接收到通知会将 FrameBuffer 中的数据传输到屏幕上。

SurfaceFlinger

持有现在和下一个状态,以及目前Surface数据,和显示设备数组。已GraphicPlane对象来管理系统的显示设备
有三种线程:

1、Binder线程:用来和用户进程通讯。
2、控制台线程:用来和内核通讯,内核一旦需要显示设备做响应,则发送信息,通过该线程处理。
3、UI渲染线程:专门负责渲染UI。如果没有渲染任务,则阻塞,有任务则通过gralloc和fb设备,渲染图形。

渲染过程

  • 在SurfaceFlinger中,应用程序窗口以Layer对象描述。SurfaceFlinger持有一个数组,在渲染时会先对Layer的各种属性,判断是否发生了变化。
  • 如果有变化,则调用Layer内部的doTransaction。Layer内部有存有描述状态的对象,根据改变,赋值各个属性。
  • 根据变化的属性,设置图形缓冲区的属性。SurefaceFlinger的Layers保存在BufferManager中,并且进行了编号。先标记当前Layer,然后处理其纹理,接下来处理各种区域,如可见区域、透明区域、半透明区域等等,在Layer内部做标记。
  • 如果SurfaceFlinger服务在编译时指定了宏USE_COMPOSITION_BYPASS,且当前要渲染的应用程序窗口只有一个,且其可见区域不为空,则跳过合并,直接渲染。在应用程序窗口唯一的时候,SurfaceFlinger为其开辟图形缓冲区是在帧缓冲区上的,也就是直接写入数据,则可通知fb渲染。则渲染完成。
  • 其他的情况,先进行各个图形缓冲区的合成,将各个窗口的各种区域合成到主屏幕上。然后再将主屏幕的内容渲染到帧缓冲区。
  • 上两步的渲染,最终都是通过Gralloc的post方法,这个方法中会判断图形缓冲区是否在帧缓冲区中分配的,如果是,那就是直接写入的,如果不是,需要通过memcpy复制过去。

何时创建

surfaceflinger是一个守护进程,


surfaceflinger启动.png

Zygote启动System进程在执行System的main方法,会加载android_servers库,会调用SurfaceFlinger.instantiate()方法。
SurfaceFlinger启动之前,Binder驱动已经启动好了,于是它把自己加入Binder线程池。接下来启动UI渲染线程。再创建一个对象描述显示屏,这个过程中会创建控制台线程,监控控制台事件。
开辟一块匿名共享内存,将显示屏的信息写在上面。

参考
https://www.cnblogs.com/blogs-of-lxl/p/11272756.html
https://fourfire.top/#/Android_Framework/View%E6%98%AF%E6%80%8E%E4%B9%88%E7%94%BB%E5%87%BA%E6%9D%A5%E7%9A%84%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94SurfaceFlinger
https://blog.csdn.net/yangwen123/article/details/12192401
https://blog.csdn.net/vviccc/article/details/104860616
http://08643.cn/p/198701520cd7?utm_campaign=haruki&utm_content=note&utm_medium=reader_share&utm_source=weixin

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351