編輯:關於android開發
在上一篇中我們講到,視圖的刷新需要很多步驟,
void SurfaceFlinger::handleMessageRefresh() {
ATRACE_CALL();
preComposition(); //合成前的准備
rebuildLayerStacks();//重新建立layer堆棧
setUpHWComposer();//HWComposer的設定
#ifdef QCOM_BSP
setUpTiledDr();
#endif
doDebugFlashRegions();
doComposition(); //正式合成工作
postComposition(); //合成的後期工作
}
本文將繼續分析這些過程。
invalidate 字面意思就是使無效,更進一步就是當前的buffer已經無限,請刷新界面。
啥也沒干,buffer已經無效,我換下一個,就是handlePageFlip
void SurfaceFlinger::handleMessageInvalidate() {
ATRACE_CALL();
handlePageFlip();
}
再來看這個函數:handlePageFlip

@step1:layer->latchBuffer(visibleRegions) 通過該函數鎖定各layer的緩沖區。可以理解這個函數一定與BufferQueue有關。
status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
mFlinger->mPrimaryDispSync);
上面是latchBuffer的核心語句。SurfaceFlingerConsumer前文已經提過,是client端操作bufferqueue的一個端口。所以這個函數一定是操作bufferqueue的。
// Acquire the next buffer.
// In asynchronous mode the list is guaranteed to be one buffer
// deep, while in synchronous mode we use the oldest buffer.
err = acquireBufferLocked(&item, computeExpectedPresent(dispSync));
if (err != NO_ERROR) {
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
err = NO_ERROR;
} else if (err == BufferQueue::PRESENT_LATER) {
// return the error, without logging
} else {
ALOGE("updateTexImage: acquire failed: %s (%d)",
strerror(-err), err);
}
return err;
}
// We call the rejecter here, in case the caller has a reason to
// not accept this buffer. This is used by SurfaceFlinger to
// reject buffers which have the wrong size
int buf = item.mBuf;
if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, EGL_NO_SYNC_KHR);
return NO_ERROR;
}
// Release the previous buffer.
err = updateAndReleaseLocked(item);
if (err != NO_ERROR) {
return err;
}
看了注釋,基本解釋了大體的過程。
1) 請求新的buffer
2)通過rejecter來判斷申請的buffer是否滿足surfaceflinger的要求。
3)釋放之前的buffer
具體流程可以參考如下:

@step2:SurfaceFlinger:invalidateLayerStack來更新各個區域。
首先來看2個Vsync Rate相關的代碼:
virtual void setVsyncRate(uint32_t count) virtual void requestNextVsync()
當count為1時,表示每個信號都要報告,當count =2 時,表示信號 一個間隔一個報告,當count =0時,表示不自動報告,除非主動觸發requestNextVsync
void SurfaceFlinger::preComposition()
{
bool needExtraInvalidate = false;
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
if (layers[i]->onPreComposition()) {
needExtraInvalidate = true;
}
}
if (needExtraInvalidate) {
signalLayerUpdate();
}
}
代碼很簡單,其實一共就3步,
1)獲取全部的layer
2)每個layer onPrecomposition
3) layer update
bool Layer::onPreComposition() {
mRefreshPending = false;
return mQueuedFrames > 0 || mSidebandStreamChanged;
}
也就是說,當layer裡存放被queue的frame以後,就會出發layer update.
void SurfaceFlinger::signalLayerUpdate() {
mEventQueue.invalidate();
}
最終會調用:
void EventThread::Connection::requestNextVsync() {
mEventThread->requestNextVsync(this);
}
void EventThread::requestNextVsync(
const sp<EventThread::Connection>& connection) {
Mutex::Autolock _l(mLock);
if (connection->count < 0) {
connection->count = 0;
mCondition.broadcast();//通知對vsync感興趣的類
}
}
那麼誰在等待這個broadcast呢?
還是EventThread
void EventThread::onVSyncEvent(nsecs_t timestamp) {
Mutex::Autolock _l(mLock);
mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
mVSyncEvent[0].header.id = 0;
mVSyncEvent[0].header.timestamp = timestamp;
mVSyncEvent[0].vsync.count++;
mCondition.broadcast();
}

