本文正在参加「金石方案 . 分割6万现金大奖」

Hi,我是小余。本文已收录到GitHub Androider-Planet中。这儿有 Android 进阶生长常识体系,重视公众号 [小余的自习室] ,在成功的路上不迷路!

浅谈

前几天有个朋友问我“了不了解关于手机硬件加快方面的常识?”,嗯?其实我也想知道。。。

“一文读懂”系列:Android中的硬件加速

所以笔者就去网上搜罗了文章再结合自己对源码的了解,总结了这篇关于硬件加快的了解。 关于屏幕制作前面文章《”一文读懂”系列:Android屏幕改写机制》现已做了一个大局的介绍,本篇来解说下屏幕制作中的硬件加快。

手机开发中最重要的两个点:

  • 1.用户点击的流通性
  • 2.界面效果的展现

早期的Android体系这两个作业都是在主线程上履行,导致用户点击的时分,界面制作阻滞或者界面制作的时分,用户点击半响不响应,体验性很差。

所以在4.0以后,以 “run fast, smooth, and responsively” 为中心目标对 UI 进行了优化,运用敞开了硬件加快对UI进行制作

1.硬件加快

在之前文章中咱们剖析过,Android 屏幕的制作流程分为两部分:

  • 1.生产者:app侧将View烘托到一个buffer中,供SurfaceFlinger消费
  • 2.消费者:SurfaceFlinger测将多个buffer兼并后放入buffer中,供屏幕显现

“一文读懂”系列:Android中的硬件加速

其中 第二步一向都是在GPU中实现的,而咱们所说的硬件加快便是榜首步中的view烘托流程

早期view的烘托是在主线程中进行的,而硬件加快则运用一个新的线程RenderThread以及硬件GPU进行烘托,

2.CPU / GPU结构比照

  • CPU (Central Processing Unit): 中央处理器,核算机设备中心器件,适用于一些杂乱的核算。
  • GPU (Graphic Processing Unit): 图形处理器,一般所说“显卡”的中心部件便是GPU,首要用于处理图形运算。

CPU和GPU结构比照:

“一文读懂”系列:Android中的硬件加速

  • 黄色代表操控器(Control):用于和谐操控整个CPU的运转,包括取指令等操作。
  • 绿色的ALU(Arithmetic Logic Unit):算数逻辑单元,首要用于进行数学,逻辑核算。
  • 橙色的Cache和DRAM分别为缓存和RAM,用于存储信息。

1.结构上看:CPU的ALU较少,而了解过OpenGl的同学应该知道View的烘托进程中是有大量的浮点数核算的,而浮点数转换为整数核算,或许会消耗大量的ALU单元,这对于CPU是比较难承受的。

2.CPU是串行的,一个CPU同一时刻只能做一件作业,(多线程其实也是将CPU时刻片分割而已),而GPU内部运用的是几千个小的GPU内核,每个GPU内核处理单元都是并行的, 这就非常契合图形的烘托进程。

GPU是显卡的中心部分,在破解密码方面也非常出色,再知道为啥哪些挖矿的运用的是显卡而不是CPU了吧,一个道理。

硬件加快底层原理

经过将核算机不拿手的图形核算指令运用特殊的api转换为GPU的专用指令,由GPU完结。这儿或许是传统的OpenGL或其他开放言语。

3.OpenGL

Android端一般运用OpenGL ES来实现硬件加快。 这儿简略介绍下OpenGL和OpenGL ES。

  • OpenGL(Open Graphics Library):开放式图形库,是用于烘托2D、3D矢量图形的跨言语、跨平台的运用程序编程接口(API)。这个接口由近350个不同的函数调用组成, 用来制作从简略的图形比特到杂乱的三维景象。
  • OpenGL ES(OpenGL for Embedded Systems):是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计

假如一个设备支撑GPU硬件加快烘托(有或许不支撑,看GPU厂商是不是适配了OpenGL 等接口), 那么当Android运用程序调用Open GL接口来制作UI时,Android运用程序的 UI 便是经过GPU进行烘托的。

