Android 視圖系統(tǒng)與渲染機(jī)制詳解

一、視圖系統(tǒng)核心組件與職責(zé)

1.1 Window 體系

  • Window 是一個(gè)抽象類,通過控制 DecorView 提供標(biāo)準(zhǔn) UI 方案,例如:

    • 背景
    • 標(biāo)題
    • 虛擬按鍵等
  • PhoneWindowWindow唯一實(shí)現(xiàn)類,其作用包括:

    • 完善 Window 的功能;
    • 提供事件的中轉(zhuǎn)。

【注意】
PhoneWindow 只是提供標(biāo)準(zhǔn) UI 方案,與窗口不等價(jià)。

1.2 WindowManager 體系

  • WindowManager 是一個(gè)接口,繼承自 ViewManager 接口,提供 View 的基本操作方法(如 addViewremoveView、updateViewLayout)。

  • WindowManagerImpl 實(shí)現(xiàn)了 WindowManager 接口,其內(nèi)部通過組合方式持有 WindowManagerGlobal,用于實(shí)際操作 View。

  • WindowManagerGlobal 是一個(gè)全局單例,其核心能力包括:

    • 內(nèi)部可通過 ViewRootImpl 將 View 添加至窗口中。

1.3 ViewRootImpl —— 視圖系統(tǒng)的樞紐

  • ViewRootImpl所有 View 的 Parent(邏輯上的根節(jié)點(diǎn)),其核心職責(zé)包括:

    • 管理 View 的繪制流程;
    • 負(fù)責(zé)窗口的開辟;
    • 視圖系統(tǒng)最關(guān)鍵的一環(huán)。
  • IWindowSession

    • IWindowSession 類型的 AIDL 接口;
    • 可通過 Binder IPC 通知 WMS(WindowManagerService)開辟窗口。

【補(bǔ)充】
盡管開發(fā)者日常只接觸 Activity.setContentView(),但錯(cuò)綜復(fù)雜的視圖系統(tǒng)基本都隱藏在 Activity 內(nèi)部,開發(fā)者只需基于模板方法即可開發(fā)。

1.4 Android 視圖體系的本質(zhì)理解

在深入技術(shù)細(xì)節(jié)前,需明確 Android 視圖系統(tǒng)的分層抽象邏輯。

  • 一切視圖均由 Canvas 而來
    所有 UI 元素的繪制最終都通過 Canvas 的繪圖指令(如 drawRect, drawText)完成;Canvas 是連接應(yīng)用邏輯與 GPU 渲染的橋梁。

  • View 的出現(xiàn)是為了提供視圖模板,用來提升開發(fā)效率
    若無 View,開發(fā)者需手動(dòng)管理每個(gè)像素的繪制、事件分發(fā)、布局計(jì)算。View 封裝了通用行為(measure/layout/draw/事件處理),形成可復(fù)用的組件模型。

  • 窗口可以讓 View 有條不紊地顯示
    “窗口”是操作系統(tǒng)層面的概念,代表一塊獨(dú)立的屏幕區(qū)域。Android 通過 Window 抽象將 View 樹與底層顯示服務(wù)(SurfaceFlinger)關(guān)聯(lián),確保多個(gè) App 的界面互不干擾。

  • Activity 給每個(gè)窗口增加生命周期,讓窗口切換更加優(yōu)雅
    Activity 不僅承載 UI,更通過 onCreate/onResume/onPause 等回調(diào)協(xié)調(diào)窗口的創(chuàng)建、激活、暫停與銷毀,實(shí)現(xiàn)多任務(wù)間平滑過渡。

  • PhoneWindow 只是提供些標(biāo)準(zhǔn)的 UI 方案,與窗口不等價(jià)
    PhoneWindowWindow 的具體實(shí)現(xiàn),負(fù)責(zé)生成包含狀態(tài)欄、標(biāo)題欄、內(nèi)容區(qū)的 DecorView 結(jié)構(gòu),但它本身不是窗口實(shí)體,僅是 UI 模板提供者。

  • 可通過 WindowManagerView 添加到窗口
    WindowManager 是客戶端接口,調(diào)用其 addView() 可將任意 View(如 Dialog、Toast)附加到當(dāng)前窗口的視圖樹中。

  • ViewRootImpl 才是開辟窗口的那個(gè)角色,并管理 View 的繪制,是視圖系統(tǒng)最關(guān)鍵的一環(huán)
    ViewRootImpl 負(fù)責(zé):

    • 通過 IWindowSession 向 WMS(WindowManagerService)申請(qǐng)窗口;
    • 建立 SurfaceCanvas 的綁定;
    • 驅(qū)動(dòng) View 的 measure/layout/draw 三大流程;
    • 處理輸入事件分發(fā)。

    【補(bǔ)充】
    沒有 ViewRootImplDecorView 僅是一棵內(nèi)存中的 View 樹,無法顯示在屏幕上。

  • 錯(cuò)綜復(fù)雜的視圖系統(tǒng)基本都隱藏在 Activity 內(nèi)部,開發(fā)者只需基于模板方法即可開發(fā)
    開發(fā)者通常只調(diào)用 setContentView() 和重寫生命周期方法,而 Activity 內(nèi)部自動(dòng)完成 PhoneWindow 創(chuàng)建、DecorView 構(gòu)建、ViewRootImpl 關(guān)聯(lián)等復(fù)雜流程。

