中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

最好網(wǎng)站建設(shè)公司排名寧波優(yōu)化網(wǎng)頁(yè)基本流程

最好網(wǎng)站建設(shè)公司排名,寧波優(yōu)化網(wǎng)頁(yè)基本流程,dedecms企業(yè)網(wǎng)站,公司網(wǎng)站建設(shè)設(shè)計(jì)公司排名一、Surface 概述 OpenGL ES/Skia定義了一組繪制接口的規(guī)范,為什么能夠跨平臺(tái)? 本質(zhì)上需要與對(duì)應(yīng)平臺(tái)上的本地窗口建立連接。也就是說(shuō)OpenGL ES負(fù)責(zé)輸入了繪制的命令,但是需要一個(gè) “畫(huà)布” 來(lái)承載輸出結(jié)果,最終展示到屏幕。這個(gè)…

一、Surface 概述

OpenGL ES/Skia定義了一組繪制接口的規(guī)范,為什么能夠跨平臺(tái)? 本質(zhì)上需要與對(duì)應(yīng)平臺(tái)上的本地窗口建立連接。也就是說(shuō)OpenGL ES負(fù)責(zé)輸入了繪制的命令,但是需要一個(gè) “畫(huà)布” 來(lái)承載輸出結(jié)果,最終展示到屏幕。這個(gè)畫(huà)布就是本地窗口。

因此,每個(gè)平臺(tái)的有著不一樣的本地窗口的實(shí)現(xiàn)。Android平臺(tái)上是 ANativeWindow。

疑問(wèn):

  • 那么如何將OpenGL本地化? 通過(guò) EGL來(lái)對(duì)OpenGL ES來(lái)進(jìn)行配置。關(guān)鍵點(diǎn)就是提供本地化窗口。
  • 本地化窗口的作用是什么? 本地窗口是OpenGL ES和 物理屏幕之間的橋梁。

1.1 Android本地窗口簡(jiǎn)述

Android圖形系統(tǒng)提供的本地窗口,可以分為兩類(lèi):

  • FrameBufferNativeWindow

面對(duì)SF(SurfaceFlinger)。它通過(guò)HAL層的Gralloc系統(tǒng)調(diào)用(alloc/free)來(lái)分配內(nèi)核中的FrameBuffer幀緩沖區(qū)。 這個(gè)幀緩沖區(qū)就代表了物理屏幕(fb驅(qū)動(dòng)節(jié)點(diǎn),表示屏幕數(shù)。如fb0主屏幕、fb1等)。 FrameBuffer的數(shù)量一般情況下是2,也就是雙緩沖。當(dāng)然還有三倍緩沖。

  • Surface

面向應(yīng)用程序。對(duì)應(yīng)的是內(nèi)存中一塊緩沖區(qū),稱(chēng)為:GraphicBuffer。是由SF來(lái)進(jìn)行分配。app從SF中獲取一塊GraphicBuffer, 通過(guò)OpenGL/Skia將圖形數(shù)據(jù)繪制(軟件/硬件)到GraphicBuffer上。最終SF會(huì)把各個(gè)應(yīng)用的GraphicBuffer數(shù)據(jù)進(jìn)行合成,最終 通過(guò) FrameBufferNativeWindow 輸出到屏幕上。

有了一個(gè)整體的概念,接下來(lái)就好理解很多。

二、引出SurfaceSession

2.1 從WindowManagerImpl的addView()說(shuō)起

app:
WindowManagerImpl.addView()WindowManagerGlobal.addView()ViewRootImpl的setView()IWindowSession.addToDisplay()
?
WMS:new WindowStateWindowState.attach()session.windowAddedLocked()new SurfaceSession()

復(fù)制代碼

view添加到window的過(guò)程中, 從WindowManagerImpl 的 addView(),到WindowManagerGlobal(構(gòu)造方法中會(huì)在system server 進(jìn)程中創(chuàng)建一個(gè)Session對(duì)象)的addView()。最后會(huì)調(diào)用 ViewRootImpl的setView()方法。 內(nèi)部會(huì)調(diào)用 IWindowSession 的addToDisplay() 方法。IWindowSession是WMS提供的一個(gè)binder服務(wù)(實(shí)現(xiàn)類(lèi)就是Session)。

