display整体框架

梳理流程和图形参考Stan_Z的博客:
Android图形系统篇总结:https://www.jianshu.com/p/180e1b6d0dcd
Android 系统显示原理简介:https://www.jianshu.com/p/a978a6250f9e

一、Android显示系统整体框架介绍

1.1 显示系统关键组件:Activity、window、view、surface、canvas、surfaceflinger、layer、Choreographer、Vsync

  1. Activity:是APP与用户进行交互的入口,一个Activity对应着一个窗口(window),完成用户的key、touch事件的相应处理和对window生命周期进行管理
  2. Window:window是视图(view)的容器,Window有唯一的实现类:PhoneWindow。Window受WindowManager统一管理,对window管理通过WindowWtate,该变量是系统对Window管理的一个封装类,与window一一对应。
  3. view:视图,承载着将要显示的内容,其中DecorView(顶级视图)是当前Activity的根View,它里面管理着当前界面的View树,包含着标题栏titleBar和内容栏contentView。
Activity、Window和view关系
  1. surface:每个Window对应一个surface,作为View显示的载体,图形的传递通过buffer作为载体,因此surface是对buffer的封装,对图形的操作其实就是对suface中buffer的操作。
  2. canvas:画布,任何的view都需要画在surface的canvas上。
  3. SurfaceFlinger:作为一个service,接收所有的surface作为输入,创建layer,把不同的layer进行裁剪等合成操作来显示。
  4. layer:由surfaceFlinger进行创建,是surfaceFlinger进行合成的基本单位(一个surface对应一个layer),layer其实是一个FrameBuffer(帧buffer),每个FrameBuffer中有两个GraphicBuffer(图形buffer,因为Android为了将系统操作buffer和硬件显示buffer分开,所以设置了两个buffer),记为FrontBuffer和BackBuffer。
不同layer合成显示

从图中可以看出,最终显示在屏幕上的内容由多个surface(layer)通过SurfaceFlinger进行合成,surface上中不同的view都画在surface的canvas上。

  1. choreographer:处于上层,接收VSync信号,同Vsync配合,统一Android显示中的动画、输入和绘制时机。
  2. Vsync:让CPU、GPU和Buffer进行同步的一个信号。

1.2 显示流程概述:

将前面内容进行串联,Android应用程序的显示过程包含了两个部分(应用侧绘制、系统侧渲染)、两个机制(进程间通讯机制、显示刷新机制)。
应用层负责绘制(UI的测量、布局,即显示View的类型和Window对View的布局),系统层负责渲染(SurfaceFlinger对layer的合成和屏幕上的显示);
通过进程间通信(binder)把应用层需要绘制的数据传递给系统层服务,系统层服务通过刷新机制(Vsync、choreography)把数据更新到屏幕上。

显示流程具体细节:

  1. Android启动后,在init.rc解析时创建SurfaceFlinger实例并向serviceManager注册,启动SurfaceFlinger服务。
  2. Activity在启动过程中会创建PhoneWindow并且与之建立回调关系(用于接收touch等消息);在PhoneWindow中创建顶级视图DecorView。
  3. Activity调用函数setView(),里面有两个重要的函数:requestlayout()mWindowSession.addtoDisplay()。后者完成view的添加、app与surfaceFlinger的服务的连接;前者完成了view的绘制、view画在surface的canvas上并把surface传入SurfaceFlinger。
  4. SurfaceFlinger创建layer并将传入的Surface进行合成,最后将合成后的数据通过SurfaceFlinger::postComposition()函数画在屏幕上。
  5. 为了让CPU/GPU的合成帧率和Display的刷新率同步,使用了Choreographer,接收Vsync信号,让底层与上层进行同步。

二、Activity创建Window和加载view过程

解决问题:1. Window创建和建立回调过程;2. View是如何加载到Window中的

2.0 Activity的启动

Activity启动流程参考:一篇文章看明白 Android 从点击应用图标到界面显示的过程
在Android启动后,点击app图标,Launcher进程(就是一个Activity)通过Binder向系统服务(systemserver)ActivityManagerService(AMS)发起startActivity()请求;
PS:Launcher通过PMS来解析AM.xml配置文件得到组件信息,系统启动后就会启动Launcher组件,启动后向PMS查询所有Action名称和Category,为每个Acitivity组件创建快捷图标,方便下面点击启动。
AMS收到请求后向zygote进程发送创建进程请求并fork一个新的子进程(app进程);进入 app 进程后创建 ActivityThread 类并加载,并调用 ActivityThread.main() 方法。
ActivityThread.main()通过Binder向AMS发起attachApplication请求对app进行一些初始化;AMS经过一系列准备后向App进程发送scheduleLaunchActivity请求,app进程的binder线程(ApplicationThread)收到请求后通过由bindapplication()封装一些重要的信息,如appinfo、config等handler消息并向主线程发送LAUNCH_ACTIVITY消息;
主线程收到启动activity消息后,执行handleLaunchActivity()方法,通过Instrumentation()(Intrumentation:用来监控app和系统之间的交互操作。)创建Activity,然后调用Activity.attact().onCreate()方法进入Activity生命周期。

