在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的过程。
从前面Android应用程序窗口(Activity)的视图对象(View)的创建过程分析一文可以知道,Activity组件在创建的过程中,也就是在其生命周期函数onCreate的调用过程中,一般会通过调用另外一个成员函数setContentView创建和初始化关联的窗口视图,最后通过调用ViewRoot类的成员函数setView完成这一过程。上述文章分析的源码是Android 2.3版本的。到了Android 4.0之后,ViewRoot类的名字改成了ViewRootImpl,它们的作用仍然一样的。 Android应用程序UI硬件加速渲染环境的初始化过程是在ViewRootImpl类的成员函数setView开始,如下所示:
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); } ...... } } } ...... } 这个函数定义在文件
frameworks/base/core/java/android/view/ViewRootImpl.java中。 参数view描述的是当前正在创建的Activity窗口的顶级视图。如果它实现了RootViewSurfaceTaker接口,并且通过该接口的成员函数
willYouTakeTheSurface提供了一个SurfaceHolder.Callback2接口,那么就表明应用程序想自己接管对窗口的一切渲染操作。这样创建出来的Activity窗口就类似于一个SurfaceView一样,完全由应用程序自己来控制它的渲染。 基本上我们是不会将一个Activity窗口当作一个SurfaceView来使用的,因此在ViewRootImpl类的成员变量mSurfaceHolder将保持为null值,这样就会导致ViewRootImpl类的成员函数enableHardwareAcceleration被调用为判断是否需要为当前创建的Activity窗口启用硬件加速渲染。
ViewRootImpl类的成员函数enableHardwareAcceleration的实现如下所示:
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