4.Android图形体系全体架构

在介绍Android图画体系架构前,咱们先来了解几个概念:假如把UI的制作进程当成一幅画的制作进程: 那么:

  • 1.画笔

    • Skia:CPU用来制作2D图形
    • Open GL /ES:GPU制作2D和3D图形。
  • 2.画纸Surface:一切的制作和烘托都是在这张画纸上进行,每个窗口都是一个DecorView的容器,一起每个窗口都相关一个Surface

  • 3.画板Graphic Buffer :Graphic Buffer是谷歌在4.1以后针对双缓冲的jank问题提出的第三个缓冲,CPU/GPU烘托的内容都将写到这个buffer上。

  • 4.组成 SurfaceFlinger:将一切的Surface兼并叠加后显现到一个buffer里边。

简略了解进程:咱们运用画笔(Skia、Open GL ES)将内容画到画纸(Surface)中,这个进程或许运用OpenGl ES也或许运用Skia, 运用OpenGl ES标明运用了硬件加快制作,运用Skia,标明运用的是纯软件制作。

下面是Android 图形体系的全体架构:

“一文读懂”系列:Android中的硬件加速

  • Image Stream Producers:图画数据流生产者,图画或视频数据终究制作到Surface中。

  • WindowManager :前面一篇文章《WindowManager体系(上)》笔者说过,每个Surface都有一个Window和他一一对应,而WindowManager则用来管理窗口的各个方面: 动画,方位,旋转,层序,生命周期等。

  • SurfaceFlinger:用来对烘托后的Surface进行兼并,并传递给硬件笼统层处理。

  • HWC : Hardware Composer,SurfaceFlinger 会委派一些组成的作业给 Hardware Composer 以此减轻 GPU 的负载。这样会比单纯经过 GPU 来组成消耗更少的电量。

  • Gralloc(Graphics memory allocator):前面解说的Graphic Buffer分配的内存。

5.软硬件制作进程源码解析

前面解说了那么多理论常识,下面从源码视点来剖析下硬件加快和软件制作进程。 “read the fking source”

在前面文章《》中剖析过。View终究是在ViewRootImpl的performDraw办法最新烘托的, 而performDraw内部调用的是draw办法。 定位到draw办法:

private void draw(boolean fullRedrawNeeded) {
    ...
    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
        if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {//1
            ...
            mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);//2
    }else {
            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {//3
                    return;
            }
        }
    }
}

注释1:假如mThreadedRenderer不为null且isEnabled为true,则调用注释2处的mThreadedRenderer.draw,这个便是硬件制作的进口

假如其他情况,则调用注释3处的drawSoftware,这儿便是软件制作的进口,再正式对软硬件制作进行深化之前咱们看下mAttachInfo.mThreadedRenderer是在哪里赋值的?

源码大局搜索下:咱们发现ViewRootImpl的enableHardwareAcceleration办法中有创立mThreadedRenderer的操作。