前文提到mVisibleRegionsDirty這個變量是標記要刷新的可見區域的,我們按字面意思解釋下:髒的可見區域,顧名思義,這就是要刷新的區域,因為buffer已經“髒”了。
@step1:系統的display可能不止一個,存在與mDisplays中。
@step2:computeVisibleRegions這個函數根據所有的layer狀態,得到2個重要的變量。opaqueRegion & dirtyRegion
dirtyRegion是需要被刷新的。 opaqueRegion 不透明區域,應為layer是按Z-order排序的,左右排在前面的opaqueRegion 會擋住後面的layer。
@step3:Region drawRegion(tr.transform( layer->visibleNonTransparentRegion));程序需要進一步得出每個layer 繪制的區域。
系統的display(顯示器)可能不止一個,但是所有的layer都記錄在layersSortedByZ裡面。記錄每個layer屬於那個display的變量是 hw->getLayerStack()
@step4:將結果保存到hw中。
這裡的不透明區域 是很有意義的一個概念,就是我們在Z-order 上,越靠近用戶的時候,值越大,所以是遞減操作。
用於合成surface的功能模塊可以有2個,OpenGL ES & HWC,它的管理實在HWC裡面實現的。
setUpHWComposer 總的來說就干了3件事情。
1)構造Worklist,並且給DisplayData:list 申請空間
2)填充各layer數據
3)報告HWC(有其他地方決定使用那個)
關鍵地方來了,上面的setUpHWComposer 只是交給HWC來負責顯示,真正顯示的地方就在這裡。
1)如何合成
2)如何顯示到屏幕上

合成的流程大體如上圖。
有了概念後,分析源碼:
void SurfaceFlinger::doComposition() {
ATRACE_CALL();
const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
const sp<DisplayDevice>& hw(mDisplays[dpy]);
if (hw->isDisplayOn()) {
// transform the dirty region into this screen's coordinate space
const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));
// repaint the framebuffer (if needed)
doDisplayComposition(hw, dirtyRegion);
++mActiveFrameSequence;
hw->dirtyRegion.clear();
hw->flip(hw->swapRegion);
hw->swapRegion.clear();
}
// inform the h/w that we're done compositing
hw->compositionComplete();
}
postFramebuffer();
}
變量mRepaintEverything用於說明,是否需要全部重繪所有內容。如果為true的化,那麼dirtyRegion就是displaydevice的 width & height構成的RECT。
否則由dirtyRegion轉換而來。
doDisplayComposition是每個Display來處理,有可能會用到OpenGL 的接口來交換buffer。
hw->compositionComplete(); 通知HWC合成結束了。
postFramebuffer HWC的Set接口調用。

傳入的參數inDirtyRegion,這就是要刷新的“髒”區域,but,我們的刷新機制,決定了必須是矩形的區域。
So,需要一個最小的矩形,能夠包裹inDirtyRegion的區域。
SWAP_RECTANGLE:系統支持軟件層面的部分刷新,就需要計算這個最小矩形。
PARTIAL_UPDATES:硬件層面的部分刷新,同理需要這個最小矩形。
最後就是重繪這個區域。