Activity启动流程

2.1 window的创建 、View创建和添加

Activity中Window创建过程

Activity启动时会创建Window,从上面可以看出,Activity启动过程中有重要的函数ActivityThread.handleLaunchActivity(),该函数中有三个重要的函数调用:

handlelaunchactivity()
{
...
  windowManagerGlobal.initialize() //获得 WMS的Binder引用
  performLaunchActivity() //创建要启动的Activity组件,创建窗口对象和视图对象
  handleResumeActivity() //将Activity组件进行激活
...
}

第二个函数performLaunchActivity()完成了PhoneWindow和View的创建,第三个函数handleResumeActivity()负责将组件进行激活。
看一下window创建函数performLaunchActivity()

performlaunchActivity()
{
  Activity activity = null;
//创建目标Activity对象和app对象
  activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent);
  Application app = r.packageInfo.makeApplication(false, mInstrumentation);
  activity.attach(...)
   //回调 Activity.onCreate(),onCreate()里面有setContentView(),我们自己设计的UI界面
   mInstrumentation.callActivityOnCreate(activity, ...);
 }


attach(...)
{
  mWindow = new phoneWindow() //创建Window,PhoneWindow是Window的唯一具体实现类
  //设置回调函数,实现了Window的callback接口,这样Activity就与Window关联起来
  mWindow.setCallback(this) 
  //初始化并且设置WindowManager,获取WindowManagerImpl对象。每一个activity都有一个WM对象。
  //mToken是IBinder类型,WMS就是通过这个 IBinder 来管理 Activity 里的 View
  mwindow.setWindowManager(...,mToken,...) 
  //mWindowManager与WMS进行通信,也是WMS识别View属于哪个Activity的关键
  mWindowManager = mWindow.getWindowManager()
  
}

performLaunchActivity().attach()中创建了一个PhoneWindow对象,实现了Window的创建和与Activity的关联,并且创建了WindowManager(WindowManager 是一个抽象类,具体实现是在 WindowManagerImpl 中).由前面已知Window是View的载体,Window将view添加到WMS过程就是由WindowManager实现的。

回调 Activity.onCreate() 后,会执行 setContentView() 方法将我们写的 Layout 布局页面设置给 Activity,即加载顶级视图DecorView。

//Activity.java
public void setContentView()
{
    getWindow().setContentView(layoutResID);    //window的一个抽象方法 ,实现类是PhoneWindow
    initWindowDecorActionBar();    
}

// PhoneWindow
public void setContentView(int layoutResID) 
{
 ...
  installDecor(); //初始化Decor,将mContentParent关联到DecorView上
  mLayoutInflater.inflate();//填充Layout,将Activity设置的布局文件,加载到mContentParent中
  final Callback cb = getCallback();//通知Activity布局改变
  cb.onContentChanged();
  ... 
}

private void installDecor()
{    
//根据不同的 Theme,创建不同的 DecorView,DecorView 是一个 FrameLayout 
}

PhoneWindow.setContentView()完成 初始化(创建DecorView对象和mContentParent对象)、 填充Layout(将Activity布局文件添加到DecorView里)、 通知Activity 进行布局改变。整个布局加载流程为:创建DecorView,把xml的View树解析出来,加到DecorView上。

创建完成后他们之间没有关系和联系,需要对组件进行激活并建立联系,需要将DecorView添加到window上。ActivityThread.handleLaunchActivity().handleResumeActivity()函数调用performResumeActivity().performResume()函数回调onResume()实现组件的激活。

//ActivityThread.java
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {
    //将Activity数据记录到ActivityClientRecord中,执行到 onResume()
    ActivityClientRecord r = performResumeActivity(token, clearHide);

    if (r != null) {
        final Activity a = r.activity;
        boolean willBeVisible = !a.mStartedActivity;
        ...
         if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();//定义一个顶级视图decor
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();// 定义一个ViewManager类型的变量 wm
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                wm.addView(decor, l);// 将Decor添加到窗口上
            }

        }
        ...
   if (!r.activity.mFinished && willBeVisible  && r.activity.mDecor != null && !r.hideForNow)
   {
            ...
            mNumVisibleActivities++;
            if (r.activity.mVisibleFromClient) {
                //添加视图,详见下面分析,
                r.activity.makeVisible(); 
            }
    }

        //resume 完成
        if (reallyResume) {
              ActivityManagerNative.getDefault().activityResumed(token);
        }
    } else {
        ...
    }
}

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

通过getWindowManager()创建的wm的类方法addview()添加了decorView,addview()经过多次函数调用最终得到类ViewRootImpl的setView()方法,该方法十分重要,是所有工作的起点,不仅把View添加到window上,而且触发了绘制流程,完成了请求SurfaceFlinger创建Surface、View显示在GraphicFrame上、建立app与SurfaceFlinger之间的服务连接等任务。