【補(bǔ)充類比】
可將整個(gè)體系類比為“舞臺(tái)劇”:

  • Canvas 是畫筆;
  • View 是演員(自帶臺(tái)詞和動(dòng)作腳本);
  • Window 是舞臺(tái)框架;
  • PhoneWindow 是舞臺(tái)布景師(提供標(biāo)準(zhǔn)背景板);
  • Activity 是導(dǎo)演(控制演出節(jié)奏);
  • ViewRootImpl 是舞臺(tái)監(jiān)督(協(xié)調(diào)演員上臺(tái)、燈光、音效);
  • SurfaceFlinger 是最終的放映機(jī)。

二、View 的工作原理

2.1 setContentView 與 DecorView 創(chuàng)建

  1. 當(dāng)調(diào)用 ActivitysetContentView() 方法后:

    • 會(huì)調(diào)用 PhoneWindow 類的 setContentView 方法;
    • PhoneWindow 是抽象類 Window 的實(shí)現(xiàn)類;
    • Window 類用于描述 Activity 視圖最頂端的窗口顯示和行為操作。
  2. PhoneWindowsetContentView 方法中:

    • 最終會(huì)生成 DecorView 對(duì)象;
    • DecorViewPhoneWindow 的內(nèi)部類。

2.2 ViewRootImpl 的紐帶作用

  • ViewRoot 的實(shí)現(xiàn)類是 ViewRootImpl;
  • 它是連接 WindowManagerDecorView紐帶;
  • View 的三大流程(measure、layout、draw)均通過 ViewRoot 完成。
  1. ActivityThread 中:
    • 當(dāng) Activity 對(duì)象創(chuàng)建完畢后,會(huì)將 DecorView 添加到 Window 中;
    • 同時(shí)創(chuàng)建 ViewRootImpl 對(duì)象;
    • 并將 ViewRootImplDecorView 建立關(guān)聯(lián)。
DecorView添加窗口到View的過程

2.3 MeasureSpec 機(jī)制

MeasureSpec 是一個(gè) 32 位的 int 值,用于在父容器與子 View 之間傳遞測(cè)量約束。其結(jié)構(gòu)如下:

位段 長(zhǎng)度 含義
高 2 位 2 bits SpecMode:測(cè)量模式
低 30 位 30 bits SpecSize:測(cè)量尺寸(單位:像素)

可通過 MeasureSpec.makeMeasureSpec(size, mode) 構(gòu)造,或通過 MeasureSpec.getMode(measureSpec) / MeasureSpec.getSize(measureSpec) 解析。

2.3.1 三種 SpecMode 詳解

