在 Android 应用程序中,我们是通过 Canvas API 来绘制 UI 元素的。在硬件加速渲染环境中,这些 Canvas API 调用最终会转化为 Open GL API 调用(转化过程对应用程序来说是透明的)。由于 Open GL API 调用要求发生在 Open GL 环境中,因此在每当有新的 Activity 窗口启动时,系统都会为其初始化好 Open GL 环境。这篇文章就详细分析这个 Open GL 环境的初始化过程。
老罗的新浪微博:,欢迎关注!
Open GL 环境也称为 Open GL 渲染上下文。一个 Open GL 渲染上下文只能与一个线程关联。在一个 Open GL 渲染上下文创建的 Open GL 对象一般来说只能在关联的 Open GL 线程中操作。这样就可以避免发生多线程并发访问发生的冲突问题。这与大多数的 UI 架构限制 UI 操作只能发生在 UI 线程的原理是差不多的。
在 Android 5.0 之前,Android 应用程序的主线程同时也是一个 Open GL 线程。但是从 Android 5.0 之后,Android 应用程序的 Open GL 线程就独立出来了,称为 Render Thread,如图 1 所示:
图 1 Android 应用程序 Main Thread 和 Render Thread
Render Thread 有一个 Task Queue,Main Thread 通过一个代理对象 Render Proxy 向这个 Task Queue 发送一个 drawFrame 命令,从而驱使 Render Thread 执行一次渲染操作。因此,Android 应用程序 UI 硬件加速渲染环境的初始化过程任务之一就是要创建一个 Render Thread。
一个 Android 应用程序可能存在多个 Activity 组件。在 Android 系统中,每一个 Activity 组件都是一个独立渲染的窗口。由于一个 Android 应用程序只有一个 Render Thread,因此当 Main Thread 向 Render Thread 发出渲染命令时,Render Thread 要知道当前要渲染的窗口是什么。从这个角度看,Android 应用程序 UI 硬件加速渲染环境的初始化过程任务之二就是要告诉 Render Thread 当前要渲染的窗口是什么。
一旦 Render Thread 知道了当前要渲染的窗口,它就将可以将该窗口绑定到 Open GL 渲染上下文中去,从而使得后面的渲染操作都是针对被绑定的窗口的,如图 2 所示:
图 2 绑定窗口到 Open GL 渲染上下文中
Java 层的 Activity 窗口到了 Open GL 这一层,被抽象为一个 ANativeWindow。将它绑定到 Open GL 渲染上下文之后,就可以通过 eglSwapBuffer 函数向 SurfaceFlinger 服务 Dequeue 和 Queue Graphic Buffer。其中,Dequeue Graphic Buffer 是为了在上面进行绘制 UI,而 Queue Graphic Buffer 是为了将绘制好的 UI 交给 Surface Flinger 合成和显示。
接下来,我们就结合源代码分析 Android 应用程序 UI 硬件加速渲染环境的初始化过程,主要的关注点就是创建 Render Thread 的过程和绑定窗口到 Render Thread 的过程。
从前面一文可以知道,Activity 组件在创建的过程中,也就是在其生命周期函数 onCreate 的调用过程中,一般会通过调用另外一个成员函数 setContentView 创建和初始化关联的窗口视图,最后通过调用 ViewRoot 类的成员函数 setView 完成这一过程。上述文章分析的源码是 Android 2.3 版本的。到了 Android 4.0 之后,ViewRoot 类的名字改成了 ViewRootImpl,它们的作用仍然一样的。
Android 应用程序 UI 硬件加速渲染环境的初始化过程是在 ViewRootImpl 类的成员函数 setView 开始,如下所示:
这个函数定义在文件 frameworks/base/core/java/android/view/ViewRootImpl.java 中。
- public final class ViewRootImpl implements ViewParent,
- View.AttachInfo.Callbacks,
- HardwareRenderer.HardwareDrawCallbacks {......public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
- synchronized(this) {
- if (mView == null) {
- mView = view;......
- if (view instanceof RootViewSurfaceTaker) {
- mSurfaceHolderCallback = ((RootViewSurfaceTaker) view).willYouTakeTheSurface();
- if (mSurfaceHolderCallback != null) {
- mSurfaceHolder = new TakenSurfaceHolder();
- mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
- }
- }......
- // If the application owns the surface, don't enable hardware acceleration
- if (mSurfaceHolder == null) {
- enableHardwareAcceleration(attrs);
- }......
- }
- }
- }......
- }
参数 view 描述的是当前正在创建的 Activity 窗口的顶级视图。如果它实现了 RootViewSurfaceTaker 接口,并且通过该接口的成员函数 willYouTakeTheSurface 提供了一个 SurfaceHolder.Callback2 接口,那么就表明应用程序想自己接管对窗口的一切渲染操作。这样创建出来的 Activity 窗口就类似于一个 SurfaceView 一样,完全由应用程序自己来控制它的渲染。
基本上我们是不会将一个 Activity 窗口当作一个 SurfaceView 来使用的,因此在 ViewRootImpl 类的成员变量 mSurfaceHolder 将保持为 null 值,这样就会导致 ViewRootImpl 类的成员函数 enableHardwareAcceleration 被调用为判断是否需要为当前创建的 Activity 窗口启用硬件加速渲染。
ViewRootImpl 类的成员函数 enableHardwareAcceleration 的实现如下所示:
这个函数定义在文件 frameworks/base/core/java/android/view/ViewRootImpl.java 中。
- public final class ViewRootImpl implements ViewParent,
- View.AttachInfo.Callbacks,
- HardwareRenderer.HardwareDrawCallbacks {......private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
- mAttachInfo.mHardwareAccelerated = false;
- mAttachInfo.mHardwareAccelerationRequested = false;......
- // Try to enable hardware acceleration if requested
- final boolean hardwareAccelerated = (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
- if (hardwareAccelerated) {
- if (!HardwareRenderer.isAvailable()) {
- return;
- }
- // 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.)
- 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) {
- // This is exclusively for the preview windows the window manager
- // shows for launching applications, so they will look more like
- // the app being launched.
- mAttachInfo.mHardwareAccelerationRequested = true;
- } else if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) {.......mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent);
- if (mAttachInfo.mHardwareRenderer != null) {.......mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true;
- }
- }
- }
- }......
- }
虽然硬件加速渲染是个好东西,但是也不是每一个需要绘制 UI 的进程都必需的。这样做是考虑到两个因素。第一个因素是并不是所有的 Canvas API 都可以被 GPU 支持。如果应用程序使用到了这些不被 GPU 支持的 API,那么就需要禁用硬件加速渲染。第二个因素是支持硬件加速渲染的代价是增加了内存开销。例如,只是硬件加速渲染环境初始化这一操作,就要花掉 8M 的内存。因此,对于两类进程就不是很适合使用硬件加速渲染。
第一类进程是 Persistent 进程。Persistent 进程是一种常驻进程,它们的优先级别很高,即使在内存紧张的时候也不会被 AMS 杀掉。对于低内存设备,这类进程是不适合使用硬件加速渲染的。在这种情况下,它们会将 HardwareRenderer 类的静态成员变量 sRendererDisabled 设置为 true,表明要禁用硬件加速渲染。这里顺便提一下,一个应用程序进程可以在 AndroidManifest.xml 文件将 Application 标签的 persistent 属性设置为 true 来将自己设置为 Persistent 进程,不过只有系统级别的应用设置才有效。类似的进程有 Phone、Bluetooth 和 Nfc 等应用。
第二类进程是 System 进程。System 进程有很多线程是需要显示 UI 的。这些 UI 一般都是比较简单的,并且 System 进程也像 Persistent 进程一样,在内存紧张时是无法杀掉的,因此它们完全没有必要通过硬件加速来渲染。于是,System 进程就会将 HardwareRenderer 类的静态成员变量 sRendererDisabled 和 sSystemRendererDisabled 都会被设置为 true,表示它要禁用硬件加速渲染。
对于 System 进程,有两种 UI 需要特殊处理。第一种 UI 是 Starting Window。当一个 Activity 启动时,如果它的宿主进程还没有创建,那么在等待其宿主进程创建的过程中,System 进程就会根据该 Activity 窗口设置的 Theme 显示一个 Starting Window,也称为 Preview Window。由于 System 进程是禁用了硬件加速渲染的,因此 Starting Window 是通过软件方式渲染的。但是为了使得 Starting Window 的渲染更像它对应的 Activity 窗口,我们将用来描述 Starting Window 属性的一个 AttachInfo 对象的成员变量 mHardwareAccelerationRequested 的值设置为 true。这将会使得 Starting Window 的 view_state_accelerated 属性设置为 true。该属性一旦被设置为 true,将会使得 Starting Window 的另一属性 colorBackgroundCacheHint 被忽略。属性 colorBackgroundCacheHint 被忽略之后,Starting Window 在绘制的过程中将不会被缓存。使用了硬件加速渲染的 Activity 窗口在渲染的过程中也是不会被缓存的。这就使得它们的渲染行为保持一致。Starting Window 的这一特性是通过将参数 attrs 指向的一个 WindowManager.LayoutParams 对象的成员变量 privateFlags 的位 WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED 设置为 1 来描述的。
第二种 UI 是锁屏界面。锁屏界面是一个例外,它允许使用硬件加速渲染。但是 System 进程又表明了它要禁用硬件加速渲染,这时候就通过将参数 attrs 指向的一个 WindowManager.LayoutParams 对象的成员变量 privateFlags 的位 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED 设置为 1 来强调锁屏界面不受 System 进程禁用硬件加速的限制。
除了上面提到的两类进程以及一些特殊的 UI,其余的就根据 Activity 窗口自己是否请求了硬件加速渲染而决定是否要为其开启硬件加速。在默认情况下,Activity 窗口是请求硬件加速渲染的,也就是参数 attrs 指向的一个 WindowManager.LayoutParams 对象的成员变量 flags 的位 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED 是被设置为 1 的。不过即便如此,也是要设备本身支持硬件加速渲染才行。判断设备是否设置硬件加速渲染可以通过调用 HardwareRenderer 类的静态成员函数 isAvailable 来获得。
最后,如果当前创建的窗口支持硬件加速渲染,那么就会调用 HardwareRenderer 类的静态成员函数 create 创建一个 HardwareRenderer 对象,并且保存在与该窗口关联的一个 AttachInfo 对象的成员变量的成员变量 mHardwareRenderer 对象。这个 HardwareRenderer 对象以后将负责执行窗口硬件加速渲染的相关操作。
HardwareRenderer 类的静态成员函数 create 的实现如下所示:
这个函数定义在文件 frameworks/base/core/java/android/view/HardwareRenderer.java。
- public abstract class HardwareRenderer {......static HardwareRenderer create(Context context, boolean translucent) {
- HardwareRenderer renderer = null;
- if (GLES20Canvas.isAvailable()) {
- renderer = new ThreadedRenderer(context, translucent);
- }
- return renderer;
- }......
- }
从这里就可以看到,在设备支持 Open GL ES 2.0 的情况下,HardwareRenderer 类的静态成员函数 create 创建的实际上是一个 ThreadedRenderer 对象。该 ThreadedRenderer 对象是从 HardwareRenderer 类继承下来的。
接下来我们就继续分析 ThreadedRenderer 对象的创建过程,如下所示:
这个函数定义在文件 frameworks/base/core/java/android/view/ThreadedRenderer.java。
- public class ThreadedRenderer extends HardwareRenderer {......private long mNativeProxy;......private RenderNode mRootNode;......ThreadedRenderer(Context context, boolean translucent) {......long rootNodePtr = nCreateRootRenderNode();
- mRootNode = RenderNode.adopt(rootNodePtr);......mNativeProxy = nCreateProxy(translucent, rootNodePtr);
- AtlasInitializer.sInstance.init(context, mNativeProxy);......
- }......
- }
在创建 ThreadedRenderer 对象的过程中,最主要的是做了三件事情:
1. 调用 ThreadedRenderer 类的成员函数 nCreateRootRenderNode 在 Native 层创建了一个 Render Node,并且通过 Java 层的 RenderNode 类的静态成员函数 adopt 将其封装在一个 Java 层的 Render Node 中。这个 Render Node 即为窗口的 Root Render Node。
2. 调用 ThreadedRenderer 类的成员函数 nCreateProxy 在 Native 层创建了一个 Render Proxy 对象。该 Render Proxy 对象以后将负责从 Main Thread 向 Render Thread 发送命令。
3. 调用 AtlasInitializer 类的成员函数 init 初始化一个系统预加载资源的地图集。通过这个地图集,可以优化资源的内存使用。
关于系统预加载资源地图集,我们在下一篇文章中再详细分析,这里我们主要关注窗口的 Root Render Node 以及在 Main Thread 线程中使用的 Render Proxy 对象的创建过程。
窗口的 Root Render Node 是通过调用 ThreadedRenderer 类的成员函数 nCreateRootRenderNode 创建的。这是一个 JNI 函数,由 Native 层的函数 android_view_ThreadedRenderer_createRootRenderNode 实现,如下所示:
这个函数定义在文件 frameworks/base/core/jni/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);
- }
从这里就可以看出,窗口在 Native 层的 Root Render Node 实际上是一个 RootRenderNode 对象。
窗口在 Main Thread 线程中使用的 Render Proxy 对象是通过调用 ThreadedRenderer 类的成员函数 nCreateProxy 创建的。这是一个 JNI 函数,由 Native 层的函数 android_view_ThreadedRenderer_createProxy 实现,如下所示:
这个函数定义在文件 frameworks/base/core/jni/android_view_ThreadedRenderer.cpp 中。
- 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);
- }
参数 rootRenderNodePtr 指向前面创建的 RootRenderNode 对象。有了这个 RootRenderNode 对象之后,函数 android_view_ThreadedRenderer_createProxy 就创建了一个 RenderProxy 对象。
RenderProxy 对象的创建过程如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderProxy.cpp 中。
- RenderProxy: :RenderProxy(bool translucent, RenderNode * rootRenderNode, IContextFactory * contextFactory) : mRenderThread(RenderThread: :getInstance()),
- mContext(0) {
- SETUP_TASK(createContext);
- args - >translucent = translucent;
- args - >rootRenderNode = rootRenderNode;
- args - >thread = &mRenderThread;
- args - >contextFactory = contextFactory;
- mContext = (CanvasContext * ) postAndWait(task);
- mDrawFrameTask.setContext( & mRenderThread, mContext);
- }
RenderProxy 类有三个重要的成员变量 mRenderThread、mContext 和 mDrawFrameTask,它们的类型分别为 RenderThread、CanvasContext 和 DrawFrameTask。其中,mRenderThread 描述的就是 Render Thread,mContext 描述的是一个画布上下文,mDrawFrameTask 描述的是一个用来执行渲染任务的 Task。接下来我们就重点分析这三个成员变量的初始化过程。
RenderProxy 类的成员变量 mRenderThread 指向的 Render Thread 是通过调用 RenderThread 类的静态成员函数 getInstance 获得的。从名字我们就可以看出,RenderThread 类的静态成员函数 getInstance 返回的是一个 RenderThread 单例。也就是说,在一个 Android 应用程序进程中,只有一个 Render Thread 存在。
为了更好地了解 Render Thread 是如何运行的,我们继续分析 Render Thread 的创建过程,如下所示:
- RenderThread: :RenderThread() : Thread(true),
- Singleton < RenderThread > ()...... {
- mFrameCallbackTask = new DispatchFrameCallbacks(this);
- mLooper = new Looper(false);
- run("RenderThread");
- }
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
RenderThread 类的成员变量 mFrameCallbackTask 指向一个 DispatchFrameCallbacks 对象,用来描述一个帧绘制任务。下面描述 Render Thread 的运行模型时,我们再详细分析。
RenderThread 类的成员变量 mLooper 指向一个 Looper 对象,Render Thread 通过它来创建一个消息驱动运行模型,类似于 Main Thread 的消息驱动运行模型。关于 Looper 的实现,可以参考前面一文。
RenderThread 类是从 Thread 类继承下来的,当我们调用它的成员函数 run 的时候,就会创建一个新的线程。这个新的线程的入口点函数为 RenderThread 类的成员函数 threadLoop,它的实现如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- bool RenderThread: :threadLoop() {.......initThreadLocals();
- int timeoutMillis = -1;
- for (;;) {
- int result = mLooper - >pollOnce(timeoutMillis);......nsecs_t nextWakeup;
- // Process our queue, if we have anything
- while (RenderTask * task = nextTask( & nextWakeup)) {
- 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) {
- drainDisplayEventQueue(true);
- mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end());
- mPendingRegistrationFrameCallbacks.clear();
- requestVsync();
- }
- }
- return false;
- }
这里我们就可以看到 Render Thread 的运行模型:
1. 空闲的时候,Render Thread 就睡眠在成员变量 mLooper 指向的一个 Looper 对象的成员函数 pollOnce 中。
2. 当其它线程需要调度 Render Thread,就会向它的任务队列增加一个任务,然后唤醒 Render Thread 进行处理。Render Thread 通过成员函数 nextTask 获得需要处理的任务,并且调用它的成员函数 run 进行处理。
RenderThread 类的成员函数 nextTask 的实现如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- RenderTask * RenderThread: :nextTask(nsecs_t * nextWakeup) {
- AutoMutex _lock(mLock);
- RenderTask * next = mQueue.peek();
- if (!next) {
- mNextWakeup = LLONG_MAX;
- } else {
- mNextWakeup = next - >mRunAt;
- // Most tasks won't be delayed, so avoid unnecessary systemTime() calls
- if (next - >mRunAt <= 0 || next - >mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
- next = mQueue.next();
- } else {
- next = 0;
- }
- }
- if (nextWakeup) { * nextWakeup = mNextWakeup;
- }
- return next;
- }
RenderThread 类的成员变量 mQueue 描述的是一个 Task Queue。每一个 Task 都是用一个 RenderTask 对象来描述。同时,RenderTask 类有一个成员变量 mRunAt,用来表明 Task 的执行时间。这样,保存在 Task Queue 的 Task 就可以按照执行时间从先到后的顺序排序。于是,RenderThread 类的成员函数 nextTask 通过判断排在队列头的 Task 的执行时间是否小于等于当前时间,就可以知道当前是否有 Task 需要执行。如果有 Task 需要执行的话,就将它返回给调用者。
RenderThread 类的成员函数 nextTask 除了返回下一个要执行的 Task 之外,还会通过参数 nextWakeup 返回下一个要执行的 Task 的执行时间。这个时间同时也会记录在 RenderThread 类的成员变量 mNextWakeup 中。注意,下一个要执行的 Task 可能是马上就要执行的,也有可能是由于执行时间还未到而不能执行的 Task。返回这个时间的意义是使得 Render Thread 可以准确计算下一次需要进入睡眠状态的时间。这个计算可以回过头去看前面分析的 RenderThread 类的成员函数 threadLoop。
注意,如果没有下一个任务可以执行,那么 RenderThread 类的成员函数 nextTask 通过参数 nextWakeup 返回的值为 LLONG_MAX,表示 Render Thread 接下来无限期进入睡眠状态,直到被其它线程唤醒为止。
RenderThread 类提供了 queue、queueAtFront 和 queueDelayed 三个成员函数向 Task Queue 增加一个 Task,它们的实现如下所示:
这三个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- void RenderThread: :queue(RenderTask * task) {
- AutoMutex _lock(mLock);
- mQueue.queue(task);
- if (mNextWakeup && task - >mRunAt < mNextWakeup) {
- mNextWakeup = 0;
- mLooper - >wake();
- }
- }
- void RenderThread: :queueAtFront(RenderTask * task) {
- AutoMutex _lock(mLock);
- mQueue.queueAtFront(task);
- mLooper - >wake();
- }
- void RenderThread: :queueDelayed(RenderTask * task, int delayMs) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- task - >mRunAt = now + milliseconds_to_nanoseconds(delayMs);
- queue(task);
- }
其中, RenderThread 类的成员函数 queue 按照执行时间将参数 task 描述的 Task 排列在 Task Queue 中,并且如果该 Task 的执行时间小于之前记录的下一个要执行任务的执行时间,就会马上唤醒 Render Thread 来处理;RenderThread 类的成员函数 queue 将参数 task 描述的 Task 排列在 Task Queue 的头部,并且马上唤醒 Render Thread 来处理;RenderThread 类的成员函数 queueDelayed 指定参数 task 描述的 Task 的执行时间为当前时间之后的 delayMs 毫秒。
理解了 Render Thread 的 Task Queue 之后,回到 RenderThread 类的成员函数 threadLoop 中,我们再来看 Render Thread 在进入无限循环之前调用的 RenderThread 类的成员函数 initThreadLocals,它的实现如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- void RenderThread: :initThreadLocals() {
- initializeDisplayEventReceiver();
- mEglManager = new EglManager( * this);
- mRenderState = new RenderState();
- }
RenderThread 类的成员函数 initThreadLocals 首先调用另外一个成员函数 initializeDisplayEventReceiver 创建和初始化一个 DisplayEventReceiver 对象,用来接收 Vsync 信号。接着又会分别创建一个 EglManager 对象和一个 RenderState 对象,并且保存在成员变量 mEglManager 和 mRenderState 中。前者用在初始化 Open GL 渲染上下文需要用到,而后者用来记录 Render Thread 当前的一些渲染状态。
接下来我们主要关注 DisplayEventReceiver 对象的创建和初始化过程,即 RenderThread 类的成员函数 initializeDisplayEventReceiver 的实现,如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- void RenderThread: :initializeDisplayEventReceiver() {......mDisplayEventReceiver = new DisplayEventReceiver();......
- // Register the FD
- mLooper - >addFd(mDisplayEventReceiver - >getFd(), 0, Looper: :EVENT_INPUT, RenderThread: :displayEventReceiverCallback, this);
- }
创建的 DisplayEventReceiver 对象关联的文件描述符被注册到了 Render Thread 的消息循环中。这意味着屏幕产生 Vsync 信号时,SurfaceFlinger 服务(Vsync 信号由 SurfaceFlinger 服务进行管理和分发)会通过上述文件描述符号唤醒 Render Thread。这时候 Render Thread 就会调用 RenderThread 类的静态成员函数 displayEventReceiverCallback。
RenderThread 类的静态成员函数 displayEventReceiverCallback 的实现如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- int RenderThread: :displayEventReceiverCallback(int fd, int events, void * data) {......reinterpret_cast < RenderThread * >(data) - >drainDisplayEventQueue();
- return 1; // keep the callback
- }
RenderThread 类的静态成员函数 displayEventReceiverCallback 调用 RenderThread 类的成员函数 drainDisplayEventQueue 来处理 Vsync 信号,后者的实现如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- void RenderThread: :drainDisplayEventQueue(bool skipCallbacks) {......nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
- if (vsyncEvent > 0) {
- mVsyncRequested = false;
- mTimeLord.vsyncReceived(vsyncEvent);
- if (!skipCallbacks && !mFrameCallbackTaskPending) {......mFrameCallbackTaskPending = true;
- queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
- }
- }
- }
RenderThread 类的成员函数 drainDisplayEventQueue 首先调用另外一个成员函数 latestVsyncEvent 获得最新发出的 Vsync 信号的时间。如果这个时间值大于 0,那么就表明这是一个有效的 Vsync 信号。在这种情况下,就将 RenderThread 类的成员变量 mVsyncRequested 设置为 false,表示上次发出的 Vsync 信号接收请求已经获得。
最后,如果参数 skipCallbacks 的值等于 false,那么就表示需要将 RenderThread 类的成员变量 mFrameCallbackTask 指向的一个类型为 DispatchFrameCallbacks 的 Task 添加到 Render Thread 的 Task Queue 去处理。但是这时候如果 RenderThread 类的成员变量 mFrameCallbackTaskPending 的值也等于 true,就表示该 Task 已经添加到 Render Thread 的 Task Queue 去了,因此就不再用重复添加。
RenderThread 类的成员变量 mFrameCallbackTask 描述的 Task 是用来做什么的呢?原来就是用来显示动画的。当 Java 层注册一个动画类型的 Render Node 到 Render Thread 时,一个类型为 IFrameCallback 的回调接口就会通过 RenderThread 类的成员函数 postFrameCallback 注册到 Render Thread 的一个 Pending Registration Frame Callbacks 列表中,如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- void RenderThread: :postFrameCallback(IFrameCallback * callback) {
- mPendingRegistrationFrameCallbacks.insert(callback);
- }
Render Thread 的 Pending Registration Frame Callbacks 列表由 RenderThread 类的成员变量 mPendingRegistrationFrameCallbacks 描述。
当 Pending Registration Frame Callbacks 列表不为空时,每次 Vsync 信号到来时,Render Thread 都会通过 RenderThread 类的成员变量 mFrameCallbackTask 描述的一个 Task 来执行它,这样就相当于是将动画的每一帧都同步到 Vsync 信号来显示。这也是为什么 RenderThread 类的成员函数 drainDisplayEventQueue 每次被调用要检查是否需要将成员变量 mFrameCallbackTask 描述的一个 Task 添加到 Render Thread 的 Task Queue 的原因。
当 RenderThread 类的成员变量 mFrameCallbackTask 描述的 Task 的类型为 DispatchFrameCallbacks,当它被调度执行时,它的成员函数 run 就会被调用,如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- class DispatchFrameCallbacks: public RenderTask {
- private: RenderThread * mRenderThread;
- public: DispatchFrameCallbacks(RenderThread * rt) : mRenderThread(rt) {}
- virtual void run() {
- mRenderThread - >dispatchFrameCallbacks();
- }
- };
DispatchFrameCallbacks 类的成员函数 run 调用了 RenderThread 类的成员函数 dispatchFrameCallbacks 来执行注册到 Pending Registration Frame Callbacks 列表中的 IFrameCallback 回调接口,如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderThread.cpp 中。
- void RenderThread: :dispatchFrameCallbacks() {
- ATRACE_CALL();
- mFrameCallbackTaskPending = false;
- std: :set < IFrameCallback * >callbacks;
- mFrameCallbacks.swap(callbacks);
- for (std: :set < IFrameCallback * >::iterator it = callbacks.begin(); it != callbacks.end(); it++) { ( * it) - >doFrame();
- }
- }
我们回过头来看前面分析的 RenderThread 类的成员函数 threadLoop,每当 Render Thread 被唤醒时,它都会检查 Pending Registration Frame Callbacks 列表是否不为空。如果不为空,那么就会将保存在里面的 IFrameCallback 回调接口转移至由 RenderThread 类的成员变量 mFrameCallbacks 描述的另外一个 IFrameCallback 回调接口列表中,并且调用 RenderThread 类的另外一个成员函数 requestVsync 请求 SurfaceFlinger 服务在下一个 Vsync 信号到来时通知 Render Thread,以便 Render Thread 可以执行刚才被转移的 IFrameCallback 回调接口。
现在既然下一个 Vsync 信号已经到来,因此 RenderThread 类的成员函数 dispatchFrameCallbacks 就执行所有转移至保存在成员变量 mFrameCallbacks 描述的 IFrameCallback 回调接口列表中的 IFrameCallback 接口,即调用它们的成员函数 doFrame。
总结来说,Render Thread 在运行时主要是做以下两件事情:
1. 执行 Task Queue 的任务,这些 Task 一般就是由 Main Thread 发送过来的,例如,Main Thread 通过发送一个 Draw Frame Task 给 Render Thread 的 Task Queue 中,请求 Render Thread 渲染窗口的下一帧。
2. 执行 Pending Registration Frame Callbacks 列表的 IFrameCallback 回调接口。每一个 IFrameCallback 回调接口代表的是一个动画帧,这些动画帧被同步到 Vsync 信号到来由 Render Thread 自动执行。具体来说,就是每当 Vsync 信号到来时,就将一个类型为 DispatchFrameCallbacks 的 Task 添加到 Render Thread 的 Task Queue 去等待调度。一旦该 Task 被调度,就可以在 Render Thread 中执行注册在 Pending Registration Frame Callbacks 列表中的 IFrameCallback 回调接口了。
在后面的文章中,我们还会继续分析上述这两件事情的详细执行过程。
了解了 Render Thread 的创建过程之后,回到 RenderProxy 类的构造函数中,接下来我们继续分析它的成员变量 mContext 的初始化过程,也就是画布上下文的初始化过程。这是通过向 Render Thread 发送一个 createContext 命令来完成的。为了方便描述,我们将相关的代码列出来,如下所示:
这些代码片断定义在文件 frameworks/base/libs/hwui/renderthread/RenderProxy.cpp。
- #define ARGS(method) method##Args#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1, a2, a3, a4, , , , )#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8)\typedef struct {\a1;
- a2;
- a3;
- a4;
- a5;
- a6;
- a7;
- a8;\
- }
- ARGS(name);\static void * Bridge_##name(ARGS(name) * args)#define SETUP_TASK(method)\.......MethodInvokeRenderTask * task = new MethodInvokeRenderTask((RunnableMethod) Bridge_##method);\ARGS(method) * args = (ARGS(method) * ) task - >payload() CREATE_BRIDGE4(createContext, RenderThread * thread, bool translucent, RenderNode * rootRenderNode, IContextFactory * contextFactory) {
- return new CanvasContext( * args - >thread, args - >translucent, args - >rootRenderNode, args - >contextFactory);
- }
- RenderProxy: :RenderProxy(bool translucent, RenderNode * rootRenderNode, IContextFactory * contextFactory)...... {
- SETUP_TASK(createContext);
- args - >translucent = translucent;
- args - >rootRenderNode = rootRenderNode;
- args - >thread = &mRenderThread;
- args - >contextFactory = contextFactory;
- mContext = (CanvasContext * ) postAndWait(task);......
- }
这是一个典型的 Main Thread 通过 Render Proxy 向 Render Thread 请求执行一个命令的流程,在后面的文章中,碰到类型的代码时,我们就将忽略中间的封装环节,直接分析命令的执行过程。
我们首先看宏 SETUP_TASK,它需要一个函数作为参数。这个函数通过 CREATE_BRIDGEX 来声明,其中 X 是一个数字,数字的大小就等于函数需要的参数的个数。例如,通过 CREATE_BRIDGE4 声明的函数有 4 个参数。在上面的代码段中,我们通过 CREATE_BRIDGE4 宏声明了一个 createContext 函数。
宏 SETUP_TASK 的作用创建一个类型 MethodInvokeRenderTask 的 Task。这个 Task 关联有一个由 CREATE_BRIDGEX 宏声明的函数。例如,SETUP_TASK(createContext) 创建的 MethodInvokeRenderTask 关联的函数是由 CREATE_BRIDGE4 声明的函数 createContext。这个 Task 最终会通过 RenderProxy 类的成员函数 postAndWait 添加到 Render Thread 的 Task Queue 中,如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderProxy.cpp。
- void * RenderProxy: :postAndWait(MethodInvokeRenderTask * task) {
- void * retval;
- task - >setReturnPtr( & retval);
- SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
- AutoMutex _lock(mSyncMutex);
- mRenderThread.queue( & syncTask);
- mSyncCondition.wait(mSyncMutex);
- return retval;
- }
RenderProxy 类的成员函数 postAndWait 除了会将参数 task 描述 MethodInvokeRenderTask 描述的 Task 添加到 Render Thread 的 Task Queue 之外,还会等待该 Task 执行完成之后才返回。也就是说,这是一个从 Main Thread 到 Render Thread 的同步调用过程。
当 MethodInvokeRenderTask 被执行时,它所关联的函数就会被调用。例如,在我们这个情景中,通过 CREATE_BRIDGE4 声明的函数 createContext 就会被执行。注意,这时候函数 createContext 是在 Render Thread 中执行的,它主要就是创建一个 CanvasContext 对象,用来描述 Render Thread 的画布上下文。这个画布上下文接下来在 Render Thread 中绑定窗口时会用到,到时候我们就可以更清楚地看到它的作用。
回到 RenderProxy 类的构造函数中,接下来我们继续分析它的成员变量 mDrawFrameTask 的初始化过程。RenderProxy 类的成员变量 mDrawFrameTask 描述的是一个 Draw Frame Task,Main Thread 每次都是通过它来向 Render Thread 发出渲染下一帧的命令的。
对 Draw Frame Task 的初始化很简单,主要是将前面已经获得的 RenderThread 对象和 CanvasContext 对象保存在它内部,以便以后它可以直接使用相关的功能,这是通过调用 DrawFrameTask 类的成员函数 setContext 实现的,如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp 中。
- void DrawFrameTask: :setContext(RenderThread * thread, CanvasContext * context) {
- mRenderThread = thread;
- mContext = context;
- }
这样,一个 RenderProxy 对象的创建过程就分析完成了,从中我们也看到 Render Thread 的创建过程和运行模型,以及 Render Proxy 与 Render Thread 的交互模型,总结来说:
1. RenderProxy 内部有一个成员变量 mRenderThread,它指向的是一个 RenderThread 对象,通过它可以向 Render Thread 线程发送命令。
2. RenderProxy 内部有一个成员变量 mContext,它指向的是一个 CanvasContext 对象,Render Thread 的渲染工作就是通过它来完成的。
3. RenderProxy 内部有一个成员变量 mDrawFrameTask,它指向的是一个 DrawFrameTask 对象,Main Thread 通过它向 Render Thread 线程发送渲染下一帧的命令。
接下来我们继续分析 Android 应用程序 UI 硬件加速渲染环境初始化的另一个主要任务 -- 绑定窗口到 Render Thread 中。
从前面这篇文章可以知道,Activity 窗口的绘制流程是在 ViewRoot(Impl) 类的成员函数 performTraversals 发起的。在绘制之前,首先要获得一个 Surface。这个 Surface 描述的就是一个窗口。因此,一旦获得了对应的 Surface,就需要将它绑定到 Render Thread 中,如下所示:
这个函数定义在文件 frameworks/base/core/java/android/view/ViewRootImpl.java 中。
- public final class ViewRootImpl implements ViewParent,
- View.AttachInfo.Callbacks,
- HardwareRenderer.HardwareDrawCallbacks {......private void performTraversals() {......
- if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null) {......
- try {......relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);......
- if (!hadSurface) {
- if (mSurface.isValid()) {......
- if (mAttachInfo.mHardwareRenderer != null) {
- try {
- hwInitialized = mAttachInfo.mHardwareRenderer.initialize(mSurface);
- } catch(OutOfResourcesException e) {......
- }
- }
- }
- }......
- } catch(RemoteException e) {}......
- }
- if (!cancelDraw && !newSurface) {
- if (!skipDraw || mReportNextDraw) {......performDraw();
- }
- }......
- }......
- }
当前 Activity 窗口对应的 Surface 是通过调用 ViewRootImpl 类的成员函数 relayoutWindow 向 WindowManagerService 服务请求创建和返回的,并且保存在 ViewRootImpl 类的成员变量 mSurface 中。如果这个 Surface 是新创建的,那么就会调用 ViewRootImpl 类的成员变量 mAttachInfo 指向的一个 AttachInfo 对象的成员变量 mHardwareRenderer 描述的一个 HardwareRenderer 对象的成员函数 initialize 将它绑定到 Render Thread 中。最后,如果需要绘制当前的 Activity 窗口,那会调用 ViewRootImpl 类的另外一个成员函数 performDraw 进行绘制。
这里我们只关注绑定窗口对应的 Surface 到 Render Thread 的过程。从前面的分析可以知道,ViewRootImpl 类的成员变量 mAttachInfo 指向的一个 AttachInfo 对象的成员变量 mHardwareRenderer 保存的实际上是一个 ThreadedRenderer 对象,它的成员函数 initialize 的实现如下所示:
这个函数定义在文件 frameworks/base/core/java/android/view/ThreadedRenderer.java 中。
- public class ThreadedRenderer extends HardwareRenderer {......@Override boolean initialize(Surface surface) throws OutOfResourcesException {
- mInitialized = true;......boolean status = nInitialize(mNativeProxy, surface);......
- return status;
- }......
- }
ThreadedRenderer 类的成员函数 initialize 首先将成员变量 mInitialized 的值设置为 true,表明它接下来已经绑定过 Surface 到 Render Thread 了,接着再调用另外一个成员函数 nInitialize 执行真正的绑定工作。
ThreadedRenderer 类的成员函数 nInitialize 是一个 JNI 函数,由 Native 层的函数 android_view_ThreadedRenderer_initialize 实现,如下所示:
这个函数定义在文件 frameworks/base/core/jni/android_view_ThreadedRenderer.cpp 中。
- static jboolean android_view_ThreadedRenderer_initialize(JNIEnv * env, jobject clazz, jlong proxyPtr, jobject jsurface) {
- RenderProxy * proxy = reinterpret_cast < RenderProxy * >(proxyPtr);
- sp < ANativeWindow > window = android_view_Surface_getNativeWindow(env, jsurface);
- return proxy - >initialize(window);
- }
参数 proxyPtr 描述的就是之前所创建的一个 RenderProxy 对象,而参数 jsurface 描述的是要绑定给 Render Thread 的 Java 层的 Surface。前面提到,Java 层的 Surface 在 Native 层对应的是一个 ANativeWindow。我们可以通过函数 android_view_Surface_getNativeWindow 来获得一个 Java 层的 Surface 在 Native 层对应的 ANativeWindow。
接下来,就可以通过 RenderProxy 类的成员函数 initialize 将前面获得的 ANativeWindow 绑定到 Render Thread 中,如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/RenderProxy.cpp 中。
- CREATE_BRIDGE2(initialize, CanvasContext * context, ANativeWindow * window) {
- return (void * ) args - >context - >initialize(args - >window);
- }
- bool RenderProxy: :initialize(const sp < ANativeWindow > &window) {
- SETUP_TASK(initialize);
- args - >context = mContext;
- args - >window = window.get();
- return (bool) postAndWait(task);
- }
从前面的分析可以知道,RenderProxy 类的成员函数 initialize 向 Render Thread 的 Task Queue 发送了一个 Task。当这个 Task 在 Render Thread 中执行时,由宏 CREATE_BRIDGE2 声明的函数 initialize 就会被执行。
在由宏 CREATE_BRIDGE2 声明的函数 initialize 中,参数 context 指向的是 RenderProxy 类的成员变量 mContext 指向的一个 CanvasContext 对象,而参数 window 指向的 ANativeWindow 就是要绑定到 Render Thread 的 ANativeWindow。
由宏 CREATE_BRIDGE2 声明的函数 initialize 通过调用参数 context 指向的 CanvasContext 对象的成员函数 initialize 来绑定参数 window 指向的 ANativeWindow,如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/CanvasContext.cpp 中。
- bool CanvasContext: :initialize(ANativeWindow * window) {
- setSurface(window);
- if (mCanvas) return false;
- mCanvas = new OpenGLRenderer(mRenderThread.renderState());......
- return true;
- }
CanvasContext 类的成员函数 initialize 通过调用另外一个成员函数 setSurface 来绑定参数 window 描述的 ANativeWindow 到 Render Thread 中。绑定完成之后,如果 CanvasContext 类的成员变量 mCanvas 等于 NULL,那么就说明负责执行 Open GL 渲染命令的一个 OpenGLRenderer 对象还没创建。在这种情况下,就创建一个 OpenGLRenderer 对象,并且保存在 CanvasContext 类的成员变量 mCanvas 中,以便后面可以用来执行 Open GL 相关操作。
CanvasContext 类的成员函数 setSurface 的实现如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/CanvasContext.cpp 中。
- void CanvasContext: :setSurface(ANativeWindow * window) {
- mNativeWindow = window;
- if (mEglSurface != EGL_NO_SURFACE) {
- mEglManager.destroySurface(mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
- }
- if (window) {
- mEglSurface = mEglManager.createSurface(window);
- }
- if (mEglSurface != EGL_NO_SURFACE) {......mHaveNewSurface = true;
- makeCurrent();
- }......
- }
每一个 Open GL 渲染上下文都需要关联有一个 EGL Surface。这个 EGL Surface 描述的是一个绘图表面,它封装的实际上是一个 ANativeWindow。有了这个 EGL Surface 之后,我们在执行 Open GL 命令的时候,才能确定这些命令是作用在哪个窗口上。
CanvasContext 类的成员变量 mEglManager 实际上是指向前面我们分析 RenderThread 类的成员函数 initThreadLocals 时创建的一个 EglManager 对象。通过调用这个 EglManager 对象的成员函数 createSurface 就可以将参数 window 描述的 ANativeWindow 封装成一个 EGL Surface。
EGL Surface 创建成功之后,就可以调用 CanvasContext 类的成员函数 makeCurrent 将它绑定到 Render Thread 的 Open GL 渲染上下文来,如下所示:
这个函数定义在文件 frameworks/base/libs/hwui/renderthread/CanvasContext.cpp 中。
- void CanvasContext: :makeCurrent() {
- // TODO: Figure out why this workaround is needed, see b/13913604
- // In the meantime this matches the behavior of GLRenderer, so it is not a regression
- mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface);
- }
从这里就可以看到,将一个 EGL Surface 绑定到 Render Thread 的 Open GL 渲染上下文中是通过 CanvasContext 类的成员变量 mEglManager 指向的一个 EglManager 对象的成员函数 makeCurrent 来完成的。实际上就是通过 EGL 函数建立了从 Open GL 到底层 OS 图形系统的桥梁。这一点应该怎么理解呢?Open GL 是一套与 OS 无关的规范,不过当它在一个具体的 OS 实现时,仍然是需要与 OS 的图形系统打交道的。例如,Open GL 需要从底层的 OS 图形系统中获得图形缓冲区来保存渲染结果,并且也需要将渲染好的图形缓冲区交给底层的 OS 图形系统来显示到设备屏幕去。Open GL 与底层的 OS 图形系统的这些交互通道都是通过 EGL 函数来建立的。
至此,将当前窗口绑定到 Render Thread 的过程就分析完成了,整个 Android 应用程序 UI 硬件加速渲染环境的初始化过程也分析完成了。前面在分析 ThreadedRenderer 对象的创建过程时提到了系统预加载资源地图集的概念,在继续分析 Android 应用程序 UI 硬件加速渲染的 Display List 构建和渲染过程之前,我们有必要先分析这个地图集的概念,因为这是 Display List 在渲染时涉及到的一个重要优化,敬请关注!更多的信息也可以参考老罗的新浪微博:。
来源: http://lib.csdn.net/article/android/41939