//frameworks/base/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl(View.Context context, Display display)
{
  setView(); 
  mChoreographer = Choreographer.getInstance(); // choreographer的启动和服务
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
{
//  把传进去的DecorView赋值给mView
  mView = view;
  requestLayout(); // View的绘制流程(PS:在执行addview()之前需要将UI布局绘制完成)
//mWindowSession类型是interface WindowSession,在getWindowSession()中实现
  res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                             getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                             mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                             mTempInsets);
//处理触摸事件回调
  mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
}
public static IWindowSession getWindowSession()
{
// 获取了WMS,openSession返回值是sWindowSession
  IWindowManager windowManager = getWindowManagerService();
// sWindowSession的具体实现在windowManager中。
  sWindowSession = windowManager.openSession();
  return sWindowSession;
}
// 到这里可以看到, IWindowSession接口真正实现类是Session,它是一个Binder。
public IWindowSession openSession()
{
  Session session = new Session;
}

//frameworks/base/services/core/java/com/android/server/wm/Session.java
class Session(){
  final WindowManagerService mService;
  addToDisplay()
  {
      return mService.addWindow()
  }
}
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

// addwindow()代码太多,具体的实现可以看博客中的那张图


2.2 View的绘制流程

在方法setView()中requestLayout()实现了View的绘制,从Activity到View绘制的函数调用如下图:


从图中可以看出,ViewRootImpl.performTraversals()方法是View绘制的执行起点。ViewRootImpl则肩负着View的标准三步曲(测量、布局、绘制)。

private void performTraversals()
{
//执行测量操作 
performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);
//执行布局操作
performLayout(lp, Width, mHeight);
//执行绘制操作
performDraw();
}

实际上,view绘制一共有5个阶段,多了预测量阶段和窗口布局阶段:
预测量阶段: 这是绘制的第一个阶段,对View树进行第一次预测量,计算View树的显示内容所需的尺寸(View树期望的尺寸,是否满足不一定)。通过方法measureHierarchy()实现 ( PS: measureHierarchy()方法最终也是调用了performMeasure()方法对View树进行测量)
窗口布局: 根据预测量的结果,调用IwindowSession.relayout()来请求WindowManagerService服务调整window的大小等属性并对window重新布局,布局结果返回给ViewRootImpl,保存在mWinFrame中。
测量过程: 预测量结果只是View树期待的窗口尺寸,具体大小需要WMS的布局(WMS布局的结果可能不满足预测量的期望尺寸,但是View树必须依据WMS的布局结果)。WMS布局结果出来后,调用performMeasure()对View树进行最终测量。
开始布局: 在测量阶段对窗口中的view进行了最终测量,然后根据测量结果,调用performLayout()开始对View树进行布局。
绘制过程:这是View三部曲的最后一步,确定了window上控件的位置和尺寸后(已经完成布局操作),通过调用performDraw(),最终调用到 drawBackground(canvas)方法,实现View的绘制(上面提到过,所有的View都是画在Canvas上的)

到这里,已经介绍了Window的创建、View的创建和添加、View的绘制等内容,应用侧工作已经完成,还有一个系统侧工作。

三、Surface与SurfaceFlinger

在上面提到Window、View的创建和绘制,这些都是上层的一些操作,那么如何将上层的内容显示到硬件的显示屏上呢? 这部分将介绍SurfaceFlinger创建Surface、surfaceFlinger合成Layer和送显过程。

3.0 SurfaceFlinger服务启动概述

SurfaceFlinger系统服务在init阶段启动,主要实现了Surface的建立、控制、管理等功能,能够将各种应用程序的2D、3D surface进行组合。
启动SurfaceFlinger时在main_surfaceflinger.cpp里 main()中实例化一个SurfaceFlinger,第一次实例化会执行onFirstRef()创建MessageQueue的对象Handler和消息循环looper,然后初始化右边的EGL等内容。HWC代表着硬件显示设备,注册了 VSYNC 信号的回调。当硬件产生VSYNC信号时,则会发送消息,handler 收到消息进行处理。当 SurfaceFlinger 进程收到 VSync 信号后经层层调用,最终调用到该对象的 handleMessageRefresh() 方法。

SurfaceFlinger.cpp:
void SurfaceFlinger::handleMessageRefresh() 
{
    ATRACE_CALL();
    preComposition();//处理显示设备与 layers 的改变,更新光标
    rebuildLayerStacks();//重建所有可见 Layer 列表,根据Z轴排序
    setUpHWComposer();//更新 HWComposer 图层
    doDebugFlashRegions(); 
    doComposition();//生成 OpenGL 纹理图像
    postComposition();//将图像传递到物理屏幕
}