private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
            // Try to enable hardware acceleration if requested
    ...
    final boolean hardwareAccelerated =
                    (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
    //这儿假如attrs.flags设置了WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,则标明该Window支撑硬件加快制作
    if (hardwareAccelerated) {
            // Persistent processes (including the system) should not do
            // accelerated rendering on low-end devices.  In that case,
            // sRendererDisabled will be set.  In addition, the system process
            // itself should never do accelerated rendering.  In that case, both
            // sRendererDisabled and sSystemRendererDisabled are set.  When
            // sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
            // can be used by code on the system process to escape that and enable
            // HW accelerated drawing.  (This is basically for the lock screen.)
            //Persistent的运用进程以及体系进程不能运用硬件加快
            final boolean fakeHwAccelerated = (attrs.privateFlags &
                            WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;
            final boolean forceHwAccelerated = (attrs.privateFlags &
                            WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;
            if (fakeHwAccelerated) {
                    mAttachInfo.mHardwareAccelerationRequested = true;
            } else if (!ThreadedRenderer.sRendererDisabled
                            || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
                    if (mAttachInfo.mThreadedRenderer != null) {
                            mAttachInfo.mThreadedRenderer.destroy();
                    }
                    ...
                    //这儿创立了mAttachInfo.mThreadedRenderer
                    mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
                                    attrs.getTitle().toString());
                    if (mAttachInfo.mThreadedRenderer != null) {
                            mAttachInfo.mHardwareAccelerated =
                                            mAttachInfo.mHardwareAccelerationRequested = true;
                    }
            }
    }
}

这儿源码告诉咱们:

  • 1.硬件加快是经过attrs.flags 设置WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED标识来发动的、
  • 2、由于硬件加快是一个耗内存的操作,仅仅硬件加快烘托环境初始化这一操作,就要花掉8M的内存, 所以一般永久性的进程或者体系进程不要运用硬件加快标志,防止出现内存走漏

再看哪里调用enableHardwareAcceleration办法? 经过源码查找咱们注意到ViewRootImpl的setView办法中:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
	//注释1
	if (view instanceof RootViewSurfaceTaker) {
		mSurfaceHolderCallback =
				((RootViewSurfaceTaker)view).willYouTakeTheSurface();
		if (mSurfaceHolderCallback != null) {
			mSurfaceHolder = new TakenSurfaceHolder();
			mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
			mSurfaceHolder.addCallback(mSurfaceHolderCallback);
		}
	}
	...
	// If the application owns the surface, don't enable hardware acceleration
	if (mSurfaceHolder == null) {//注释2
		enableHardwareAcceleration(attrs);
	}
}

注释1处:标明当时view实现了RootViewSurfaceTaker接口,且view的willYouTakeTheSurface返回的mSurfaceHolderCallback不为null, 则标明运用想自己接收一切的烘托操作,这样创立出来的Activity窗口就类似于一个SurfaceView相同,完全由运用程序自己来操控它的烘托

基本上咱们是不会将一个Activity窗口当作一个SurfaceView来运用的, 因此在ViewRootImpl类的成员变量mSurfaceHolder将保持为null值, 这样就会导致ViewRootImpl类的成员函数enableHardwareAcceleration被调用为判别是否需求为当时创立的Activity窗口启用硬件加快烘托。

好了咱们回到ViewRootImpl的draw办法:

1.先来看软件制作

软件制作调用的是drawSoftware办法。 进入

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {
	...
	canvas = mSurface.lockCanvas(dirty);//1
	mView.draw(canvas);//2
	surface.unlockCanvasAndPost(canvas);//3
}

软件制作基本就分三步走:

  • 进程1:lockCanvas:每个Window都相关了一个Surface,当有需求制作UI时,就调用lockCanvas获取一个Canvas目标,这个Canvas封装了Skia提供的2D图形制作api、

而且向SurfaceFlinger Dequeue了一块Graphic buffer,制作的内容都会输出到这个buffer中,供SurfaceFlinger组成运用。

  • 进程2:draw:调用了View的draw办法,这个就会调用到咱们自定义组件中View的onDraw办法,传入1中创立的Canvas目标,运用Skia api对图画进行制作。

  • 进程3:unlockCanvasAndPost:制作完结后,告诉SurfaceFlinger制作完结,能够进行buffer的交换,显现到屏幕上了,实质是给SurfaceFlinger queue 一个Graphic buffer、 关于什么是Queue和Dequeue看下图:

“一文读懂”系列:Android中的硬件加速

软件制作条形简图:

“一文读懂”系列:Android中的硬件加速

2.硬件加快剖析:

硬件加快分为两个进程:

  • 1.构建阶段
  • 2.制作阶段

构建阶段:

这个阶段用于遍历一切的视图,将需求制作的Canvas API调用及其参数记录下来,保存在一个Display List,这个阶段发生在CPU主线程上