Mode 含義 對(duì)應(yīng) LayoutParams 行為說明
EXACTLY 精確模式 match_parent 或 具體數(shù)值(如 100dp 父容器已確定子 View 的確切大小,子 View 必須使用該尺寸。
AT_MOST 至多模式 wrap_content 子 View 可以在其最大允許范圍內(nèi)自由決定尺寸,但不能超過 SpecSize。
UNSPECIFIED 未指定模式 ——(系統(tǒng)內(nèi)部使用) 父容器不對(duì)子 View 施加任何限制,子 View 可按需返回任意尺寸(常用于 ScrollView 內(nèi)部測(cè)量)。

【注意】
UNSPECIFIED 模式極少在普通布局中出現(xiàn),主要由系統(tǒng)組件(如 ListView、RecyclerView 的 item 測(cè)量)使用。

2.3.2 DecorView 的 MeasureSpec 如何確定?

DecorView 作為頂級(jí) View,其 MeasureSpec 不由父容器決定,而是由窗口尺寸自身的 LayoutParams 共同計(jì)算得出。

  • 窗口尺寸來源
    由 WMS 在 relayoutWindow() 中分配,反映當(dāng)前 Activity 窗口的實(shí)際可用區(qū)域(扣除狀態(tài)欄、導(dǎo)航欄等)。

  • 計(jì)算規(guī)則(簡(jiǎn)化):

    // 假設(shè)窗口寬高為 windowWidth, windowHeight
    // DecorView 的 LayoutParams 為 lp
    int widthMeasureSpec = 
        (lp.width == MATCH_PARENT) ? 
            MeasureSpec.makeMeasureSpec(windowWidth, EXACTLY) :
        (lp.width >= 0) ?
            MeasureSpec.makeMeasureSpec(lp.width, EXACTLY) :
            MeasureSpec.makeMeasureSpec(windowWidth, AT_MOST);
    

2.3.3 普通 View 的 MeasureSpec 如何確定?

對(duì)于非頂級(jí) View,其 MeasureSpec父容器的 MeasureSpec自身的 LayoutParams 共同決定。Android 提供了 getChildMeasureSpec() 方法封裝此邏輯。

getChildMeasureSpec 規(guī)則表(父容器 mode × 子 View LayoutParams)
父容器 SpecMode 子 View LayoutParams 子 View MeasureSpec Mode 子 View MeasureSpec Size
EXACTLY match_parent EXACTLY 父容器 size
EXACTLY 具體數(shù)值(如 100dp) EXACTLY 指定數(shù)值
EXACTLY wrap_content AT_MOST 父容器 size
AT_MOST match_parent AT_MOST 父容器 size
AT_MOST 具體數(shù)值 EXACTLY 指定數(shù)值
AT_MOST wrap_content AT_MOST 父容器 size
UNSPECIFIED match_parent UNSPECIFIED 0
UNSPECIFIED 具體數(shù)值 EXACTLY 指定數(shù)值
UNSPECIFIED wrap_content UNSPECIFIED 0

【補(bǔ)充】
此表解釋了為何 wrap_contentEXACTLY 父容器中表現(xiàn)為“最多占滿”,而在 AT_MOST 中同樣受限于父容器剩余空間。

2.3.4 MeasureSpec 與 LayoutParams 的映射關(guān)系

LayoutParams.width 對(duì)應(yīng) MeasureSpec Mode
MATCH_PARENT (-1) EXACTLY(若父容器為 EXACTLY)或 AT_MOST(若父容器為 AT_MOST
WRAP_CONTENT (-2) AT_MOST(若父容器為 EXACTLYAT_MOST
具體數(shù)值(≥0) EXACTLY

【補(bǔ)充】
MATCH_PARENTWRAP_CONTENTViewGroup.LayoutParams 中的常量,分別對(duì)應(yīng) -1-2

2.4 自定義 View 的 onMeasure 實(shí)現(xiàn)規(guī)范

當(dāng)自定義 View 直接繼承 View(而非 TextView、ImageView 等已有實(shí)現(xiàn))時(shí),必須重寫 onMeasure(),否則 wrap_content 將失效。

正確實(shí)現(xiàn)示例:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width;
    int height;

    // 處理寬度
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    if (widthMode == MeasureSpec.EXACTLY) {
        width = widthSize; // 父容器已指定確切大小
    } else {
        // 計(jì)算內(nèi)容所需寬度(例如文本寬度 + padding)
        int desiredWidth = calculateDesiredWidth();
        if (widthMode == MeasureSpec.AT_MOST) {
            width = Math.min(desiredWidth, widthSize); // 不能超過父容器限制
        } else { // UNSPECIFIED
            width = desiredWidth;
        }
    }

    // 高度同理...
    
    setMeasuredDimension(width, height);
}

【關(guān)鍵提醒】
若未處理 AT_MOST 模式,直接 setMeasuredDimension(desiredWidth, desiredHeight),則當(dāng)布局中使用 wrap_content 時(shí),View 會(huì)無視父容器限制,可能導(dǎo)致布局溢出或性能問題。

2.5 View 測(cè)量與 Activity 生命周期不同步問題

  • View 的 measure 過程和 Activity 的生命周期實(shí)際上是不同步的;
  • 若在 View 尚未測(cè)量完畢時(shí)獲取寬高,結(jié)果為 0。

解決方案(四種方法):

  1. Activity/View#onWindowFocusChanged
  2. view.post(runnable)
  3. ViewTreeObserver
  4. view.measure(int widthMeasureSpec, int heightMeasureSpec)

2.6 getWidth() / getHeight() 與測(cè)量寬高的關(guān)系

  • 默認(rèn)情況下,getWidth()、getHeight() 返回的值正好等于 View 的測(cè)量寬/高;
  • 形成時(shí)機(jī)差異
    • 測(cè)量寬/高:形成于 measure 過程;
    • 最終寬/高:形成于 layout 方法中。
  • 特殊情況
    • 若在 layout 過程中不按默認(rèn)套路出牌(即不使用 mMeasuredWidthmMeasuredHeight,而是人為設(shè)定值),則兩者不相等。

三、Android 渲染架構(gòu)

3.1 整體架構(gòu)概覽

概覽
  • 圖像生產(chǎn)者(Producer):

    • 即我們的 App;
    • 更底層是 Canvas → Surface。
  • 圖像消費(fèi)者(Consumer):

    • SurfaceFlinger
  • 圖像緩沖區(qū)

    • BufferQueue,一般采用三緩沖區(qū)機(jī)制。

3.2 圖像生產(chǎn)者分類

主要圖像生產(chǎn)者包括:

  • MediaPlayer
  • CameraPreview
  • NDK (Skia)
  • OpenGL ES

工作方式差異:

  • MediaPlayer / Camera Preview

    • 通過直接讀取圖像源生成圖像數(shù)據(jù)。
  • NDK (Skia) / OpenGL ES

    • 通過自身的繪制能力生產(chǎn)圖像數(shù)據(jù)。

3.3 Skia 與 OpenGL 的區(qū)別

  • OpenGL

    • 是一種跨平臺(tái)的 3D 圖形繪制規(guī)范接口;
    • OpenGL ES 是其針對(duì)嵌入式設(shè)備(如手機(jī))的優(yōu)化版本。
  • Skia

    • 是一個(gè)2D 圖形渲染庫,可獨(dú)立完成 2D 繪制;
    • 支持 CPU 軟件繪制GPU 硬件加速;
    • 3D 效果(依賴硬件)由 OpenGL、Vulkan、Metal 支持;
    • Android、Flutter 均使用 Skia 完成繪制。
  • Vulkan

    • Android 引入 Vulkan 支持;
    • 用于替換 OpenGL;
    • 不僅支持 3D,也支持 2D;
    • 更加輕量級(jí)。

3.4 硬件加速機(jī)制

  • 柵格化(Rasterization)是繪制前的關(guān)鍵步驟,但非常耗時(shí);
  • 硬件加速思想
    • 將 CPU 不擅長(zhǎng)的圖形計(jì)算轉(zhuǎn)換為 GPU 專用指令;
    • 由 GPU 完成繪制任務(wù)。
  • 本質(zhì)
    • 使用 GPU 代替 CPU 完成 Graphic Buffer 繪制工作,以提升性能。

硬件加速演進(jìn):

  • Android 4.0 開始默認(rèn)開啟硬件加速;
  • 但部分 API 不支持硬件加速,需手動(dòng)關(guān)閉;
  • Skia 與硬件加速
    • 軟件繪制使用 Skia 庫;
    • 不代表 Skia 不支持硬件加速;
    • Android 8 開始可選擇使用 Skia 進(jìn)行硬件加速;
    • Android 9 開始默認(rèn)使用 Skia 進(jìn)行硬件加速;
    • Skia 的硬件加速主要通過 copybit 模塊調(diào)用 OpenGL 或 Skia 自身實(shí)現(xiàn)。

3.5 渲染緩沖區(qū)與“黃油計(jì)劃”

  • 圖像生產(chǎn)者(OpenGL、Skia、Vulkan)將繪制數(shù)據(jù)存入圖像緩沖區(qū);
  • SurfaceFlinger 從緩沖區(qū)取出數(shù)據(jù),進(jìn)行加工與合成。
三緩沖機(jī)制
  • VSync 信號(hào)驅(qū)動(dòng)
    • 系統(tǒng)在收到 VSync 信號(hào)后(每 16ms 一次),立即開始下一幀渲染;
    • CPU 和 GPU 在收到信號(hào)后立刻開始計(jì)算,并將數(shù)據(jù)寫入 buffer。

Android 5.0 的兩大改進(jìn):

  1. 引入 RenderNode

    • 對(duì) DisplayList 及 View 顯示屬性進(jìn)一步封裝。
  2. 引入 RenderThread

    • 所有 GL 命令執(zhí)行都放到此線程;
    • RenderNode 中存有渲染幀的全部信息;
    • 可執(zhí)行屬性動(dòng)畫,即使主線程有耗時(shí)操作,也能保證動(dòng)畫流暢。

3.6 SurfaceFlinger —— 圖像消費(fèi)者核心

  • SurfaceFlinger 是 Android 系統(tǒng)中最重要的圖像消費(fèi)者;
  • 所有 Activity 繪制的界面圖像都會(huì)傳遞給它;
  • 主要作用:
    • 接收 GraphicBuffer;
    • 交給 HWComposerOpenGL 做合成;
    • 合成完成后,將最終數(shù)據(jù)提交給 FrameBuffer。

3.7 渲染主流程總結(jié)

  1. onMeasure、onLayout 計(jì)算出 View 的大小和位置(UI 線程);
  2. draw 方法中進(jìn)行繪制,但并未真正繪制;
  3. 繪制指令被封裝為 DisplayList,進(jìn)一步封裝為 RenderNode;
  4. RenderNode 同步給 RenderThread
  5. RenderThread 通過 dequeueSurfaceFlingerBufferQueue 獲取 GraphicBuffer;
  6. 根據(jù)繪制指令調(diào)用 OpenGL 接口,通過 GPU 渲染到離屏緩沖區(qū);
  7. 渲染完成后,將緩沖區(qū)交還給 SurfaceFlingerBufferQueue;
  8. SurfaceFlinger 通過硬件設(shè)備進(jìn)行 layer 合成,最終展示到屏幕。

四、View 動(dòng)畫機(jī)制

4.1 View 動(dòng)畫 vs 屬性動(dòng)畫

  • View 動(dòng)畫(Animation)不會(huì)修改 View 的屬性值;
    • 例如:平移動(dòng)畫后,View 的實(shí)際位置仍在原處;
    • 僅視覺上移動(dòng)。

4.2 動(dòng)畫執(zhí)行流程

  1. 初始化動(dòng)畫基本信息;
  2. 不立即執(zhí)行,而是走到 ViewRootImpl 中注冊(cè) VSYNC 信號(hào);
  3. 等待下一次 VSYNC 到來,執(zhí)行 scheduleTraversals;
  4. scheduleTraversals 中執(zhí)行 View 的三大流程;
    • measurelayout 不會(huì)執(zhí)行(因不需要);
  5. draw 過程中順便執(zhí)行動(dòng)畫;
    • 一次只執(zhí)行一幀
    • 若動(dòng)畫未完成,則調(diào)用 invalidate() 觸發(fā)下一幀。

4.3 性能優(yōu)化

  • 只有參與動(dòng)畫的 View 才走 draw 流程;
  • Animation 內(nèi)部通過重繪申請(qǐng),由 ViewRootImpl 監(jiān)聽 VSYNC 信號(hào);
  • performDraw 中遍歷 View 樹,遇到需執(zhí)行動(dòng)畫的 View 則執(zhí)行該幀動(dòng)畫。

五、從生命周期分析 UI 原理

  • attach 方法

    • 創(chuàng)建 PhoneWindow。
  • onCreate 方法

    • setContentView 調(diào)用 PhoneWindowsetContentView;
    • 創(chuàng)建 DecorView;
    • 將 XML 布局解析后添加到 DecorView 中。
  • onResume 方法執(zhí)行后

    • 創(chuàng)建 ViewRootImpl(最頂級(jí)的 View,DecorView 的 parent);
    • 調(diào)用 setView 方法。
  • ViewRootImpl.setView 方法

    • PhoneWindow 添加到 WMS 中(通過 Session 作為媒介);
    • 調(diào)用 requestLayout,發(fā)起繪制請(qǐng)求。
  • requestLayout 流程

    • 最終調(diào)用 performTraversals
    • 執(zhí)行 View 的 measure、layout、draw 三大流程;
    • draw 方法需要傳入 Canvas 參數(shù)。
  • Surface 綁定與提交

    • 通過 relayoutWindow 方法將 Surface 與當(dāng)前 Window 綁定;
    • 通過 Surface.lockCanvas() 獲取 Canvas;
    • View 的繪制通過此 Canvas 完成;
    • 最后通過 Surface.unlockCanvasAndPost() 提交繪制數(shù)據(jù);
    • 數(shù)據(jù)最終交給 SurfaceFlinger 提交至屏幕顯示。
圖片

以下四張圖清晰地展示了 View 在 Activity 不同生命周期階段的狀態(tài)變化:


第一次加載

切換到后臺(tái)

切換到前臺(tái)

銷毀

六、UI 優(yōu)化要點(diǎn)

6.1 卡頓的根本原因

UI 卡頓主要發(fā)生在滑動(dòng)或首次打開頁面時(shí),原因包括:

  1. inflate 布局耗時(shí)

    • 讀取、解析 layout XML 文件耗時(shí);
    • 反射創(chuàng)建 View 耗時(shí)。
  2. 過度嵌套

    • 導(dǎo)致 measure、layout、render(過度繪制)耗時(shí)增加。
  3. onDraw 中頻繁觸發(fā) GC

    • 如大量拼接 String 等操作。
  4. 主線程執(zhí)行耗時(shí)任務(wù)

  5. 主線程同步鎖競(jìng)爭(zhēng)

    • 與其他線程競(jìng)爭(zhēng)鎖資源,導(dǎo)致阻塞。
  6. 系統(tǒng)負(fù)載過高

    • 同一時(shí)間線程過多,造成 CPU/IO 負(fù)載過高。

6.2 優(yōu)化策略

  • 針對(duì)性優(yōu)化底層流程

    • 優(yōu)化 layout XML 讀??;
    • 優(yōu)化 View 創(chuàng)建(如使用 ViewStub、Merge、ConstraintLayout 減少嵌套)。
  • 場(chǎng)景化排查

    • 對(duì)歸類的卡頓場(chǎng)景逐一分析、優(yōu)化。

【重要提醒】
開發(fā)者常在高端機(jī)測(cè)試,對(duì)卡頓感知不明顯;
但線上用戶多處于弱網(wǎng)/低端機(jī)/多任務(wù)并行環(huán)境,卡頓感知強(qiáng)烈。

6.3 開發(fā)者現(xiàn)狀統(tǒng)計(jì)(官方數(shù)據(jù))

  • 超過一半開發(fā)者未寫過自定義 ViewGroup
  • 接近六成開發(fā)者不知道 MeasureSpec 的算法
  • 80% 的開發(fā)者不會(huì)優(yōu)化 UI 性能。

6.4 UI 本質(zhì)原理

Android 渲染機(jī)制的核心是一個(gè) View 從:

  • 文本數(shù)據(jù)(layout XML)
  • 實(shí)例數(shù)據(jù)(View 對(duì)象)
  • 圖像數(shù)據(jù)(由 OpenGL 調(diào)用 GPU 生成的 bitmap)
  • → 推送至屏幕顯示的過程。

中間還涉及 SurfaceFlinger 進(jìn)程對(duì)驅(qū)動(dòng)的處理。


七、參考資料(原樣保留)


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容