当前位置: 首页 > news >正文

Android HWUI绘制流程

Android中绘图的API有很多,比如2D的绘图skia;3D的绘图OpenGLES,Vulkan等。Android在后来完善3D API支持的同时,也在更新View Widget渲染机制,提出了硬件加速机制。

HWUI绘制的大致流程是先初始化绘制环境(创建rendernode、渲染线程RenderThread、Context上下文、RenderProxy代理对象),之后是创建DisplayList显示列表,然后开始视图绘制,视图绘制结束后开始同步帧数据。

硬件加速作用:是将2D的绘图操纵转换为对应的3D绘图操纵。需要显示的时候,再用OpenGLES通过GPU渲染。过程:界面创建时,第一次全部渲染,后续界面如果只有部分区域的widget更新,只需要重新渲染更新的widget。渲染好的绘图保存在一个显示列表DisplayList中,需要真正显示到界面的时候,直接显示DisplayList中的绘图。好处:一方面利用GPU去渲染,比Skia要快;另一方面,采用DisplayList,再次渲染只更新部分区域,最大程度利用上一帧的数据,提高效率。

使用Android Q AOSP源码梳理流程。

1. GPU渲染(硬件加速)介绍

在Android应用程序中是通过Canvas API来绘制UI元素的。在硬件加速渲染环境中,这些Canvas API调用最终会转化为OpenGL API调用(转化过程对应用程序来说是透明的)。由于OpenGL API调用要求发生在Open GL环境中,因此在每当有新的Activity窗口启动时,系统都会为其初始化好OpenGL环境。

这里的渲染,主要是Android硬件加速,即GPU渲染。android上就是通过libhwui调用OpenGL api来渲染, Android P上libhwui 会调用skia,再调用GLES相关的API进行渲染。

GPU作为一个硬件 , 用户空间是不可以直接使用的, 它是由GPU厂商按照Open GL规范实现的驱动间接进行使用的。也就是说 , 如果一个设备支持GPU硬件加速渲染, 那么当Android应用程序调用OpenGL接口来绘制UI时 ,Android应用程序的UI就是通过硬件加速技术进行渲染的。

名词介绍:

  • GPU:一个类似于CPU的专门用来处理Graphics的处理器, 作用用来帮助加快栅格化操作, 当然, 也有相应的缓存数据(例如缓存已经光栅化过的bitmap等)机制。
  • OpenGL ES:是手持嵌入式设备的3DAPI, 跨平台的、功能完善的2D和3D图形应用程序接口API, 有一套固定渲染管线流程
  • DisplayList:在Android把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的。DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息。
  • 栅格化:是将图片等矢量资源, 转化为一格格像素点的像素图, 显示到屏幕上。
  • 垂直同步VSYNC:让显卡的运算和显示器刷新率一致以稳定输出的画面质量。它告知GPU在载入新帧之前,要等待屏幕绘制完成前一帧。
  • RefreshRate:屏幕一秒内刷新屏幕的次数, 由硬件决定, 例如60Hz
  • Frame Rate:GPU一秒绘制操作的帧数, 单位是fps

2. Android 5.0 之后的渲染框架

在Android应用程序窗口中, 每一个View都抽象为一个Render Node, 而且如果一个View设置有Background, 这个background 也被抽象为一个Render Node 。

这是由于在OpenGLRenderer库中, 并没有View的概念, 所有的一切可绘制的元素都抽象为一个Render Node。

每一个Render Node都关联有一个DisplayList Renderer, Display List是一个绘制命令缓冲区。当View的成员函数onDraw被调用时, 我们调用通过参数传递进来的Canvas的drawXXX成员函数绘制图形时, 我们实际上只是将对应的绘制命令以及参数保存在一个Display List中。接下来再通过DisplayList Renderer执行这个Display List的命令, 这个过程称为Display List Replay。

Android应用程序窗口的View是通过树形结构来组织的。这些View不管是通过硬件加速渲染还是软件渲染, 或者是一个特殊的TextureView,在它们的成员函数onDraw被调用期间, 它们都是将自己的UI绘制在ParentView的DisplayList中。

其中, 最顶层的Parent View是一个Root View, 它关联的RootNode称为Root Render Node。也就是说, 最终Root Render Node的DisplayList将会包含一个窗口的所有绘制命令。

在绘制窗口的下一帧时, RootRender Node的Display List都会通过一个OpenGL Renderer真正地通过Open GL命令绘制在一个Graphic Buffer中。

最后这个 Graphic Buffer 被交给 SurfaceFlinger 服务进行合成和显示。


3. Android原生硬件绘制案例

这个案例是用的SurfaceView.java的流程。这个流程和实际上从ViewRootImpl.java中通过performDraw的流程类似。可以互相借鉴参考。

Android原生的硬件绘制案例,在frameworks/base/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java