Display List实质上是一个缓存区,它里边记录了即将要履行的制作指令序列,这些指令终究会在制作阶段被OpenGL转换为GPU烘托指令。

视图构建阶段会将每个View笼统为一个RenderNode,每个View的制作操作笼统为一系列的DrawOp, 比方: View的drawLine操作会被笼统为一个DrawLineOp,drawBitmap操作会被笼统成DrawBitmapOp,每个子View的制作被笼统成DrawRenderNodeOp,每个DrawOp都有对应的OpenGL制作指令,一起内部也握有需求制作的数据元

“一文读懂”系列:Android中的硬件加速

运用Display List的好处:

  • 1、在制作窗口的下一帧时,假如某个视图UI没有发生变化,则不需求履行与他相关的Canvas API操作,即不用重复履行View的onDraw操作, 而是直接运用上一帧的Display List即可
  • 2.假如制作窗口下一帧时,视图发生了变化,可是仅仅一些简略特点变化,如方位和透明度等,则只需求修正上次构建的Display List的相关特点即可,也不用重复构建

Display List模型图:

“一文读懂”系列:Android中的硬件加速
接下来咱们从源码视点来看下:

前面咱们剖析了硬件加快进口是在ThreadedRenderer的draw办法:

mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this)

进入这个办法看看: ThreadedRenderer.java

void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
	...
	updateRootDisplayList(view, callbacks);//1
	...
	int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);//2告诉RenderThread线程制作
}

ThreadedRenderer首要效果便是在主线程CPU中视图的构建,然后告诉RenderThread运用OpenGL进行视图的烘托(注释2处)。

注释1处:updateRootDisplayList看名称应该便是用于视图构建,进去看看

private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
	//1.构建参数view(DecorView)视图的Display List
	updateViewTreeDisplayList(view);
	//2
	//mRootNodeNeedsUpdate true标明需求更新视图
	//mRootNode.isValid()  标明现已构建了Display List
	if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
		//获取DisplayListCanvas
		DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);//3
		try {
			//ReorderBarrie标明会按照Z轴坐标值从头排列子View的烘托顺序
			canvas.insertReorderBarrier();
			//构建并缓存一切的DrawOp
			canvas.drawRenderNode(view.updateDisplayListIfDirty());
			canvas.insertInorderBarrier();
			canvas.restoreToCount(saveCount);
		} finally {
			//将一切的DrawOp填充到根RootNode中,作为新的Display List
			mRootNode.end(canvas);
		}
	}
}

注释1:updateViewTreeDisplayList对View树Display List进行构建

private void updateViewTreeDisplayList(View view) {
	view.mPrivateFlags |= View.PFLAG_DRAWN;
	view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
			== View.PFLAG_INVALIDATED;
	view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
	view.updateDisplayListIfDirty();
	view.mRecreateDisplayList = false;
}

看View的updateDisplayListIfDirty办法。

/**
 * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported)
 * @hide
 */
@NonNull
public RenderNode updateDisplayListIfDirty() {
	//获取当时mRenderNode
	final RenderNode renderNode = mRenderNode;
	//2.判别是否需求进行从头构建
	if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
                || !renderNode.isValid()
                || (mRecreateDisplayList)) {
		if (renderNode.isValid()
                    && !mRecreateDisplayList) {
			mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
			mPrivateFlags &= ~PFLAG_DIRTY_MASK;
			//这儿用于当时View是ViewGroup,且本身不需求重构,对其子View的DisplayList进行构建
			dispatchGetDisplayList();
			return renderNode; // no work needed
        }
		...
		final DisplayListCanvas canvas = renderNode.start(width, height);
		try {
			if (layerType == LAYER_TYPE_SOFTWARE) {
				//软件制作
				buildDrawingCache(true);
				Bitmap cache = getDrawingCache(true);
				if (cache != null) {
					canvas.drawBitmap(cache, 0, 0, mLayerPaint);
				}
			} else {
				...
				if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
					//View是ViewGroup,需求制作子View
					dispatchDraw(canvas);
					...
				} else {
					draw(canvas);
				}
			}
		} finally {
			//将制作好后的数据填充到renderNode中去
			renderNode.end(canvas);
			setDisplayListProperties(renderNode);
		}
	}
}

