這篇博客參考老羅的文章:http://blog.csdn.net/luoshengyang/article/details/45601143。java
上一篇博客詳細分析了GraphicsStatsService的工做流程,還遺留了一個問題就是各類卡頓類型的信息具體是怎麼統計的。在回答這個問題以前咱們先來看另一個疑問:android
上一篇博客中說過,JankTracker是在initThreadLocal()中被初始化的:canvas
void RenderThread::initThreadLocals() { nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps); mTimeLord.setFrameInterval(frameIntervalNanos); initializeDisplayEventReceiver(); mEglManager = new EglManager(*this); mRenderState = new RenderState(*this); mJankTracker = new JankTracker(frameIntervalNanos); }
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。windows
那麼這initThreadLocal()怎麼被調用的呢?讓咱們從硬件渲染的初始化流程開始提及。數組
根據老羅的博客,Activity組件在建立的過程當中,也就是在其生命週期函數onCreate的調用過程當中,通常會經過調用另一個成員函數 setContentView建立和初始化關聯的窗口視圖,最後經過調用ViewRoot類的成員函數setView完成這一過程。到了Android 4.0以後,ViewRoot類的名字改爲了ViewRootImpl,它們的做用仍然同樣的。 app
Android應用程序UI硬件加速渲染環境的初始化過程是在ViewRootImpl類的成員函數setView開始,以下:ide
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同樣,徹底由應用程序本身來控制它的渲染。oop
基本上咱們是不會將一個Activity窗口看成一個SurfaceView來使用的,所以在ViewRootImpl類的成員變量 mSurfaceHolder將保持爲null值,這樣就會致使ViewRootImpl類的成員函數 enableHardwareAcceleration被調用爲判斷是否須要爲當前建立的Activity窗口啓用硬件加速渲染。post
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; mAttachInfo.mHardwareAccelerationRequested = false; // Don't enable hardware acceleration when the application is in compatibility mode if (mTranslator != null) return; // 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)) { if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.destroy(); } final boolean translucent = attrs.format != PixelFormat.OPAQUE; mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent); if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString()); mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; } } } }
這個函數定義在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。
這裏面的大部分操做都是在處理是否開始硬件加速。雖然硬件加速渲染是個好東西,可是也不是每個須要繪製UI的進程都必需的。這樣作是考慮到兩個因素。第一個因素是並非全部的Canvas API均可以被GPU支持。若是應用程序使用到了這些不被GPU支持的API,那麼就須要禁用硬件加速渲染。第二個因素是支持硬件加速渲染的代價是增長了 內存開銷。例如,只是硬件加速渲染環境初始化這一操做,就要花掉8M的內存。因此有的進程就不適合開啓硬件加速,主要是Persistent進程和System進程,詳細看老羅博客。
上述代碼中重點的是這句:
mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent);
若是當前建立的窗口支持硬件加速渲染,那麼就會調用HardwareRenderer類的靜態成員函數create建立一個 HardwareRenderer對象,而且保存在與該窗口關聯的一個AttachInfo對象的成員變量mHardwareRenderer 中。這個HardwareRenderer對象之後將負責執行窗口硬件加速渲染的相關操做。
那麼這個HardwareRenderer是何方神聖呢?讓咱們繼續往下看。
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。
能夠看到,若是當前設備支持GLES2.0,才能開啓硬件加速,經過ThreadedRenderer來完成這個加速任務。這個類繼承於HardwareRenderer。這意思着Android硬件加速目前只支持GLES?
接下來咱們就繼續分析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); ...... } ...... }
能夠看到,ThreadedRenderer在構造函數中主要乾了三件事,新建了一個RootRenderNode,一個ThreadProxy和調用AtlasInitializer進行資源集的初始化。
這裏咱們先看看第二個,由於這玩意兒上一篇博客咱們好像看過。很明顯這是一個JNI調用,Native層對應的實現是:
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); }
這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
這裏利用了上面生成的rootRenderNode來生成了一個RenderProxy,看來這個rootRenderNode仍是挺重要的,之後有機會再看吧,先看看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。接下來咱們先重點分析RenderThread的初始化過程。
從構造函數中能夠看出,mRenderThread的默認值是RenderThread::getInstance(),這個應該是個單例模式,也就是說在一個Android應用程序進程中,只有一個Render Thread存在。
繼續看RenderThread的構造過程:
RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() ...... { mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); run("RenderThread"); }
這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderThread.cpp中。
這裏一樣也幹了三件事情,新建了一個DispatchFrameCallbacks,一個Looper和調用了run函數。
DispatchFrameCallbacks對象,用來描述一個幀繪製任務。下面描述RenderThread的運行模型時,咱們再詳細分析。RenderThread類的成員變量mLooper指向一個Looper對象,RenderThread經過它來建立一個消息驅動運行模型,相似於Main Thread的消息驅動運行模型。
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中。
在這裏終於看了咱們索要找的initThreadLocals()!很激動有木有~也就是說在硬件初始化渲染的時候,即當一個窗口視圖初始化的時候,其對應的JankTracker就已經被創建起來了,等到窗口視圖真正渲染的時候,用來統計渲染信息。
接上一篇博客,要弄懂渲染信息的收集過程,咱們就得知道JankTracker::addFrame是在哪裏被調用的。首先讓咱們回頭去看看這個函數:
void JankTracker::addFrame(const FrameInfo& frame) { mData->totalFrameCount++; using namespace FrameInfoIndex; // Fast-path for jank-free frames int64_t totalDuration = frame[kFrameCompleted] - frame[kIntendedVsync]; uint32_t framebucket = frameCountIndexForFrameTime( totalDuration, (sizeof(mData->frameCounts) / sizeof(mData->frameCounts[0])) ); //keep the fast path as fast as possible if (CC_LIKELY(totalDuration < mFrameInterval)) { mData->frameCounts[framebucket]++; return; } //exempt this frame, so drop it if (frame[kFlags] & EXEMPT_FRAMES_FLAGS) { return; } mData->frameCounts[framebucket]++; mData->jankFrameCount++; for (int i = 0; i < NUM_BUCKETS; i++) { int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start]; if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) { mData->jankTypeCounts[i]++; } } }
咱們能夠發現,這個函數有一個重要的參數FrameInfo類型的frame!這好像仍是個數組。統計信息就是直接從它身上取出來的。趕忙地,咱們去看看這個類:
class FrameInfo { public: void importUiThreadInfo(int64_t* info); void markSyncStart() { mFrameInfo[FrameInfoIndex::kSyncStart] = systemTime(CLOCK_MONOTONIC); } void markIssueDrawCommandsStart() { mFrameInfo[FrameInfoIndex::kIssueDrawCommandsStart] = systemTime(CLOCK_MONOTONIC); } void markSwapBuffers() { mFrameInfo[FrameInfoIndex::kSwapBuffers] = systemTime(CLOCK_MONOTONIC); } void markFrameCompleted() { mFrameInfo[FrameInfoIndex::kFrameCompleted] = systemTime(CLOCK_MONOTONIC); } int64_t operator[](FrameInfoIndexEnum index) const { if (index == FrameInfoIndex::kNumIndexes) return 0; return mFrameInfo[static_cast<int>(index)]; } int64_t operator[](int index) const { if (index < 0 || index >= FrameInfoIndex::kNumIndexes) return 0; return mFrameInfo[static_cast<int>(index)]; } private: int64_t mFrameInfo[FrameInfoIndex::kNumIndexes]; };
這個類定義在frameworks/base/libs/hwui/FrameInfo.h文件中
能夠看到,FrameInfo裏面有不少markXXXStart的函數,這些函數的功能都是一樣的,就是記錄系統如今的時間放在mFrameInfo的不一樣位置中!很明顯,這是在記錄一個幀每個渲染階段的開始時間,以便後來作卡頓的統計。
同時在同一個文件中,還有另一個類UiFrameInfoBuilder
class ANDROID_API UiFrameInfoBuilder { public: UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) { memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); } UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) { mBuffer[FrameInfoIndex::kVsync] = vsyncTime; mBuffer[FrameInfoIndex::kIntendedVsync] = intendedVsync; return *this; } UiFrameInfoBuilder& addFlag(FrameInfoFlagsEnum flag) { mBuffer[FrameInfoIndex::kFlags] |= static_cast<uint64_t>(flag); return *this; } private: int64_t* mBuffer; };
一樣這裏的setVsync函數記錄了kVsync和kIntendedVsync的時間。所以,如今的問題就變成了這個函數和上面的那些markXXXStart函數是在何時被調用的?
經過搜索,咱們能夠發現,這些函數都是在同一個類中被調用的,那就是CanvasContext!主要涉及到兩個函數:CanvasContext::prepareTree和CanvasContext::draw。下面咱們看看這個三個函數,咱們按時間的前後一個個看:
// Called by choreographer to do an RT-driven animation void CanvasContext::doFrame() { ... int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; UiFrameInfoBuilder(frameInfo) .addFlag(FrameInfoFlags::kRTAnimation) .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(), mRenderThread.timeLord().latestVsync()); TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); prepareTree(info, frameInfo); if (info.out.canDrawThisFrame) { draw(); } }
這個doFrame函數定義在frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
該函數首先新建了一個frameInfo數組來存放幀的各類渲染信息,大小是UI_THREAD_FRAME_INFO_SIZE,實際上是9,具體的定義以下:
#define UI_THREAD_FRAME_INFO_SIZE 9 HWUI_ENUM(FrameInfoIndex, kFlags = 0, kIntendedVsync, kVsync, kOldestInputEvent, kNewestInputEvent, kHandleInputStart, kAnimationStart, kPerformTraversalsStart, kDrawStart, // End of UI frame info kSyncStart, kIssueDrawCommandsStart, kSwapBuffers, kFrameCompleted, // Must be the last value! kNumIndexes );
這個枚舉定義在frameworks/base/libs/hwui/FrameInfo.h中
這個9其實就是下面的HWUI_ENUM枚舉類型前9個的意思,能夠從註釋中看到,這9個是UI幀的信息。
而後doFrame調用了UiFrameInfoBuilder來添加兩個重要的時間,kVsync和kIntendedVsync,這兩個是都是經過調用mRenderThread.timeLord()的相關函數來得到。再接着定義了一個TreeInfo類型的info,並和frameInfo傳給了prepareTree。下面咱們看看prepareTree。
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo) { mRenderThread.removeFrameCallback(this); mCurrentFrameInfo = &mFrames.next(); mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); mCurrentFrameInfo->markSyncStart(); info.damageAccumulator = &mDamageAccumulator; info.renderer = mCanvas; if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) { info.canvasContext = this; } mAnimationContext->startFrame(info.mode); mRootRenderNode->prepareTree(info); mAnimationContext->runRemainingAnimations(info); if (info.canvasContext) { freePrefetechedLayers(); } ... int runningBehind = 0; // TODO: This query is moderately expensive, investigate adding some sort // of fast-path based off when we last called eglSwapBuffers() as well as // last vsync time. Or something. mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); info.out.canDrawThisFrame = !runningBehind; if (info.out.hasAnimations || !info.out.canDrawThisFrame) { if (!info.out.requiresUiRedraw) { // If animationsNeedsRedraw is set don't bother posting for an RT anim // as we will just end up fighting the UI thread. mRenderThread.postFrameCallback(this); } } }
這個函數定義在frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中,
這個函數首先調用mFrames.next()取出了一個mCurrentFrameInfo,而後經過importUiThreadInfo導入傳過來的那個uiFrameInfo,而後調用markSyncStart()記錄了SyncStart的時間點,代表Sync已經開始了,緊接着纔開始進行真正的業務處理,例如處理一些相關的動畫,和調用mRootRenderNode->prepareTree(info)來真正準備ViewTree,這應該是一個上傳視圖到GPU的過程。
prepareTree完成了之後,返回到frameInfo中,若是這個準備是成功的,下面就能夠調用Draw函數立刻開始繪製啦:
void CanvasContext::draw() { ... mCurrentFrameInfo->markIssueDrawCommandsStart(); SkRect dirty; mDamageAccumulator.finish(&dirty); EGLint width, height; mEglManager.beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); } else if (!mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(dirty), width, height); dirty.setEmpty(); } profiler().unionDirty(&dirty); } status_t status; if (!dirty.isEmpty()) { status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); } else { status = mCanvas->prepare(mOpaque); } Rect outBounds; status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); mCanvas->finish(); profiler().markPlaybackEnd(); // Even if we decided to cancel the frame, from the perspective of jank // metrics the frame was swapped at this point mCurrentFrameInfo->markSwapBuffers(); if (status & DrawGlInfo::kStatusDrew) { swapBuffers(); } else { mEglManager.cancelFrame(); } // TODO: Use a fence for real completion? mCurrentFrameInfo->markFrameCompleted(); mJankTracker.addFrame(*mCurrentFrameInfo); mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); profiler().finishFrame(); }
這個函數定義在frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中,
一進來,就調用了markIssueDrawCommandsStart()記錄繪製命令開始的時間,而後作了一大堆的工做,都是在設置一個dirty和mCanvas,弄好了之後調用mCanvas->drawRenderNode、profiler().draw(mCanvas),mCanvas->finish()標誌着繪製工做的完成。
隨後,調用markSwapBuffers()來記錄開始交換緩衝區的時間,這是要開始顯示了麼?接着調用swapBuffers()來進行真正的緩衝交換工做,交換結束之後,這一個幀的繪製就所有完成了,調用markFrameCompleted()來記錄繪製的結束時間,最後這些記錄在mCurrentFrameInfo的幀信息添加到咱們的mJankTracker中,這樣,JankTracker就能統計渲染信息啦。
這樣,大部分的時間節點的記錄已是清除的了,除了兩個:kVsync和kIntendedVsync,前面說過,這兩個函數是由mRenderThread.timeLord().computeFrameTimeNanos()和mRenderThread.timeLord(). latestVsync()取得的。
nsecs_t TimeLord::computeFrameTimeNanos() { // Logic copied from Choreographer.java nsecs_t now = systemTime(CLOCK_MONOTONIC); nsecs_t jitterNanos = now - mFrameTimeNanos; if (jitterNanos >= mFrameIntervalNanos) { nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos; mFrameTimeNanos = now - lastFrameOffset; } return mFrameTimeNanos; }
這個函數定義在frameworks/base/libs/hwui/renderthread/TimeLord.cpp
能夠看到這個函數除了特殊狀況下作了修正以外,是直接返回mFrameTimeNanos的。因此要看一下這個mFrameTimeNanos是怎麼被賦值的。就在同一個文件中:
bool TimeLord::vsyncReceived(nsecs_t vsync) { if (vsync > mFrameTimeNanos) { mFrameTimeNanos = vsync; return true; } return false; }
mFrameTimeNanos被初始化爲0,因此當這個函數被調用的時候,vsync > mFrameTimeNanos成立,因此mFrameTimeNanos被賦值爲vsync,從函數名咱們能夠知道mFrameTimeNanos記錄的就是接收到vsync的時間,即kVsync的時間。