編輯:安卓省電與加速
在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 || (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) { ....... mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent); if (mAttachInfo.mHardwareRenderer != null) { ....... mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; } } } } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。
雖然硬件加速渲染是個好東西,但是也不是每一個需要繪制UI的進程都必需的。這樣做是考慮到兩個因素。第一個因素是並不是所有的Canvas API都可以被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的實現如下所示:
public abstract class HardwareRenderer { ...... static HardwareRenderer create(Context context, boolean translucent) { HardwareRenderer renderer = null; if (GLES20Canvas.isAvailable()) { renderer = new ThreadedRenderer(context, translucent); } return renderer; } ...... }這個函數定義在文件frameworks/base/core/java/android/view/HardwareRenderer.java。
從這裡就可以看到,在設備支持Open GL ES 2.0的情況下,HardwareRenderer類的靜態成員函數create創建的實際上是一個ThreadedRenderer對象。該ThreadedRenderer對象是從HardwareRenderer類繼承下來的。
接下來我們就繼續分析ThreadedRenderer對象的創建過程,如下所示:
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); ...... } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ThreadedRenderer.java。
在創建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實現,如下所示:
static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) { RootRenderNode* node = new RootRenderNode(env); node->incStrong(0); node->setName(RootRenderNode); return reinterpret_cast這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。(node); }
從這裡就可以看出,窗口在Native層的Root Render Node實際上是一個RootRenderNode對象。
窗口在Main Thread線程中使用的Render Proxy對象是通過調用ThreadedRenderer類的成員函數nCreateProxy創建的。這是一個JNI函數,由Native層的函數android_view_ThreadedRenderer_createProxy實現,如下所示:
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, jboolean translucent, jlong rootRenderNodePtr) { RootRenderNode* rootRenderNode = reinterpret_cast這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。(rootRenderNodePtr); ContextFactoryImpl factory(rootRenderNode); return (jlong) new RenderProxy(translucent, rootRenderNode, &factory); }
參數rootRenderNodePtr指向前面創建的RootRenderNode對象。有了這個RootRenderNode對象之後,函數android_view_ThreadedRenderer_createProxy就創建了一個RenderProxy對象。
RenderProxy對象的創建過程如下所示:
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); }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp中。
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() ...... { 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的實現,可以參考前面Android應用程序消息處理機制(Looper、Handler)分析一文。
RenderThread類是從Thread類繼承下來的,當我們調用它的成員函數run的時候,就會創建一個新的線程。這個新的線程的入口點函數為RenderThread類的成員函數threadLoop,它的實現如下所示:
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; }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
這裡我們就可以看到Render Thread的運行模型:
1. 空閒的時候,Render Thread就睡眠在成員變量mLooper指向的一個Looper對象的成員函數pollOnce中。
2. 當其它線程需要調度Render Thread,就會向它的任務隊列增加一個任務,然後喚醒Render Thread進行處理。Render Thread通過成員函數nextTask獲得需要處理的任務,並且調用它的成員函數run進行處理。
RenderThread類的成員函數nextTask的實現如下所示:
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; }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
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,它們的實現如下所示:
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); }這三個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
其中, 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,它的實現如下所示:
void RenderThread::initThreadLocals() { initializeDisplayEventReceiver(); mEglManager = new EglManager(*this); mRenderState = new RenderState(); }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
RenderThread類的成員函數initThreadLocals首先調用另外一個成員函數initializeDisplayEventReceiver創建和初始化一個DisplayEventReceiver對象,用來接收Vsync信號。接著又會分別創建一個EglManager對象和一個RenderState對象,並且保存在成員變量mEglManager和mRenderState中。前者用在初始化Open GL渲染上下文需要用到,而後者用來記錄Render Thread當前的一些渲染狀態。
接下來我們主要關注DisplayEventReceiver對象的創建和初始化過程,即RenderThread類的成員函數initializeDisplayEventReceiver的實現,如下所示:
void RenderThread::initializeDisplayEventReceiver() { ...... mDisplayEventReceiver = new DisplayEventReceiver(); ...... // Register the FD mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
創建的DisplayEventReceiver對象關聯的文件描述符被注冊到了Render Thread的消息循環中。這意味著屏幕產生Vsync信號時,SurfaceFlinger服務(Vsync信號由SurfaceFlinger服務進行管理和分發)會通過上述文件描述符號喚醒Render Thread。這時候Render Thread就會調用RenderThread類的靜態成員函數displayEventReceiverCallback。
RenderThread類的靜態成員函數displayEventReceiverCallback的實現如下所示:
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { ...... reinterpret_cast這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。(data)->drainDisplayEventQueue(); return 1; // keep the callback }
RenderThread類的靜態成員函數displayEventReceiverCallback調用RenderThread類的成員函數drainDisplayEventQueue來處理Vsync信號,後者的實現如下所示:
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); } } }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
RenderThread類的靜態成員函數displayEventReceiverCallback首先調用另外一個成員函數latestVsyncEvent獲得最新發出的Vsync信號的時間。如果這個時間值大於0,那麼就表明這是一個有效的Vsync信號。在這種情況下,就將RenderThread類的成員變量mVsyncRequested設置為true,表示上次發出的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列表中,如下所示:
void RenderThread::postFrameCallback(IFrameCallback* callback) { mPendingRegistrationFrameCallbacks.insert(callback); }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
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就會被調用,如下所示:
class DispatchFrameCallbacks : public RenderTask { private: RenderThread* mRenderThread; public: DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {} virtual void run() { mRenderThread->dispatchFrameCallbacks(); } };這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
DispatchFrameCallbacks類的成員函數run調用了RenderThread類的成員函數dispatchFrameCallbacks來執行注冊到Pending Registration Frame Callbacks列表中的IFrameCallback回調接口,如下所示:
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(); } }</iframecallback*></iframecallback*>這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
我們回過頭來看前面分析的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命令來完成的。為了方便描述,我們將相關的代碼列出來,如下所示:
#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); ...... }這些代碼片斷定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp。
這是一個典型的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中,如下所示:
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; }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp。
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實現的,如下所示:
void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) { mRenderThread = thread; mContext = context; }這個函數定義在文件frameworks/base/libs/hwui$ vi renderthread/DrawFrameTask.cpp中。
這樣,一個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通過它向ender Thread線程發送渲染下一幀的命令。
接下來我們繼續分析Android應用程序UI硬件加速渲染環境初始化的另一個主要任務--綁定窗口到Render Thread中。
從前面Android應用程序窗口(Activity)的測量(Measure)、布局(Layout)和繪制(Draw)過程分析這篇文章可以知道,Activity窗口的繪制流程是在ViewRoot(Impl)類的成員函數performTraversals發起的。在繪制之前,首先要獲得一個Surface。這個Surface描述的就是一個窗口。因此,一旦獲得了對應的Surface,就需要將它綁定到Render Thread中,如下所示:
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(); } } ...... } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。
當前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的實現如下所示:
public class ThreadedRenderer extends HardwareRenderer { ...... @Override boolean initialize(Surface surface) throws OutOfResourcesException { mInitialized = true; ...... boolean status = nInitialize(mNativeProxy, surface); ...... return status; } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。
ThreadedRenderer類的成員函數initialize首先將成員變量mInitialized的值設置為true,表明它接下來已經綁定過Surface到Render Thread了,接著再調用另外一個成員函數nInitialize執行真正的綁定工作。
ThreadedRenderer類的成員函數nInitialize是一個JNI函數,由Native層的函數android_view_ThreadedRenderer_initialize實現,如下所示:
static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject jsurface) { RenderProxy* proxy = reinterpret_cast這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。(proxyPtr); sp 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中,如下所示:
CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) { return (void*) args->context->initialize(args->window); } bool RenderProxy::initialize(const sp& window) { SETUP_TASK(initialize); args->context = mContext; args->window = window.get(); return (bool) postAndWait(task); }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp中。
從前面的分析可以知道,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,如下所示:
bool CanvasContext::initialize(ANativeWindow* window) { setSurface(window); if (mCanvas) return false; mCanvas = new OpenGLRenderer(mRenderThread.renderState()); ...... return true; }這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
CanvasContext類的成員函數initialize通過調用另外一個成員函數setSurface來綁定參數window描述的ANativeWindow到Render Thread中。綁定完成之後,如果CanvasContext類的成員變量mCanvas等於NULL,那麼就說明負責執行Open GL渲染命令的一個OpenGLRenderer對象還沒創建。在這種情況下,就創建一個OpenGLRenderer對象,並且保存在CanvasContext類的成員變量mCanvas中,以便後面可以用來執行Open GL相關操作。
CanvasContext類的成員函數setSurface的實現如下所示:
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(); } ...... }這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
每一個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渲染上下文來,如下所示:
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); }這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
從這裡就可以看到,將一個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在渲染時涉及到的一個重要優化,敬請關注!
【ROM 介紹】 原汁原味官方界面 HTC SENSE3.6 + 4.04系統 華麗麗的,對剩余APP進行大量優化,提升手機使用效率。釋放大量緩存,親測1周,超穩定無BU
手機電量不夠用想必是現在很多智能手機用戶們都比較頭疼的一個問題,而如今在續航方面,不少廠商們也在這方面做出了很大的努力,比如高容量電池、快充技術等。大家都知道OPPO R
【巴士數碼】三星S6 edge太費電怎麼辦?Galaxy S6 edge是三星第一款搭載雙曲面側屏的智能手機,金屬玻璃外觀和雙曲面側屏讓其賺足眼球,不過對於普通用戶來說,
現在對於智能手機用戶來說待機時間一直都是一個硬傷,即使手機上搭載了超大容量的電池,也難逃一天一充的尴尬局面。雖然現