updateDisplayListIfDirty首要效果

  • 1.获取当时View的RenderNode。
  • 2.假如需求或者支撑则更新当时DisplayList

判别是否需求进行从头构建的条件如下:

  • 1.mPrivateFlags 设置了 PFLAG_DRAWING_CACHE_VALID,标明当时缓存现已失效,需求从头构建
  • 2.!renderNode.isValid():标明当时Display List的数据不合法,需求从头构建
  • 3.mRecreateDisplayList的值等于true,一些其他原因需求从头构建

mRenderNode在View的结构办法中初始化:

public View(Context context) {
	...
	mRenderNode = RenderNode.create(getClass().getName(), this);
}

构建进程如下:

  • 1.运用renderNode.start获得一个与当时View相关的DisplayListCanvas。
  • 2.运用draw(canvas),将当时View以及子View制作到当时DisplayListCanvas
  • 3.运用renderNode.end(canvas),将现已制作在 DisplayListCanvas 的 Display List Data 填充到当时 View 相关的 Render Node 中

经过上面几个进程就将View树对应的DisplayList构建好了。而且这个构建进程会递归构建子View的Display List

咱们从制作流程火焰图中也能够看到大概流程:

“一文读懂”系列:Android中的硬件加速

赤色框中部分:是制作的DecorView的时分,一向递归updateDisplayListIfDirty办法进行Display List的构建 其他色彩框部分是子View Display List的构建

制作阶段

这个阶段会调用OpenGL接口将构建好视图进行制作烘托,将烘托好的内容保存到Graphic buffer中,并提交给SurfaceFlinger。

回到ThreadedRenderer的draw办法: ThreadedRenderer.java

void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
    ...
    updateRootDisplayList(view, callbacks);//1
    ...
    int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);//2
}

注释1中创立好视图对应的Display List后,在注释2处调用nSyncAndDrawFrame办法告诉RenderThread线程进行制作

nSyncAndDrawFrame是一个native办法,在解说nSyncAndDrawFrame办法前咱们先来看ThreadedRenderer结构函数中做了哪些事。

ThreadedRenderer(Context context, boolean translucent, String name) {
	//这个办法在native层创立RootRenderNode目标并返回目标的地址
	long rootNodePtr = nCreateRootRenderNode();
	mRootNode = RenderNode.adopt(rootNodePtr);
	mRootNode.setClipToBounds(false);
	//这个办法在native层创立一个RenderProxy
	mNativeProxy = nCreateProxy(translucent, rootNodePtr);
}

nCreateRootRenderNode和nCreateProxy办法在android_view_ThreadedRenderer.cpp中实现:

static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
    RootRenderNode* node = new RootRenderNode(env);
    node->incStrong(0);
    node->setName("RootRenderNode");
    return reinterpret_cast<jlong>(node);
}
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
        jboolean translucent, jlong rootRenderNodePtr) {
    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
    ContextFactoryImpl factory(rootRenderNode);
    return (jlong) new RenderProxy(translucent, rootRenderNode, &factory);
}
RenderProxy结构办法:
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance())//1
        , mContext(nullptr) {
    ...
}

注意到mRenderThread运用的是RenderThread::getInstance()单例线程,也就说整个制作进程只有一个RenderThread线程。

接着看RenderThread::getInstance()创立线程的办法:

RenderThread::RenderThread() : Thread(true)
	...
	Properties::load();
    mFrameCallbackTask = new DispatchFrameCallbacks(this);
    mLooper = new Looper(false);
    run("RenderThread");
}