OpenGL:open graphics library,用于渲染2D、3D矢量图形的、操作GPU的API,通过驱动向GPU发送相关指令。
EGL:作为 OpenGL 和原生窗口系统之间的桥梁,用于opengl与本地窗口交互时的中间层,与平台无关。具体作用是为openGL指令创建context、绘制目标surface、配置FrameBuffer属性、Swap提交绘制结果。
HWC:Hardware Composer HAL (HWC) 是 SurfaceFlinger 用来将 Surface 合成到屏幕,介于SurfaceFlinger和HAL之间,用于处理部分SurfaceFlinger的合成任务和产生vsync信号。

3.1 app与SurfaceFlinger服务连接过程

ViewRootImpl.setView()中mWindowSession.addToDisplay()实现了app与SurfaceFlinger的连接。
在addToDisplay()方法中执行win.attach()(类WindowState的方法attach()),然后在attach()方法中通过mSession.windowaddedlocked()创建SurfaceSession对象(Session的成员变量SurfaceSession通过windowAddedLocked()方法进行初始化),并将Session实例添加到WMS.mSessions中,这样实现了与WMS进行通信。SurfaceSession的创建会调用JNI中的nativeCreate()函数。(从上层到了下层)

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

//Session.java
void windowAddedLocked() {
    if (mSurfaceSession == null) {
        mSurfaceSession = new SurfaceSession();
        mService.mSessions.add(this);
        if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
            mService.dispatchNewAnimatorScaleLocked(this);
        }
    }
    mNumWindow++;
}
// ./frameworks/base/core/java/android/view/SurfaceSession.java
public SurfaceSession()
{
  mNativeClient = nativeCreate():
}

下面是app与SurfaceFlinger建立连接的关键点
在nativeCreate()中创建了SurfaceComposerClinent对象 client,作为跟 SurfaceFlinger 通信的代理对象。
并且client被赋值给了mSession(因为调用的是 mSession.windowAddedLocked()),

//android_view_SurfaceSession.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz) 
{
    SurfaceComposerClient* client = new SurfaceComposerClient();
}
// 在赋值时,SurfaceComposerClient类成员函数onFirstRef将被调用
// frameworks/native/libs/gui/SurfaceComposerClient.cpp
void SurfaceComposerClient::onFirstRef() {
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if (sf != nullptr && mStatus == NO_INIT) {
        sp<ISurfaceComposerClient> conn;
        conn = sf->createConnection();
        if (conn != nullptr) {
            mClient = conn;
            mStatus = NO_ERROR;
        }
    }
}

sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
    return initClient(new Client(this));
}
static sp<ISurfaceComposerClient> initClient(const sp<Client>& client) {
    status_t err = client->initCheck();
    if (err == NO_ERROR) {
        return client;
    }
    return nullptr;
}

到这里,方法onFirstRef()中创建了sp<ISurfaceComposer> sf和 sp<ISurfaceComposerClient> conn, 然后 conn赋值给了sp<SurfaceComposerClient> mClient。sf 获得了SurfaceFlinger服务的代理接口(在调用getComposerService()方法时,会在构造函数中获得SurfaceFlinge服务的代理接口,并且创建一块匿名共享内存,此时sf就有值了),成员函数createConnection()建立SurfaceComposerClinet与SurfaceComposer的连接。

ComposerService作为client与SurfaceFlinger 作为 server进行binder IPC,获得SurfaceFlinger创建的Client对象,拿到SurfaceFlinger服务;与此同时,ComposerService创建的Client(server端)与SurfaceComposerClient(client端)又互为binder IPC的两端。
从代码中理解,sf是一个指针,获得了CompoesrService,然后sf与SurfaceFlinger创建的Client建立连接;
sf然后返回给conn,conn是SurfaceComposerClient类型。


3.2 app请求surfaceFlinger创建Surface

在请求建立服务的过程中,ViewRootImpl.setView()中mWindowSession.addToDisplay()实现了app与SurfaceFlinger的连接。ViewRootImpl.setView()还有一个重要的函数requestLayout()。该函数实现了surface的创建。
requestLayout()经过多层调用到WMS.relayoutWindow()方法

//WindowManagerSerview
public int relayoutWindow(..., Surface outSurface)
{
  synchroized(mWindowMap)
  { 
     WindowState win = windowForClientLocked();
     Surface surface  = win.createSurfaceLocked();
     if(surface != null) { outSurface.copyForm(surface); ...}
  }
}

在WindoManagerService中createSurfaceLocked()方法new了一个surface(这里是java层的surface),并且将创建的内容拷贝到outSurface中(ViewRootImpl传进来的参数),用作返回给APP使用。


那么创建出来的surface对象是什么样子呢?其实surface主要包含三个变量

Surface {
  private int mSurfaceControl //保存C++层的一个SurfaceControl对象的地址值
  private Canvas mCanvas //描述一块类型为CompativleCanvas的画布(Canvas)
  private String mName //描述当前正在创建的一个绘图surface的名称
} 

这里的mSurfaeControl是java层的,控制底层的surface。在类surface 的成员函数中有名为init()的JNI方法(Surface_init()),可以创建C++层SurfaceControl对象。