2.2 IWindowSession.windowAddedLocked()

內(nèi)部會(huì)創(chuàng)建一個(gè)WindowState 對(duì)象。 調(diào)用 WindowState的 attach()方法。最終調(diào)到Session中的windowAddedLocked(),會(huì)創(chuàng)建 一個(gè)SurfaceSession對(duì)象。這就是我們要找的的跟SurfaceFlinger建立聯(lián)系的地方。

 SurfaceSession mSurfaceSession;
void windowAddedLocked(String packageName) {mPackageName = packageName;mRelayoutTag = "relayoutWindow: " + mPackageName;if (mSurfaceSession == null) {// 一個(gè)進(jìn)程只有一個(gè)session,因此也只創(chuàng)建一次 SurfaceSession 對(duì)象
?// 創(chuàng)建 SurfaceSession 對(duì)象mSurfaceSession = new SurfaceSession();
?// 每個(gè)session 都存入WMS中的mService.mSessions.add(this);
?if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {mService.dispatchNewAnimatorScaleLocked(this);}}mNumWindow++; // 進(jìn)程中所有窗口的數(shù)量+1
}

復(fù)制代碼

一個(gè)應(yīng)用進(jìn)程對(duì)應(yīng)一個(gè)Session對(duì)象,一個(gè)Session對(duì)象對(duì)應(yīng)一個(gè)SurfaceSession。 WMS會(huì)把 這個(gè)Session 存儲(chǔ)起來(lái)。也就是說(shuō)WMS 會(huì)把所有跟SurfaceFlinger保持連接狀態(tài)的應(yīng)用Session存儲(chǔ)起來(lái)。

2.3 SurfaceSession 創(chuàng)建過(guò)程

這個(gè)類(lèi)的實(shí)例代表了和SurfaceFlinger的一個(gè)連接。我們可以通過(guò)它 創(chuàng)建一個(gè)或多個(gè) Surface 對(duì)象。

2.3.1 構(gòu)造方法

> SurfaceSession.java
private long mNativeClient; // SurfaceComposerClient*
?
public SurfaceSession() {//native 方法mNativeClient = nativeCreate();
}
?
> frameworks/base/core/jni/android_view_SurfaceSession.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz) {// 新建一個(gè) SurfaceComposerClient 對(duì)象SurfaceComposerClient* client = new SurfaceComposerClient();client->incStrong((void*)nativeCreate);//返回SurfaceComposerClient對(duì)象的引用到j(luò)ava層。return reinterpret_cast<jlong>(client);
}

復(fù)制代碼

SurfaceComposerClient 是什么呢?

2.3.2 SurfaceComposerClient

在 SurfaceComposerClient第一次被引用的時(shí)候會(huì)走onFirstRef()方法。

> frameworks/native/libs/gui/SurfaceComposerClient.cpp
void SurfaceComposerClient::onFirstRef() {//創(chuàng)建sf代理binder對(duì)象sf,類(lèi)型為 ISurfaceComposersp<ISurfaceComposer> sf(ComposerService::getComposerService());if (sf != nullptr && mStatus == NO_INIT) {sp<ISurfaceComposerClient> conn;//創(chuàng)建一個(gè) ISurfaceComposerClient 對(duì)象,用來(lái)跨進(jìn)程調(diào)用conn = sf->createConnection();if (conn != nullptr) {mClient = conn;mStatus = NO_ERROR;}}
}

復(fù)制代碼

  • ISurfaceComposer 實(shí)現(xiàn)類(lèi)就是 SurfaceFlinger對(duì)象。在server進(jìn)程的代理對(duì)象是 ComposerService。This class defines the Binder IPC interface for accessing various SurfaceFlinger features.
  • 通過(guò)SF.createConnection(),創(chuàng)建一個(gè) ISurfaceComposerClient 對(duì)象 mClient,用來(lái)跨進(jìn)程調(diào)用。