居然也是运用的Looper,是不是和咱们的主线程的音讯机制相同呢?哈哈 调用run办法会履行RenderThread的threadLoop办法。

bool RenderThread::threadLoop() {
	...
    int timeoutMillis = -1;
    for (;;) {
        int result = mLooper->pollOnce(timeoutMillis);
		...
        nsecs_t nextWakeup;
        {
            ...
            while (RenderTask* task = nextTask(&nextWakeup)) {
                workQueue.push_back(task);
            }
            for (auto task : workQueue) {
                task->run();
                // task may have deleted itself, do not reference it again
            }
        }
        if (nextWakeup == LLONG_MAX) {
            timeoutMillis = -1;
        } else {
            nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos);
            if (timeoutMillis < 0) {
                timeoutMillis = 0;
            }
        }
        if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
			...
            requestVsync();
        }
        if (!mFrameCallbackTaskPending && !mVsyncRequested && mFrameCallbacks.size()) {
            ...
            requestVsync();
        }
    }
    return false;
}

石锤了便是运用程序主线程的音讯机制模型

    1. 空闲的时分,Render Thread就睡觉在成员变量mLooper指向的一个Looper目标的成员函数pollOnce中。
    1. 当其它线程需求调度Render Thread,就会向它的使命行列添加一个使命,然后唤醒Render Thread进行处理。Render Thread经过成员函数nextTask获得需求处理的使命,而且调用它的成员函数run进行处理。

这儿做个小结: ThreadedRenderer结构办法中

  • 1.初始化mRootNode指向native层的一个RootRenderNode
  • 2.初始化mNativeProxy指向native层的RenderProxy
  • 3.在native层创立RenderProxy时,一起也会创立RenderThread线程,这个线程机制和咱们主线程音讯机制一向,轮询等候获取制作使命。

好了回头看nSyncAndDrawFrame的native办法

nSyncAndDrawFrame同样也在android_view_ThreadedRenderer.cpp中实现:

static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
        jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
    LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
            "Mismatched size expectations, given %d expected %d",
            frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
    env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
    return proxy->syncAndDrawFrame();
}

这个办法返回值是proxy->syncAndDrawFrame(),进入RenderProxy的syncAndDrawFrame办法:

int RenderProxy::syncAndDrawFrame() {
    return mDrawFrameTask.drawFrame();
}

这儿的 mDrawFrameTask.drawFrame其实便是向RenderThread的TaskQueue添加一个drawFrame烘托使命,告诉RenderThread烘托UI视图

如下图:

“一文读懂”系列:Android中的硬件加速

mDrawFrameTask是DrawFrameTask中的函数

int DrawFrameTask::drawFrame() {
	...
    postAndWait();
    return mSyncResult;
}
void DrawFrameTask::postAndWait() {
    AutoMutex _lock(mLock);
    mRenderThread->queue(this);
    mSignal.wait(mLock);//锁住等候锁开释
}
void RenderThread::queue(RenderTask* task) {
    AutoMutex _lock(mLock);
    mQueue.queue(task);
    if (mNextWakeup && task->mRunAt < mNextWakeup) {
        mNextWakeup = 0;
        mLooper->wake();
    }
}

看到这就知道了drawFrame其实便是往RenderThread线程的使命行列mQueue中按时刻顺序加入一个制作task,并调用mLooper->wake()唤醒RenderThread线程处理

说到底还是主线程音讯机制那套东西。

注意DrawFrameTask在postAndWait的mRenderThread->queue(this)中是将this传入使命行列,所以此使命便是this自己。后面履行制作使命就运用到了OpenGL对构建好的DisplayList进行烘托。

经过上面的剖析,整个硬件制作流程就有个明晰模型了

“一文读懂”系列:Android中的硬件加速

点到为止,后面代码大家能够自行找到源码阅读。

制作阶段这块或许比较杂乱些,由于基本上都是native层的东西,有的消化下。

硬件加快和纯软件制作比照