//HardwareCanvasSurfaceViewActivity.java
private static class RenderingThread extends Thread {private final SurfaceHolder mSurface;private volatile boolean mRunning = true;private int mWidth, mHeight;//应用拿到一个Surfacepublic RenderingThread(SurfaceHolder surface) {mSurface = surface;}void setSize(int width, int height) {mWidth = width;mHeight = height;}@Overridepublic void run() {float x = 0.0f;float y = 0.0f;float speedX = 5.0f;float speedY = 3.0f;Paint paint = new Paint();paint.setColor(0xff00ff00);while (mRunning && !Thread.interrupted()) {//先调用Surface的lockHardwareCanvas函数final Canvas canvas = mSurface.lockHardwareCanvas();try {//绘制canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);canvas.drawRect(x, y, x + 20.0f, y + 20.0f, paint);} finally {//绘制完成后mSurface.unlockCanvasAndPost(canvas);}if (x + 20.0f + speedX >= mWidth || x + speedX <= 0.0f) {speedX = -speedX;}if (y + 20.0f + speedY >= mHeight || y + speedY <= 0.0f) {speedY = -speedY;}x += speedX;y += speedY;try {//每个15s循环一次Thread.sleep(15);} catch (InterruptedException e) {// Interrupted}}}void stopRendering() {interrupt();mRunning = false;}}

3.1. Java层相关流程(frameworks/base的View模块和graphics模块)

  1. 首先调用关键函数lockHardwareCanvas,在frameworks/base/core/java/android/view/SurfaceView.java
//frameworks/base/core/java/android/view/SurfaceView.java@Overridepublic Surface getSurface() {return mSurface;}public Canvas lockHardwareCanvas() {return internalLockCanvas(null, true);}private Canvas internalLockCanvas(Rect dirty, boolean hardware) {mSurfaceLock.lock();Canvas c = null;if (!mDrawingStopped && mSurfaceControl != null) {try {if (hardware) {//hardware传递的是true,执行lockHardwareCanvasc = mSurface.lockHardwareCanvas();} else {c = mSurface.lockCanvas(dirty);}} catch (Exception e) {Log.e(LOG_TAG, "Exception locking surface", e);}}if (c != null) {mLastLockTime = SystemClock.uptimeMillis();return c;}......mLastLockTime = now;mSurfaceLock.unlock();return null;}

然后就调用Surface.java的lockHardwareCanvas函数,此处封装了一个HwuiContext对象,构造函数如下:

//frameworks/base/core/java/android/view/Surface.javapublic Canvas lockHardwareCanvas() {Log.d(TAG, "lockHardwareCanvas");synchronized (mLock) {checkNotReleasedLocked();if (mHwuiContext == null) {//Step 1 创建HwuiContext,调用构造函数mHwuiContext = new HwuiContext(false);}//Step 2 调用他的lockCanvas函数return mHwuiContext.lockCanvas(nativeGetWidth(mNativeObject),nativeGetHeight(mNativeObject));}}//从上面调用到Canvas lockCanvas(int width, int height) {if (mCanvas != null) {throw new IllegalStateException("Surface was already locked!");}//调用RenderNode的beginRecording函数mCanvas = mRenderNode.beginRecording(width, height);return mCanvas;}.....}

3.2. 创建RenderNode

RenderNode用以绘图操纵的批处理,当绘制的时候,可以store和apply。java层的代码如下:其实RenderNode就对应前面我们所说的ViewGroup,有一个RootView,同样也有一个RootNode。

  1. 在上面Surface.java调用HwuiContext构造函数的时候,会创建RenderNode对象:
//Surface.javaprivate final class HwuiContext {//创建RenderNode和HwuiRenderprivate final RenderNode mRenderNode;private long mHwuiRenderer;private RecordingCanvas mCanvas;private final boolean mIsWideColorGamut;//构造函数HwuiContext(boolean isWideColorGamut) {//创建一个RenderNodemRenderNode = RenderNode.create("HwuiCanvas", null);......

创建RenderNode对象:

//frameworks/base/graphics/java/android/graphics/RenderNode.javapublic static RenderNode create(String name, @Nullable AnimationHost animationHost) {return new RenderNode(name, animationHost);}private RenderNode(String name, AnimationHost animationHost) {mNativeRenderNode = nCreate(name);NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);mAnimationHost = animationHost;}
  1. JNI层:
//frameworks/base/core/jni/android_view_RenderNode.cpp
static const JNINativeMethod gMethods[] = {
// ----------------------------------------------------------------------------
// Regular JNI
// ----------------------------------------------------------------------------{ "nCreate",               "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },{ "nGetNativeFinalizer",   "()J",    (void*) android_view_RenderNode_getNativeFinalizer },{ "nOutput",               "(J)V",    (void*) android_view_RenderNode_output },...static jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) {//创建一个native层的rendernode对象RenderNode* renderNode = new RenderNode();renderNode->incStrong(0);if (name != NULL) {const char* textArray = env->GetStringUTFChars(name, NULL);renderNode->setName(textArray);env->ReleaseStringUTFChars(name, textArray);}return reinterpret_cast<jlong>(renderNode);
}
  1. Native层,创建好RenderNode是提供给DisplayListCanvas。
//frameworks/base/libs/hwui/RenderNode.cpp
RenderNode::RenderNode(): mUniqueId(generateId()), mDirtyPropertyFields(0), mNeedsDisplayListSync(false), mDisplayList(nullptr), mStagingDisplayList(nullptr), mAnimatorManager(*this), mParentCount(0) {}

3.3. beginRecording初始化DisplayList

初始化DisplayList