Surface_init{
// 从 SurfaceSession 对象中取出之前创建的那个 SurfaceComposerClient 对象
SurfaceComposerClient* client = (SurfaceComposerClient*) env ->GetIntField(session,sso.client)
sp<SurfaceControl> surface;//注意它的类型是 SurfaceControl
// 调用SurfaceComposerClient的createsurface函数,这个surface类型是surfacecontrol类型,为WMS服务
surface = client ->createSurface();
...
   //把这个 surfaceControl 对象设置到 Java 层的 Surface 对象中
   setSurfaceControl(env, clazz, surface);
}

在SurfaceComposerClient类型的client中创建surface,在3.1中所描述的,SurfaceComposerClient通过Binder获取到ComposerService的对象,ComposerService最后到SurfaceFlinger中执行createSurface(本地surface)。
在 createSurface 内部会使用 Binder 通信将请求发给 SurfaceFlinger

sp<ISurface>SurfaceFlinger::createSurface()
{
    sp<LayerBaseClient> layer;//LayerBaseClient 是 Layer 家族的基类
    //LayerBaseClient 的内部类,它也叫Surface
    sp<LayerBaseClient::Surface> surfaceHandle;

    //根据 clientId 找到 createConnection 时加入的那个 Client 对象
    sp<Client> client = mClientsMap.valueFor(clientId);
    //注意这个 id,它的值表示 Client 创建的是第几个显示层
    //同时也表示将使用 SharedBufferStatck 数组的第 id 个元素
    int32_t id = client->generateId(pid);

    //根据 flags 参数来创建不同类型的显示层
    switch(flags & eFXSurfaceMask) 
        case ...
                  layer = createNormalSurfaceLocked(client, d, id, w, h, flags, format);
         break;
...
    //从显示层对象中取一个ISurface对象赋值给SurfaceHandle
    surfaceHandle = layer ->getSurface();
    return surfaceHandle;//ISurface 的 Bn 端就是这个对象
}

前面所说,每一个surface对应着一个layer,所以createSurface主要是创建了一个layer(可根据不同的参数创建不同的layer),然后layer初始化一些参数。

sp<LayerBaseClient>SurfaceFlinger::createNormalSurfaceLocked()
{
 //创建一个 Layer 类型的对象
    sp<Layer> layer = new Layer(this, display,client, id);

    //设置 Buffer
    status_t err = layer->setBuffers(w, h, format, flags);
    if (LIKELY(err == NO_ERROR)) {
        //初始化这个新 layer 的一些状态
        layer->initStates(w, h, flags);
        //下面这个函数把这个 layer 加入到 Z 轴集合中
        addLayer_l(layer);
    }
...
return layer;
}

在 上面的surfaceHandle = layer ->getSurface();可以获取SurfaceFlinger创建的Surface对象,然后该变量返回给sp<SurfaceControl> surface,这样surfaceControl就与底层的surfacehandle建立了链接,并且能够控制底层surface。
Surface类成员mClient(SurfaceClient::getInstance())指向了app进程中的一个SurfaceClient单例,mSharedBufferClien指向了SharedBufferClient对象

void Surface::init()
{
  int32_t token = mClient.getTokenForSurface(mSurface);
  mSharedBufferClient = new SharedBufferClient(mClient.getSharedClient(),token,2,mIdentity);
}
// getTokenForSurface()调用setToken()
status_t Layer::setToken()
{
  //new 一个SharedBufferServer
  sp<SharedBufferServer> lcblk = new SharedBufferServer();
}

总结:
将APP window绘制在屏幕的载体是Surface,因此需要在app和底层都创建Surface承载各自的内容,并且需要控制变量SurfaceControl。
APP:在WindoManagerService中创建surface,并且将创建的内容拷贝到outSurface中(ViewRootImpl传进来的参数),用作返回给APP使用,这样app层的surface与WMS层的Surface就建立了联系。此外,app创建的surface与C++层管理一个Surface对象用来绘制APP UI,WMS创建的Surface在C++关联一个SurfaceControl对象,用来设置app window属性。

底层:SurfaceFlinger服务中创建了surfaceHandle,这样surfaceControl就与底层的surfacehandle建立了链接,并且能够控制底层surface。创建Surface过程就是创建Layer、buffer。

surface创建过程

3.3 app中的view如何画在surface的canvas上

还是3.2中的requestLayout()函数,一直调用到drawSoftware(surface,...)

drawSoftware(surface,...)
{
  canvas = mSurface.lockCanvas(dirty);#获取canvas
  mView.draw(canvas); #通过Canvas绘制view,是具体的绘制实现
  surface.unlockCanvasAndPost(canvas); #绘制结束
}

重点看一下mView.draw(canvas).

onDraw(Canvas canvas)
{
  canvas.save(); // 坐标系原点,坐标轴方向等信息
  canvas.translate();//开始绘制
}