那么 ISurfaceComposerClient的實(shí)現(xiàn)類(lèi)是哪個(gè)呢? 繼續(xù)看看 SF.createConnection()。

2.3.3 SurfaceFlinger.createConnection()

注意,此時(shí)是在SF進(jìn)程。
> frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {// new client對(duì)象。return initClient(new Client(this));
}
static sp<ISurfaceComposerClient> initClient(const sp<Client>& client) {status_t err = client->initCheck();if (err == NO_ERROR) {// 返回該對(duì)象return client;}return nullptr;
}
> frameworks/native/services/surfaceflinger/Client.h
class Client : public BnSurfaceComposerClient{...class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {...

復(fù)制代碼

原來(lái),ISurfaceComposerClient的實(shí)現(xiàn)類(lèi)就是 SF中定義的 Client。也是一個(gè)binder服務(wù)。 我們回到 SurfaceComposerClient 類(lèi),它持有 ISurfaceComposerClient的binder引用 mClient。通過(guò) mClient實(shí)現(xiàn)與SF通信。

2.3 小結(jié)

  • Session 類(lèi)中,創(chuàng)建了一個(gè) SurfaceSession 對(duì)象,內(nèi)部引用c++層的 SurfaceComposerClient 對(duì)象。
  • SurfaceComposerClient 對(duì)象是通過(guò)SF創(chuàng)建的另一個(gè)binder服務(wù)。減輕SF的工作量。
  • SurfaceComposerClient 對(duì)象則通過(guò) mClient成員(ISurfaceComposerClient)代理binder,后續(xù)用來(lái)創(chuàng)建 Surface。

Surface繪制原理

Surface的Buffer是從哪里來(lái)的?

源碼:frameworks/base/core/java/android/view/ViewRootImpl.java View觸發(fā)繪制是通過(guò)requestLayout()函數(shù)或者setLayoutParms()函數(shù):

performTravsersals()函數(shù)實(shí)現(xiàn):

private void performTraversals() {……performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);performLayout(lp, mWidth, mHeight);performDraw();……
}

perfomrDraw()函數(shù)調(diào)用draw()函數(shù)開(kāi)始繪制:

private void performDraw() {……boolean canUseAsync = draw(fullRedrawNeeded);……
}

ViewRootImpl.draw()函數(shù)實(shí)現(xiàn):

private boolean draw(boolean fullRedrawNeeded) {Surface surface = mSurface;……if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty, surfaceInsets)) {return false;}……return useAsyncReport;
}

drawSoftware()軟件繪制,默認(rèn)是軟件繪制。

drawSoftware()函數(shù)軟件繪制流程:

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
?// Draw with software renderer.final Canvas canvas;……canvas = mSurface.lockCanvas(dirty);……mView.draw(canvas);……surface.unlockCanvasAndPost(canvas);
?
}

獲取:通過(guò)lockCanvas函數(shù)獲取Canvas對(duì)象,

繪制:再通過(guò)mView.draw(canvas)函數(shù)向在canvas上繪制,

提交:最后通過(guò)surface.unlockCanvasAndPost(canvas)函數(shù)提交Canvas。

通過(guò)lockCanvas()函數(shù)獲取Canvas對(duì)象,lockCanvas()函數(shù)如何獲取Canvas對(duì)象。

lockCanvas()函數(shù)實(shí)現(xiàn):