依次分析:hasGlesComposition需要Open GL來合成的layer,hasHwcComposition需要HWC來合成的layer。
這2各變量不是互斥的,有同時存在需要Open GL layer & HWC layer。
hasHwcComposition在2種情況下是true。
1)layer的類型是HWC_Framebuffer的時候,通常情況下是true。
2)cur ==end 核心實現layer->draw 來完成。
3)cur!=end, 將有hwc來實現。
{
ATRACE_CALL();
if (CC_UNLIKELY(mActiveBuffer == 0)) {
// the texture has not been created yet, this Layer has
// in fact never been drawn into. This happens frequently with
// SurfaceView because the WindowManager can't know when the client
// has drawn the first time.
// If there is nothing under us, we paint the screen in black, otherwise
// we just skip this update.
// figure out if there is something below us
Region under;
const SurfaceFlinger::LayerVector& drawingLayers(
mFlinger->mDrawingState.layersSortedByZ);
const size_t count = drawingLayers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<Layer>& layer(drawingLayers[i]);
if (layer.get() == static_cast<Layer const*>(this))
break;
under.orSelf( hw->getTransform().transform(layer->visibleRegion) );
}
// if not everything below us is covered, we plug the holes!
Region holes(clip.subtract(under));
if (!holes.isEmpty()) {
clearWithOpenGL(hw, holes, 0, 0, 0, 1);
}
return;
}
// Bind the current buffer to the GL texture, and wait for it to be
// ready for us to draw into.
status_t err = mSurfaceFlingerConsumer->bindTextureImage();
if (err != NO_ERROR) {
ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
// Go ahead and draw the buffer anyway; no matter what we do the screen
// is probably going to have something visibly wrong.
}
bool blackOutLayer = isProtected() || (isSecure() && !hw->isSecure());
RenderEngine& engine(mFlinger->getRenderEngine());
if (!blackOutLayer) {
// TODO: we could be more subtle with isFixedSize()
const bool useFiltering = getFiltering() || needsFiltering(hw) || isFixedSize();
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering);
mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix);
if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
/*
* the code below applies the display's inverse transform to the texture transform
*/
// create a 4x4 transform matrix from the display transform flags
const mat4 flipH(-1,0,0,0, 0,1,0,0, 0,0,1,0, 1,0,0,1);
const mat4 flipV( 1,0,0,0, 0,-1,0,0, 0,0,1,0, 0,1,0,1);
const mat4 rot90( 0,1,0,0, -1,0,0,0, 0,0,1,0, 1,0,0,1);
mat4 tr;
uint32_t transform = hw->getOrientationTransform();
if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90)
tr = tr * rot90;
if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H)
tr = tr * flipH;
if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V)
tr = tr * flipV;
// calculate the inverse
tr = inverse(tr);
// and finally apply it to the original texture matrix
const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
}
// Set things up for texturing.
mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight());
mTexture.setFiltering(useFiltering);
mTexture.setMatrix(textureMatrix);
engine.setupLayerTexturing(mTexture);
} else {
engine.setupLayerBlackedOut();
}
drawWithOpenGL(hw, clip, useIdentityTransform);
engine.disableTexturing();
}
裡面關鍵就是drawwithOpenGL,可見是由Open GL來合成layer。
解析BroadcastReceiver之你需要了解的一些東東,broadcastreceiver
解析BroadcastReceiver之你需要了解的一些東東,broadcastreceiver 前些天把四大組件之一的Service扯了一遍,今天就要開始談談它的弟兄
我的Android進階之旅之Android自定義View來實現解析lrc歌詞同步滾動、上下拖動、縮放歌詞等功能
我的Android進階之旅之Android自定義View來實現解析lrc歌詞同步滾動、上下拖動、縮放歌詞等功能 前言 最近有個項目有關於播放音樂時候
使用新版Android Studio檢測內存洩露和性能
使用新版Android Studio檢測內存洩露和性能 內存洩露,是Android開發者最頭疼的事。可能一處小小的內存洩露,都可能是毀於千裡之堤的蟻穴。 怎麼才能檢測內
Android第三方開源對話消息提示框:SweetAlertDialog(sweet-alert-dialog),
Android第三方開源對話消息提示框:SweetAlertDialog(sweet-alert-dialog),Android第三方開源對話消息提示框:SweetAle
伴隨ListView、RecyclerView、ScrollView滾動滑入滑出小圖標--第三方開源--FloatingActionButton,recyclerviewscroll
伴隨ListView、RecyclerView、ScrollView滾動