onDraw()中的translate()与jni中的native_translate()相关联,native_translate()然后调用SKCanvas.cpp中的translate(),开始由底层SKCanvas来完成真正的绘制工作。
总结:绘制view时,首先获取一块存放绘制数据的buffer,然后传入canvas进行绘制,绘制其实是调用底层的SKia引擎进行绘制,包含画家SKCanvas和画布SKBitmap;绘制结束后调用unlockCanvasAndPost(canvas),让surface通过queueBuffer将数据投放到queue中,通知SurfaceFlinger进行使用。

3.4 surfaceFlinger合成多个layer

每个Android app都有很多window,每个window都有自己的UI元数据,因此不能使用binder在app和surfaceflinger之间传递UI数据,因为数据量太大,复制效率不高,所以选择使用Android os的匿名共享内存方案(SharedClient)

sharedclient 里面有最多31个sharedbufferstack,每个stack里面有N个buffer(共享缓冲区堆栈,每一个stack与一个surface对应,一个surface与一个window对应)。因此一个app内部最多有31个window。


SurfaceFlinger进程是init进程通过Init.rc创建的,然后独立运行在系统中。创建SurfaceFlinger由main_surfaceflinger.cpp中的main函数来实现

main {
 flinger = new SurfaceFlinger ;# 实例化SurfaceFlinger
 sp <SurfaceFlinger> flinger ->init #把实例化的SurfaceFlinger初始化
 sp<IServiceManager> sm ->addService() #把SurfaceFlinger注册到ServiceManager
}

图层合成就是把多个图层,按照既定的显示区域,展现到显示屏上。


在创建SurfaceFlinger时会调用onFirstRef()函数,然后创建looper对象和handler对象,handle对象实现消息的接收和根据消息类型进行图形处理

handleMessage 
{  case INVALIDATE #用于处理Layer或display属性变化和layer对应buffer的更新
  ..........onMessageReceived()
   case REFRESH #表示SurfaceFlinger需要一次合成(Refresh)操作
  ..........onMessageReceived()
} 
onMessageReceived()
{
case MessageQueue::INVALIDATE
 ....  if layer属性变化:handleMessageTransation()处理;
 ....  else buffer更新:handleMessageInvaliate()处理
 
case MessageQueue::REFRESH
   ....handleMessageRefresh()处理
}

SurfaceFlinger合成和投递共有5个步骤:



详细的内容可以参考Android图形显示,详细讲了HWC、SurfaceFlinger合成。

3.5 surface显示过程

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

在SurfaceFlinger和app端分别对应Layer对象和Surface对象,用SharedBufferServer对象和SharedBufferClient来操作。app想要更新surface时,找到对应的SharedBufferClient对象找到它对应的SharedBufferStack,从bufferqueue尾部找到一个空闲的buffer,然后app请求为这个buffer分配GraphicBuffer,然后把GraphicBuffer返回给app访问。app得到就写入UI数据并插入到对应的SharedBufferStack,然后通知SurfaceFlinger去消费。SurfaceFlinger找到SharedBufferServer对应的成员函数queue去获得待渲染的buffer。

四、Vsync与Choreographer

在上面介绍了window的创建、view的绘制(对窗口的测量和绘制)、surface的创建和关联、view画到canvas、将画好的canvas通过SurfaceFlinger进行合成投递,如此显示系统从Activity到屏幕就介绍完毕。
但是,如何协调CPU/GPU和显示帧之间的关系呢。在Android4.1之后增加了Choreographer机制,用于同Vsync机制配合,统一动画、输入和绘制的时机。
VSYNC是Vertical Synchronized的缩写,是一种定时中断,即当收到Vsync通知时,CPU和GPU立即开始计算数据然后写入buffer中,避免绘制的随机性。Choreographer起调度作用,将绘制工作统一到VSYNC的某个时间点上,使应用的绘制工作有序进行。引入VSYNC核心目的就是解决刷新不同步的问题;

Choreographer:一个java类,是个消息处理器,根据vsync 信号 来计算frame,而计算frame的方式就是处理三种回调,包括事件回调、动画回调、绘制回调。这三种事件在消息输入、加入动画、准备绘图layout 等动作时均会发给Choreographer。收到VSYNC信号时,调用用户设置的回调函数,三种类型的回调为:
CALLBACK_INPUT:优先级最高,与输入事件有关;
CALLBACK_ANIMATON:第二优先级,与动画有关;
CALLBACK_TRAVERSAL:最低优先级,与UI控件绘制有关;
View的onclick、onDraw等等都是从Choreographer.doFrame开始执行的;关于Choreographer可以参考Android Choreographer 源码分析

引入Vsync同步机制,可以使得CPU/GPU和display按照一个规定时机进行帧制作和显示。


4.1 Choreographer 启动

首先介绍Choreographer,其构造在ViewRootImpl中

public ViewRootImpl()
{
...
  // 这里获取了Choreographer的实例
  mChoreographer = Choreographer.getInstance();
...
}

public static Choreographer getInstance() 
{
//sThreadInstance是一个ThreadLocal对象,ThreadLocal 是线程的局部变量, 是每一个线程所单独持有的
        return sThreadInstance.get();
}