/*** Gets a {@link Canvas} for drawing into this surface.** After drawing into the provided {@link Canvas}, the caller must* invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.** @param inOutDirty A rectangle that represents the dirty region that the caller wants* to redraw.  This function may choose to expand the dirty rectangle if for example* the surface has been resized or if the previous contents of the surface were* not available.  The caller must redraw the entire dirty region as represented* by the contents of the inOutDirty rectangle upon return from this function.* The caller may also pass <code>null</code> instead, in the case where the* entire surface should be redrawn.* @return A canvas for drawing into the surface.** @throws IllegalArgumentException If the inOutDirty rectangle is not valid.* @throws OutOfResourcesException If the canvas cannot be locked.*/
public Canvas lockCanvas(Rect inOutDirty)throws Surface.OutOfResourcesException, IllegalArgumentException {synchronized (mLock) {checkNotReleasedLocked();if (mLockedObject != 0) {// Ideally, nativeLockCanvas() would throw in this situation and prevent the// double-lock, but that won't happen if mNativeObject was updated.  We can't// abandon the old mLockedObject because it might still be in use, so instead// we just refuse to re-lock the Surface.throw new IllegalArgumentException("Surface was already locked");}mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);return mCanvas;}
}

通過(guò)Native層android_view_Surface.cpp的nativeLockCanvas(mNativeObject, mCanvas, inOutDirty)函數(shù)獲取,mNativeOjbect參數(shù)是Java層的Surface在Native層對(duì)應(yīng)的Surface對(duì)象的指針。mCanvas是Surface的變量,在lockCanvas()函數(shù)調(diào)用時(shí)mCanvas是空的。

在調(diào)用nativeLockCanvas()函數(shù)后mCanvas就有值了,最后返回mCanvas對(duì)象。

static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {// (1)sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));……// (2)ANativeWindow_Buffer buffer;status_t err = surface->lock(&buffer, dirtyRectPtr);……// (3)graphics::Canvas canvas(env, canvasObj);canvas.setBuffer(&buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));……// (4)// Create another reference to the surface and return it.  This reference// should be passed to nativeUnlockCanvasAndPost in place of mNativeObject,// because the latter could be replaced while the surface is locked.sp<Surface> lockedSurface(surface);lockedSurface->incStrong(&sRefBaseOwner);return (jlong) lockedSurface.get();
}

(1) 獲取Native層的Surface對(duì)象。

(2) 獲取Native層的Surface對(duì)象的Buffer。

(3) 將Buffer設(shè)置給Canvas,這里Canvas就有一個(gè)Buffer了。在每次都申請(qǐng)一個(gè)新的Buffer給Canvas對(duì)象。

(4) 向Java層返回Native的Surface對(duì)象,這里返回的是一個(gè)Long型數(shù)據(jù),這個(gè)Long型數(shù)據(jù)是Surface指針。

獲取Buffer實(shí)現(xiàn),surface -> lock(&buffer, ),這里傳入Buffer地址:

源碼:frameworks/native/libs/gui/Surface.cpp

status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{……// (1)ANativeWindowBuffer* out;status_t err = dequeueBuffer(&out, &fenceFd);……// (2)sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));……// (3)void* vaddr;status_t res = backBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, newDirtyRegion.bounds(), &vaddr, fenceFd);……// (4)mLockedBuffer = backBuffer;// (5)outBuffer->bits   = vaddr;……
}

(1) 獲取dequeueBuffer()函數(shù)在SurfaceFlinger的Buffer隊(duì)列中獲取Buffer。

(2) 創(chuàng)建GraphicBuffer對(duì)象backBuffer。在SharedBufferStack中有雙緩沖機(jī)制,分別為FontBuffer和BackBuffer。

FontBuffer:代表當(dāng)前將顯示在屏幕的Buffer數(shù)據(jù)。屬于前臺(tái)Buffer。 BackBuffer:代表繪制的Buffer數(shù)據(jù),是準(zhǔn)備渲染的數(shù)據(jù)Buffer。屬于后臺(tái)Buffer。   (3) 鎖定Buffer,并將Buffer地址返回,將返回的Buffer地址給Canvas的Buffer。

(4) 切換Buffer,將后臺(tái)BackBuffer切換到前臺(tái),交給mLockedBuffer。FontBuffer的變量就是mLockedBuffer。

(5) 將vaddr賦值給outBuffer->bits,bits最后賦值給Canvas的Buffer,就是BkBitmap,作為Canvas的緩沖區(qū)。

dequeteBuffer()是如何獲取Buffer的,dequeteBuffer()函數(shù)實(shí)現(xiàn):

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {……int buf = -1;// (1)status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,reqFormat, reqUsage, &mBufferAge,enableFrameTimestamps ? &frameTimestamps: nullptr);……// (2)sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);……// (3)if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {if (mReportRemovedBuffers && (gbuf != nullptr)) {mRemovedBuffers.push_back(gbuf);}result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);if (result != NO_ERROR) {ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);mGraphicBufferProducer->cancelBuffer(buf, fence);return result;}}……// (4)*buffer = gbuf.get();
}

(1) 通過(guò)mGaphicBufferProducer->dequeteBuffer()函數(shù)在遠(yuǎn)端的Buffer slots中獲得一個(gè)空閑的Buffer,返回遠(yuǎn)端Buffer地址指針。

(2) 通過(guò)gbp從本地Buffer Slots里獲取Buffer,在(1)中從遠(yuǎn)端,在(2)中從本地,這里涉及遠(yuǎn)端Buffer queue與本地Buffer queue同步問(wèn)題。

(3) 負(fù)責(zé)本地Buffer與遠(yuǎn)端Buffer同步,遠(yuǎn)端返回的Buffer的result是BUFFER_NEEDS_REALLOCATION或者本地的gbp是null,通過(guò)gbp的requestBuffer()獲取新的遠(yuǎn)端Buffer指針地址。mGaphicBufferProducer->requestBuffer()函數(shù)。

(4) 獲取Buffer。

Surface的Buffer是如何提交的?

通過(guò)surface.unlockCanvasAndPost(canvas)向遠(yuǎn)端提交更新的Buffer,unlockCanvasAndPost()函數(shù)實(shí)現(xiàn):

/*** Posts the new contents of the {@link Canvas} to the surface and* releases the {@link Canvas}.** @param canvas The canvas previously obtained from {@link #lockCanvas}.*/
public void unlockCanvasAndPost(Canvas canvas) {synchronized (mLock) {unlockSwCanvasAndPost(canvas);}
}
?
private void unlockSwCanvasAndPost(Canvas canvas) {try {nativeUnlockCanvasAndPost(mLockedObject, canvas);} finally {nativeRelease(mLockedObject);mLockedObject = 0;}
}

最后調(diào)用到Native層的nativeUnlockCanvasAndPost(mLockedObject, canvas)。

Native層,nativeUnlockCanvasAndPost()函數(shù)實(shí)現(xiàn):

static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,jlong nativeObject, jobject canvasObj) {// (1)sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
?// (2)// detach the canvas from the surfacegraphics::Canvas canvas(env, canvasObj);canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);// (3)// unlock surfacestatus_t err = surface->unlockAndPost();
}

(1) 獲取對(duì)應(yīng)Java層的Native層的Surface對(duì)象。

(2) 獲取對(duì)應(yīng)Java層的Native層的Canvas對(duì)象。

(3) 將本地Buffer更新到遠(yuǎn)端的Buffer queue中。

Native層更新遠(yuǎn)端Buffer queue,surface->unlockAndPost()函數(shù)實(shí)現(xiàn):

源碼:frameworks/native/libs/gui/Surface.cpp

status_t Surface::unlockAndPost()
{if (mLockedBuffer == nullptr) {ALOGE("Surface::unlockAndPost failed, no locked buffer");return INVALID_OPERATION;}
?int fd = -1;status_t err = mLockedBuffer->unlockAsync(&fd);ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);err = queueBuffer(mLockedBuffer.get(), fd);ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",mLockedBuffer->handle, strerror(-err));mPostedBuffer = mLockedBuffer;mLockedBuffer = nullptr;return err;
}