烘托场景 纯软件制作 硬件加快 加快效果剖析
页面初始化 制作一切View 创立一切DisplayList GPU分管了杂乱核算使命
在一个杂乱页面调用布景透明TextView的setText(),且调用后其尺度方位不变 重绘脏区一切View TextView及每一级父View重建DisplayList 堆叠的兄弟节点不需CPU重绘,GPU会自行处理
TextView逐帧播映Alpha / Translation / Scale动画 每帧都要重绘脏区一切View 除榜首帧同场景2,之后每帧只更新TextView对应RenderNode的特点 改写一帧功能极大进步,动画流通度进步
修正TextView透明度 重绘脏区一切View 直接调用RenderNode.setAlpha()更新 只触发DecorView.updateDisplayListIfDirty,不再往下遍历,CPU履行时刻可忽略不计

出现方法剖析东西

Android 4.1(API 等级 16)或更高版别的设备上,

履行以下进程敞开东西:

  • 1.发动开发者选项;
  • 2.在“监控”部分,找到“GPU出现方法剖析”(不同厂商命名有所区别);
  • 3.点击“GPU出现方法剖析”,弹出页面中,选择“在屏幕上显现为条形图”即可。

这时,GPU 出现方法东西现已敞开了,接下来,咱们能够翻开咱们要测验的APP来进行观察测验了。

视觉出现

GPU 烘托方法剖析东西以图表(以色彩编码的直方图)的方法显现各个阶段及其相对时刻。

Android 10 上显现的五颜六色部分:

“一文读懂”系列:Android中的硬件加速

注意点:

  • 1.一个运用对应一个图形
  • 2.沿水平轴的每个竖条代表一个帧,每个竖条的高度标明烘托该帧所花的时刻(以毫秒为单位)。
  • 3.中间绿色的线是16.6ms的分割线,高于绿色线标明出现了掉帧
  • 4.经过加宽竖条降低透明度来反响比较耗时的帧
  • 5.每个竖条都有与烘托管道中某个阶段对应的五颜六色区段。区段数因设备的 API 等级不同而异。

色彩块含义

Android 6.0 及更高版别的设备时剖析器输出中某个竖条的每个区段含义:

“一文读懂”系列:Android中的硬件加速
4.0(API 等级 14)和 5.0(API 等级 21)之间的 Android 版别具有蓝色、紫色、赤色和橙色区段。低于 4.0 的 Android 版别只有蓝色、赤色和橙色区段。下表显现的是 Android 4.0 和 5.0 中的竖条区段。

“一文读懂”系列:Android中的硬件加速

GPU 出现方法东西,很直观的为咱们展现了 APP 运转时每一帧的耗时概况。咱们只需求重视代表每一帧的柱状图的色彩概况,就能够剖析出卡顿的原因了。

好了,说了那么多优点,咱来说下他的几个缺陷

  • 1.稳定性,敞开硬件加快后,有小概率出现画面崩溃,所以在一些视频播映器会给个开关让用户手动开关。
  • 2.功耗:GPU的功耗远远大于CPU。
  • 3.内存消耗:运用OpenGL接口初始化就需求8M效果的内存。
  • 4.兼容性:不兼容某些接口和api。

总结

默认情况下Skia的制作没有采用GPU烘托的方法(尽管Skia也能用GPU烘托),也就说默认drawSoftware作业完全由CPU来完结,不会牵扯到GPU的操作,可是8.0之后,Google逐渐加重了Skia,开始让Skia接手OpenGL,直接统一调用,将来还或许是Skia同Vulkan的结合。这也是手机端硬件功能越来越好的成果吧。

假如本篇文章对你有帮助,请帮助点个赞重视下谢谢,笔者会定时推送一些关于Android移动开发中的高质量文章笔者公众号:小余的自习室

参考

Android 屏幕制作机制及硬件加快

Android运用程序UI硬件加快烘托环境初始化进程剖析

如何运用“GPU 出现方法”进行卡顿问题定位