  1. 在Surface.java中通过lockCanvas调用RenderNode对象的beginRecording函数。
//frameworks/base/graphics/java/android/graphics/RenderNode.javapublic @NonNull RecordingCanvas beginRecording(int width, int height) {if (mCurrentRecordingCanvas != null) {throw new IllegalStateException("Recording currently in progress - missing #endRecording() call?");}mCurrentRecordingCanvas = RecordingCanvas.obtain(this, width, height);return mCurrentRecordingCanvas;}

接着调用RecordingCanvas的obtain函数:

类的继承关系: RecordingCanvas类继承DisplayListCanvas,而DisplayListCanvas继承BaseRecordingCanvas, BaseRecordingCanvas继承Canvas(继承BaseCanvas)。

//frameworks/base/graphics/java/android/graphics/RecordingCanvas.java
public final class RecordingCanvas extends DisplayListCanvas {//构造函数/** @hide */protected RecordingCanvas(@NonNull RenderNode node, int width, int height) {super(nCreateDisplayListCanvas(node.mNativeRenderNode, width, height));mDensity = 0; // disable bitmap density scaling}static RecordingCanvas obtain(@NonNull RenderNode node, int width, int height) {if (node == null) throw new IllegalArgumentException("node cannot be null");RecordingCanvas canvas = sPool.acquire();if (canvas == null) {canvas = new RecordingCanvas(node, width, height);} else {//创建一个native的DisplayListCanvas对象(即显示列表的Canvas)//JNI通过mNativeCanvasWrapper(BaseCanvas.java创建)找对应的Native的CanvasnResetDisplayListCanvas(canvas.mNativeCanvasWrapper, node.mNativeRenderNode,width, height);}canvas.mNode = node;canvas.mWidth = width;canvas.mHeight = height;return canvas;}...
}
  1. 查看测试案例代码中的canvas.drawColorcanvas.drawRect函数,是调用了其父类BaseCanvas的对应方法。
//frameworks/base/graphics/java/android/graphics/Canvas.javapublic void drawColor(@ColorLong long color) {super.drawColor(color, BlendMode.SRC_OVER);}public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {super.drawRect(rect, paint);}

父类BaseCanvas.java

//frameworks/base/graphics/java/android/graphics/BaseCanvas.javapublic void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);}public void drawRect(@NonNull Rect r, @NonNull Paint paint) {throwIfHasHwBitmapInSwMode(paint);drawRect(r.left, r.top, r.right, r.bottom, paint);
http://www.dtcms.com/a/607026.html

相关文章:

  • 企业首次建设网站的策划流程最热门的短期培训课程
  • 地方做什么网站手机网站 代码
  • libfvad 库详解:原理、使用方法与实践案例
  • Effective STL 第9条:C++容器元素删除技巧详解
  • wordpress关闭评论窗口seo标题优化关键词怎么选
  • 推广网站做网站图结构
  • 4G LTE多天线技术
  • Python社区文化:如何参与开源并提交你的第一个PR?
  • 北京市网站建设公司排名潍坊百度关键词优化
  • 网站建设用语言wordpress pageadmin
  • Entity API vs Primitive API 详细对比
  • 织梦cms 网站计数广东个人网站备案
  • 做公司年报网站登录密码是什么创立外包网站
  • 线程本地(ThreadLocal)的缓存容器
  • 可以做项目的网站网站开发工作 岗位怎么称呼
  • 【OpenCV + VS】调用摄像头与视频文件处理
  • 云手机的网络架构
  • 品牌网站建设四川微信开发公众号
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段-二阶段(18):文法和单词-第四课
  • wordpress是mvc架构吗google seo网站 被k
  • 【数据分享】2000-2022年我国省市县三级的逐年牛、山羊、绵羊和马的牲畜数量数据(Shp/Excel格式)
  • 利用模板建网站工信部信息备案网站
  • 阿里云建设网站步骤网络公司网站首页图片
  • 徐州做网站企业WordPress右下角提醒
  • 凡科建站做的网站收录慢吗娱乐视频直播网站建设
  • 【目标检测】热力图可视化脚本
  • 怎样用dede搭建网站网页传奇怎么删除
  • 做网站直播平台制作一个论坛网站多少钱
  • 电力设备机械结构声发射特征提取与深度学习驱动的裂纹损伤预测
  • 力扣面试150题打卡第五天