天天看点

App的启动过程(5)ViewTree遍历中最后一步的Draw

以上是WMS端窗口的添加,下面接着ViewTree遍历中最后一步的Draw的分析。

private boolean drawSoftware()à canvas =mSurface.lockCanvas(dirty);

跟View交互的是Canvas,比如draw(Canvas cs)参数,应用进程与surfaceflinger交互的是surface,即应用进程端的本地窗口,那么canvas和surface之间怎么协作的?

lockCanvas @Surface.java –>nativeLockCanvas@ android_view_Surface.cpp

static jlong nativeLockCanvas((){

//这个是C++层的surface对象

         sp<Surface>surface(reinterpret_cast<Surface *>(nativeObject));

//获取一个存储UI数据的buffer,

         ANativeWindow_BufferoutBuffer;

//通过IGraphicBufferProducer的dequeuBuffer,获取一个可用的buffer,

status_t err =surface->lock(&outBuffer, dirtyRectPtr);

//为bitmap分配内存空间,为bitmap分配可用的存储空间

         SkBitmapbitmap;

bitmap.setInfo(info,bpr);

bitmap.setPixels(outBuffer.bits);

//构造本地层的canvas,第二个参数是canvasObj是java层的canvas对象

Canvas*nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);

//给canvas这个作画的工具集设置作画的画纸,也就是bitmap,实际上bitmap的画纸来自于outBuffer,而outBuffer所指向的内存是surface通过lock去申请

nativeCanvas->setBitmap(bitmap);

//返回一个java层的surface

sp<Surface>lockedSurface(surface);

return (jlong)lockedSurface.get();

}

typedef struct ANativeWindow_Buffer {

   // The number of pixels that are show horizontally.

   int32_t width;

   // The number of pixels that are shown vertically.

   int32_t height;

   // The number of *pixels* that a line in the buffer takes in

   // memory.  This may be >=width.

   int32_t stride;

   // The format of the buffer.  Oneof WINDOW_FORMAT_*

   int32_t format;

   // The actual bits.

   void* bits;

   // Do not touch.

   uint32_t reserved[6];

} ANativeWindow_Buffer;

ANativeWindow_Buffer中的属性void*bits就是canvas中作画,存储数据的地方。

Surface的职责只是管理SurfaceFlinger分配的用于存储UI数据的内存块,它是一个中介,通过与SurfaceFlinger的协作满足上层的需求。

status_t Surface::lock(){

// 当前是否有buffer被locked,因为被Locked的buffer只能有一个,以mLockedBuffer表示

         if(mLockedBuffer != 0)

//      执行连接到CPU

         interr = Surface::connect(NATIVE_WINDOW_API_CPU);

//设置内存块的用法,

         setUsage(GRALLOC_USAGE_SW_READ_OFTEN| GRALLOC_USAGE_SW_WRITE_OFTEN);

//dequeue一个可用的buffer,是通过GraphicBufferProducer来向BufferQueue获取一个buffer,这里有frontBuffer,backBuffer分别代表上一次处理的buffer和当前正在处理的buffer,两次图像更新间通常不需要重绘整个区域,可以借助之前的buffer填充本次的buffer内容,判断是否可以从上一次的buffer中copy数据的依据就是buffer的宽、高、格式是否一致。不能copy就要重绘整个界面。

         status_terr = dequeueBuffer(&out, &fenceFd);

//锁定buffer

         status_tres = backBuffer->lockAsync()

}

status_t Surface::unlockAndPost(){

//UI绘制完成,需要解锁,Buffer入队,提交给SurfaceFlinger渲染,SurfaceFlinger后续会这一buffer进行处理,最终显示到屏幕上

         status_terr = mLockedBuffer->unlockAsync(&fd);

         err= queueBuffer(mLockedBuffer.get(), fd);

}

分析了native层canvas的实现机制,回到java层的performDraw

drawSoftware()@ViewRootImpl.javaà mView.draw(canvas);//同样从viewtree根元素开始

public void draw(){

//从View树的根元素开始,逐步往下绘制

         mView.draw(canvas);

}

public void draw(Canvas canvas) {

//绘制背景,要考虑是否进行坐标变换

         drawBackground(canvas);

//绘制内容,调用具体子类的onDraw方法

         if(!dirtyOpaque) onDraw(canvas);

//递归绘制子对象,调用ViewGroup子类的实现

         dispatchDraw(canvas);

//画scrollbar

         onDrawScrollIndicators(canvas);

         onDrawScrollBars(canvas);

}

onDraw就是具体的绘制,以ImageView为例

[email protected]{

//把drawable中内容绘制到这个canvas上

         mDrawable.draw(canvas);

}

上面应用进程写完内容后,会解锁这个buffer并queueBuffer,把数据提交:

[email protected]>[email protected]

在把这个buffer入队,即queuebuffer时,[email protected]这个方法中,会通过sp<IConsumerListener>frameAvailableListener这个监听,把当前有可消费的buffer的这个事件通过onFrameAvailable发出去,这样buffer的消费者就会取这个buffer。

继续阅读