private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() 
{
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();
            return new Choreographer(looper, VSYNC_SOURCE_APP);
        }
}

Choreographer是 线程单例的。接着我们来看一下

private Choreographer(Looper looper) {    
  mLooper = looper;    
//使用当前线程looper创建 mHandler,接受处理消息
  mHandler = new FrameHandler(looper); 
// 用来接收垂直同步脉冲 vsync,用来控制系统的同步操作
//可以通过读取系统属性debug.choreographer.vsync来获取
//VSync事件接收器就是FrameDisplayEventReceiver(重要)
  mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;   
// 标记上一个frame的渲染 
  mLastFrameTimeNanos = Long.MIN_VALUE;    
// 计算一帧的时间,Android手机屏幕是60Hz的刷新频率,就是16ms
  mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());   //屏幕刷新周期 
// 初始化callbackQueue,ballback队列将在下一帧开始渲染时回调,创建一个大小为5的数组
  mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];   
// 初始化回调任务链表数组
  for (int i = 0; i <= CALLBACK_LAST; i++) {        
   mCallbackQueues[i] = new CallbackQueue();    
  }
}

sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() 
{
    return initClient(new Client(this));
}
static sp<ISurfaceComposerClient> initClient(const sp<Client>& client) 
{
    status_t err = client->initCheck();
    if (err == NO_ERROR) {
        return client;
    }
    return nullptr;
}

4.2 Choreographer 执行流程

Activity启动 走完onResume方法后,会进行window的添加。window添加过程会 调用ViewRootImpl的setView()方法,setView()方法会调用requestLayout()方法来请求绘制布局,requestLayout()方法内部又会走到scheduleTraversals()方法,最后会走到performTraversals()方法,接着到了我们熟知的测量、布局、绘制三大流程了。所有UI的变化都是走到ViewRootImpl的scheduleTraversals()方法

scheduleTraversals() 到 performTraversals() ,根据我们上面的介绍,在VSync信号到来时才会执行绘制,即performTraversals()方法。