通過(guò)函數(shù)queueBuffer(mLockedBuffer.get(), )函數(shù)實(shí)現(xiàn)更新:

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {……// (1)int i = getSlotFromBufferLocked(buffer);……// (2)status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);……return err;
}

(1) 獲取Buffer的index。

(2) 通過(guò)mGraphicBufferProducer->queueBuffer(i, )函數(shù),將本地的Buffer同步到遠(yuǎn)端Buffer queue中。

以上為車(chē)載技術(shù)中Window Display板塊的surface的繪制過(guò)程與原理;更多車(chē)載技術(shù)參考,可點(diǎn)擊《車(chē)載技術(shù)手冊(cè)》查看類(lèi)目學(xué)習(xí)。

Android車(chē)載學(xué)習(xí)手冊(cè)?docs.qq.com/doc/DUldvclB5d0JZSVFn

三、總結(jié)

http://m.risenshineclean.com/news/65996.html

相關(guān)文章:

  • 虛擬資源站碼支付wordpress全網(wǎng)營(yíng)銷(xiāo)推廣公司
  • 淄博網(wǎng)站建設(shè)-中國(guó)互聯(lián)中小企業(yè)管理培訓(xùn)班
  • 西城專(zhuān)業(yè)網(wǎng)站建設(shè)公司哪家好如何進(jìn)行網(wǎng)站性能優(yōu)化
  • 網(wǎng)站整站下載帶數(shù)據(jù)庫(kù)后臺(tái)的方法濟(jì)南百度競(jìng)價(jià)開(kāi)戶(hù)
  • 想自己做網(wǎng)站嗎培訓(xùn)班有哪些課程
  • 專(zhuān)門(mén)做app的原型網(wǎng)站付費(fèi)推廣平臺(tái)有哪些
  • 科技公司網(wǎng)站設(shè)計(jì)公司深圳優(yōu)化公司義高粱seo
  • 網(wǎng)站沒(méi)完善做cdn的后果吸引人氣的營(yíng)銷(xiāo)方案
  • 泰安有口碑的企業(yè)建站公司免費(fèi)推廣軟件
  • 專(zhuān)做美容師招聘網(wǎng)站網(wǎng)絡(luò)管理系統(tǒng)
  • 響應(yīng)式網(wǎng)站建設(shè)推廣百度推廣銷(xiāo)售員的工作內(nèi)容
  • 成都科技網(wǎng)站建設(shè)電話(huà)多少公司網(wǎng)站制作要多少錢(qián)
  • 那家財(cái)經(jīng)網(wǎng)站做的好2020十大網(wǎng)絡(luò)熱詞
  • 成功案例 網(wǎng)站網(wǎng)站推廣平臺(tái)有哪些
  • 網(wǎng)站建設(shè)獨(dú)立高端網(wǎng)站建設(shè)制作
  • 做國(guó)外市場(chǎng)哪個(gè)網(wǎng)站好口碑營(yíng)銷(xiāo)成功案例有哪些
  • 淄博周村網(wǎng)站建設(shè)哪家好杭州網(wǎng)絡(luò)推廣
  • 做網(wǎng)站設(shè)計(jì)提成賺錢(qián)嗎百度百度一下官網(wǎng)
  • 動(dòng)態(tài)網(wǎng)站開(kāi)發(fā)心得體會(huì)百度推廣后臺(tái)登陸官網(wǎng)
  • 漳州網(wǎng)站建設(shè)點(diǎn)擊博大選百度云
  • 第3章?tīng)I(yíng)銷(xiāo)型企業(yè)網(wǎng)站建設(shè)開(kāi)魯seo網(wǎng)站
  • 2020年新聞大事件長(zhǎng)春網(wǎng)站seo公司
  • 蘭州網(wǎng)站建設(shè)公司排名網(wǎng)絡(luò)營(yíng)銷(xiāo)推廣的渠道有哪些
  • 上海閔行區(qū)租房?jī)r(jià)格杭州seo搜索引擎優(yōu)化
  • 網(wǎng)站怎么做反鏈網(wǎng)絡(luò)營(yíng)銷(xiāo)推廣合同
  • 手機(jī)網(wǎng)站建設(shè)方案doc微信朋友圈推廣平臺(tái)
  • 網(wǎng)站維護(hù)是什么意思b站推廣網(wǎng)站入口mmm
  • 怎么做網(wǎng)站搜索引擎利于搜索競(jìng)價(jià)惡意點(diǎn)擊報(bào)案
  • 展示型型網(wǎng)站建設(shè)營(yíng)銷(xiāo)案例分析報(bào)告模板
  • 有什么好的免費(fèi)網(wǎng)站做教育宣傳沈陽(yáng)seo排名優(yōu)化教程