// ViewRootImpl.java
void scheduleTraversals() {
    if (!mTraversalScheduled) {
     //此字段保证同时间多次更改只会刷新一次,例如TextView连续两次setText(),也只会走一次绘制流程
        mTraversalScheduled = true;
        //添加同步屏障,屏蔽同步消息,保证VSync到来立即执行绘制
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //mTraversalRunnable是TraversalRunnable实例,最终走到run(),也即doTraversal();
        //第一个参数三种回调类型,第二个参数添加了Runnable,第三个参数表示是否需要延时
        mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
    if (mTraversalScheduled) 
    {
        mTraversalScheduled = false;
        //移除同步屏障
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        ...
        //开始三大绘制流程
        performTraversals();
        ...
     }
}

在上面的代码中,scheduleTraversals()调用了mChoreographer.postCallback()方法,发送一个会在下一帧执行的回调,即在下一个VSync到来时会执行TraversalRunnable–>doTraversal()—>performTraversals()–>绘制流程。

    //输入事件,首先执行
    public static final int CALLBACK_INPUT = 0;
    //动画,第二执行
    public static final int CALLBACK_ANIMATION = 1;
    //插入更新的动画,第三执行
    public static final int CALLBACK_INSETS_ANIMATION = 2;
    //绘制,第四执行
    public static final int CALLBACK_TRAVERSAL = 3;
    //提交,最后执行,
    public static final int CALLBACK_COMMIT = 4;

安排任务—postCallback

public void postCallback(int callbackType, Runnable action, Object token)
 {
   postCallbackDelayed(callbackType, action, token, 0);
 }
public void postCallbackDelayed() 
{
  ...
  postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}

private void postCallbackDelayedInternal() 
{
        synchronized (mLock) {
//当前时间
            final long now = SystemClock.uptimeMillis();
//回调执行时间,为当前时间+延迟时间
            final long dueTime = now + delayMillis;
//取对应类型的CallbackQueue添加任务
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
//如果delayMillis = 0s,就是没有延迟时,duetime == now,马上执行
                scheduleFrameLocked(now);
            } else {
//如果有延迟,发送有一个msg定时消息,等时间到了再处理,
//最终也是执行上面一个步骤,scheduleFrameLocked(now)
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

此处获取当前时间,然后加上要延迟的时间,作为当前Callback的时间点,以这个时间点作为标准,把Callback对象添加到mCallbackQueues[callbackType]队列当中.当执行时间还没到时,往当前的队列中添加一个Message,那么通过Handler机制就会进行处理,此处的mHandler是一个FrameHandler对象

private final class FrameHandler extends Handler {    
  public FrameHandler(Looper looper) {        
    super(looper);  
  }    
  @Override    
  public void handleMessage(Message msg) {        
    switch (msg.what) {            
执行doFrame,即绘制过程,开始渲染下一帧的操作
      case MSG_DO_FRAME:  
        doFrame(System.nanoTime(), 0);                
      break;     
//申请VSYNC信号,例如当前需要绘制任务时       
      case MSG_DO_SCHEDULE_VSYNC:              
        doScheduleVsync();                
      break;            
//需要延迟的任务,最终还是执行上述两个事件
      case MSG_DO_SCHEDULE_CALLBACK:       
        doScheduleCallback(msg.arg1);                
      break;        
  }    
  }
}

如果时间还没有,会判断消息,最后还是会走到下面这个方法:doScheduleCallback().scheduleFrameLocked()

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {
        mFrameScheduled = true;
        //检查是否使用了Vsync机制,通过检查系统值进行确定
        if (USE_VSYNC) {
            //当前执行的线程,是否是mLooper所在线程
            if (isRunningOnLooperThreadLocked()) {
//请求vsync信号,最终调到native层,native处理后出发FramedisplayEventReceiver
//的onVsync()回调,然后执行doFrame()
                scheduleVsyncLocked();
            } else {
// 若不在,就用mHandler发送消息到原线程,最后还是调用scheduleVsyncLocked方法
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);//异步
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        } else {
 //如果系统没有使用VSync机制,则使用异步消息延时执行屏幕刷新(4.1后默认开启)
            final long nextFrameTime = Math.max(
                    mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now);
            Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, nextFrameTime);
        }
    }
}

到这里,FrameHandler的作用很明显里了:发送异步消息(因为前面设置了同步屏障)。有延迟的任务发延迟消息、不在原线程的发到原线程、没开启VSYNC的直接走 doFrame 方法取执行绘制。

申请和接受VSync
调用mDisplayEventReceiver的scheduleVsync()方法,mDisplayEventReceiver是Choreographer构造方法中创建,是FrameDisplayEventReceiver 的实例。 FrameDisplayEventReceiver是 DisplayEventReceiver 的子类,DisplayEventReceiver 是一个 abstract class

// mDisplayEventReceiver类变量是在Choreographer的构造方法中赋值的
private void scheduleVsyncLocked() {
    //申请Vsync信号
    mDisplayEventReceiver.scheduleVsync();
}
public void scheduleVsync() {
  nativeScheduleVsync(mReceiverPtr);
}

底层的 nativeScheduleVsync()向surfaceflinger服务注册,即在下一次脉冲接受后调用DisplayEventReceiver的dispatchVsync()方法。(每次调用只有一次dispatchVsync()方法回调)
底层向应用层发送VSYNC信号,java层通过dispatchVsync()接受,最后的回调在FrameDisplayEventReceiver()的onVsync()中

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {          
            if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
               scheduleVsync();
                return;
            }

             mTimestampNanos = timestampNanos;
            mFrame = frame;
//将本身作为runnable传入msg, 发消息后 会走run(),即doFrame(),也是异步消息
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
//此处的mHandler为FrameHandler
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }

onVsync()方法通过FrameHandler向主线程looper发送callback消息,当looper执行到该消息时调用run()方法,然后调用doFrame()。然后doFrame()在Vsync事件到来时顺序执行CallbackQueue队列中注册的回调,调用doCallbacks().CallbackRecord对应的run()方法。

    void doFrame(long frameTimeNanos, int frame) {
            ...

        try {
            // 按类型顺序 执行任务
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

    void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
        
            final long now = System.nanoTime();
            // 根据指定的类型CallbackkQueue中查找到达执行时间的CallbackRecord
       callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);
        try {
            // 迭代执行队列所有任务
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                // 回调CallbackRecord的run,其内部回调Callback的run
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    //回收CallbackRecord
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
        }
    }
//上面主要内容就是取对应任务类型的队列,遍历队列执行所有任务
//执行任务是 CallbackRecord的 run 方法:
    private static final class CallbackRecord {
...
        @UnsupportedAppUsage
        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                // 通过postFrameCallback 或 postFrameCallbackDelayed,会执行这里
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                //取出Runnable执行run()
                ((Runnable)action).run();
            }
        }
    }

前面看到mChoreographer.postCallback传的token是null,所以取出action,就是Runnable,执行run()。这里的action就是 ViewRootImpl 发起的绘制任务mTraversalRunnable了,那么这样整个逻辑就闭环了.


run()方法被执行,所以doTraversal()被执行,调用performTraversals开启View的绘制流程。

4.3 Choreographer小结

使用Choreographer的postCallback()、postFrameCallback() 作用理解:发送任务 存队列中,监听VSync信号,当前VSync到来时 会使用mHandler发送异步message,这个message的Runnable就是队列中的所有任务。
参考博客:Android屏幕刷新机制—VSync、Choreographer 全面理解!

VSYNC,这个具体指啥?在屏幕刷新中如何工作的?
答:当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时会出现的vertical sync pulse(垂直同步脉冲)来保证双缓冲在最佳时间点才进行交换。并且Android4.1后 CPU/GPU的绘制是在VSYNC到来时开始。

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