自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

移動端渲染原理淺析

原創(chuàng) 精選
移動開發(fā) 移動應(yīng)用
本文主要介紹了移動端渲染原理的相關(guān)內(nèi)容。文章開始介紹了一下渲染相關(guān)的基礎(chǔ)知識,講了渲染所需要的原始數(shù)據(jù)源-位圖以及 CPU 和 GPU 如何協(xié)同工作得到位圖數(shù)據(jù)的。

作者 | 尚懷軍

計算機或手機的渲染是一個非常復(fù)雜的過程,本文介紹了渲染相關(guān)的一些基礎(chǔ)知識,并結(jié)合 iOS 和安卓的技術(shù)框架介紹了移動端渲染原理,最后詳細的解析了 iOS 中的離屏渲染以及圓角優(yōu)化的一些方法。

渲染基礎(chǔ)知識

屏幕繪制的原始數(shù)據(jù)源

位圖

我們在屏幕上繪制圖像需要的原始數(shù)據(jù)叫做位圖。位圖(Bitmap) 是一種數(shù)據(jù)結(jié)構(gòu)。一個位圖是由 n*m 個像素組成,每個像素的顏色信息由 RGB 組合或者灰度值表示。根據(jù)位深度,可將位圖分為 1、4、8、16、24 及 32 位圖像等。每個像素使用的信息位數(shù)越多,可用的顏色就越多,顏色表現(xiàn)就越逼真,越豐富,相應(yīng)的數(shù)據(jù)量越大。

物理像素和邏輯像素

位圖一般存儲的是物理像素,而應(yīng)用層一般用的是邏輯像素,物理像素和邏輯像素之間會存在一定的對應(yīng)關(guān)系。例如,iOS 中物理像素和邏輯像素的對應(yīng)關(guān)系如下:

  • iOS1 倍屏 1pt 對應(yīng) 1 個物理像素
  • iOS2 倍屏 1pt 對應(yīng) 2 個物理像素
  • iOS3 倍屏 1pt 對應(yīng) 3 個物理像素

將位圖繪制到顯示器

上邊講了屏幕上繪制圖像需要的原始數(shù)據(jù)叫做位圖。那么問題來了,有了位圖數(shù)據(jù)之后如何將圖像繪制到屏幕上呢?如下圖所示:電子槍從上到下逐行掃描,掃描完成后顯示器就呈現(xiàn)一幀畫面。然后電子槍回到屏幕初始位置進行下一次掃描。為了同步顯示器的顯示過程和視頻控制器的掃描過程,顯示器會用硬件時鐘產(chǎn)生一系列的定時信號。當(dāng)電子槍換行進行掃描時,顯示器會發(fā)出一個水平同步信號;當(dāng)一幀畫面繪制完成后,電子槍回到原位,準(zhǔn)備畫下一幀前,顯示器會發(fā)出一個垂直同步信號。顯示器通常以固定的頻率進行刷新,這個刷新率就是垂直同步信號產(chǎn)生的頻率。

CPU、GPU、顯示器協(xié)同工作流程

前一部分介紹了視頻控制器將位圖數(shù)據(jù)顯示到物理屏幕上的過程,那么位圖數(shù)據(jù)是怎么得到的呢?其實位圖數(shù)據(jù)是通過 CPU、GPU 協(xié)同工作得到的。下圖就是常見的 CPU、GPU、顯示器協(xié)同工作的流程。CPU 計算好顯示內(nèi)容提交至 GPU,GPU 渲染完成后將渲染結(jié)果存入幀緩沖區(qū),接下來就需要將得到的像素信息顯示在物理屏幕上了,這時候視頻控制器(Video Controller)會讀取幀緩沖器中的信息傳遞給顯示器(Monitor)進行顯示。完整的流程如下圖所示:

CPU 和 GPU 的區(qū)別

講到 CPU、GPU、顯示器的協(xié)同工作流程,就不得不提一下 CPU 和 GPU 的區(qū)別。

CPU是中央處理器,適合單一復(fù)雜邏輯,而GPU是圖形處理器,適合高并發(fā)簡單邏輯。

GPU 有特別多的的計算單元和超長的流水線,但是控制邏輯非常簡單,并且還省去了緩存,適合對低延遲要求不高的運算。而 CPU 不僅被 Cache 占據(jù)了大量空間,而且還有特別復(fù)雜的控制邏輯,相比之下計算能力只是 CPU 很小的一部分。圖形渲染涉及到的矩陣運算比較多,矩陣相關(guān)的運算可以被拆分成并行的簡單的運算,所以渲染處理這件事特別適合 GPU 去做。

總結(jié)來說:GPU 的工作計算量大,但技術(shù)含量不高,需要簡單重復(fù)很多次。就好比有個工作需要算成百上千次一百以內(nèi)加減乘除一樣。而 CPU 就像老教授,積分微分都會算,適合處理單一復(fù)雜邏輯運算。

通用渲染流水線

我們通常將圖像繪制的完整流程稱為渲染流水線,這個過程是 CPU 和 GPU 協(xié)作完成的。一般一個渲染流程可以分成 4 個概念階段,分別是:應(yīng)用階段(Application Stage),幾何階段(Geometry Stage),光柵化階段(Rasterizer Stage),像素處理階段(Pixel Processing)。在《Real–Time Rendering 4th》中非常透徹的講解了實時渲染的各種知識點,對渲染原理感興趣的可以看看這本書,這本書堪稱“實時渲染圣經(jīng)”。下邊會簡單介紹一下這幾個過程。

應(yīng)用階段(Application Stage)

簡而言之,就是圖像在應(yīng)用中的處理階段。說白了就是一段運行在 CPU 上的程序,這時還沒有 GPU 什么事。這一階段主要是 CPU 負責(zé)處理用戶的交互和操作,然后做一些應(yīng)用層布局相關(guān)的處理,最后輸出圖元(點、線和三角形)信息給到下一階段。

大家可能會疑惑,圖元只有簡單的點、線、三角形,能表示豐富的立體圖形么,下邊這張立體感很強的海豚就能給出肯定的答案了,簡單的三角形再加上不同的著色,就能呈現(xiàn)出立體圖形。

幾何階段(Geometry Stage)

1. 頂點著色器(Vertex Shader)

頂點著色器可以對頂點的屬性進行一些基本的處理。將頂點信息進行視角轉(zhuǎn)換、添加光照信息、增加紋理等操作。CPU 丟給 GPU 的信息,就好像是站在上帝視角把這個視角看到的所有信息都給到 GPU。而 GPU 則是站在人類的角度,將人類可以觀察到的畫面,輸出在顯示器上。所以這里是以人的視角為中心,進行坐標(biāo)轉(zhuǎn)換。

2. 形狀裝配(Shape Assembly)這個階段是將頂點著色器輸出的所有頂點作為輸入,并將所有的點裝配成指定圖元的形狀。圖元(Primitive)如:點、線、三角形。這個階段也叫圖元裝配。

3. 幾何著色器(Geometry Shader)在圖元外添加額外的頂點,將原始圖元轉(zhuǎn)換成新圖元,來構(gòu)建更加復(fù)雜的模型。

光柵化階段(Rasterizer Stage)

光柵化階段會把前三個幾何階段處理后得到的圖元(primitives)轉(zhuǎn)換成一系列的像素。

如上圖所示,我們可以看到,每個像素的中心有一個點,光柵化便是用這個中心點來進行劃分的,如果中心點在圖元內(nèi)部,那么這個中心點所對應(yīng)的像素就屬于該圖元。簡而言之,這一階段就是將連續(xù)的幾何圖形轉(zhuǎn)化為了離散化的像素點。

像素處理階段(Pixel Processing)

1. 片段著色器(Fragment Shader)

通過上述的光柵化階段之后,我們就拿到了各個圖元對應(yīng)的像素,最后這個階段要做的事情就是給每個 Pixel 填充上正確的顏色,然后通過一系列處理計算,得到相應(yīng)的圖像信息,最終輸出到顯示器上。這里會做內(nèi)插,就像補間動畫一樣。比如想要把一系列散點連成平滑曲線,相鄰的已知點之間可能會缺少很多點,這時候就需要通過內(nèi)插填補缺少的數(shù)據(jù),最終平滑曲線上除已知點之外的所有點都是插值得到的。同樣的,三角形的三個角色值給定后,其它的片段則根據(jù)插值計算出來,也就呈現(xiàn)來漸變的效果。

2. 測試與混合(Tests and Blending)

這個階段會檢測對應(yīng)的深度值(z 坐標(biāo)),來判斷這個像素位于其它圖層像素的前面還是后面,決定是否應(yīng)該丟棄。此外,該階段還會檢查 alpha? 值( alpha 值定義了一個像素的透明度),從而對圖層進行混合。( 一句話簡單說,就是檢查圖層深度和透明度,并進行圖層混合。)

R = S + D * (1 - Sa)

含義:
R:Result,最終像素顏色。
S:Source,來源像素(上面的圖層像素)。
D:Destination,目標(biāo)像素(下面的圖層像素)。
a:alpha,透明度。

結(jié)果 = S(上)的顏色 + D(下)的顏色 * (1 - S(上)的透明度)

經(jīng)歷了上邊漫長的流水線之后我們便可以拿到屏幕繪制所需要的原始數(shù)據(jù)源-位圖數(shù)據(jù),然后由視頻控制器把位圖數(shù)據(jù)顯示在物理屏幕上。

iOS 渲染原理

渲染技術(shù)棧

上邊鋪墊完渲染相關(guān)的一些基礎(chǔ)知識之后,下面主要介紹 iOS 渲染相關(guān)的一些原理和知識。下圖是 iOS 的圖形渲染技術(shù)棧,有三個相關(guān)的核心系統(tǒng)框架:Core Graphics、Core Animation、Core Image ,這三個框架主要用來繪制可視化內(nèi)容。他們都是通過 OpenGL 來調(diào)用 GPU 進行實際的渲染,然后生成最終位圖數(shù)據(jù)存儲到幀緩沖區(qū),視頻控制器再將幀緩沖區(qū)的數(shù)據(jù)顯示物理屏幕上。

UIKit

UIKit  是 iOS 開發(fā)者最常用的框架,可以通過設(shè)置  UIKit  組件的布局以及相關(guān)屬性來繪制界面。但是  UIKit  并不具備在屏幕成像的能力,這個框架主要負責(zé)對用戶操作事件的響應(yīng)(UIView  繼承自  UIResponder),事件經(jīng)過響應(yīng)鏈傳遞。

Core Animation

Core Animation  主要負責(zé)組合屏幕上不同的可視內(nèi)容,這些可視內(nèi)容可被分解成獨立的圖層也就是我們?nèi)粘i_發(fā)過程中常接觸的 CALayer,這些圖層被存儲在圖層樹中。CALayer 主要負責(zé)頁面渲染,它是用戶能在屏幕上看見的一切的基礎(chǔ)。

Core Graphics

Core Graphics 主要用于運行時繪制圖像。開發(fā)者可以使用此框架來處理基于路徑的繪圖,轉(zhuǎn)換,顏色管理,離屏渲染,圖案,漸變和陰影等等。

Core Image

Core Image  與  Core Graphics 正好相反,Core Graphics  是在運行時創(chuàng)建圖像,而  Core Image  則是在運行前創(chuàng)建圖像。

OpenGL ES 和 Metal

OpenGL ES 和 Metal 都是第三方標(biāo)準(zhǔn),基于這些標(biāo)準(zhǔn)具體的內(nèi)部實現(xiàn)是由對應(yīng)的 GPU 廠商開發(fā)的。Metal 是蘋果的一套第三方標(biāo)準(zhǔn),由蘋果實現(xiàn)。很多開發(fā)者都沒有直接使用過  Metal,但卻通過 Core Animation、Core Image 這些核心的系統(tǒng)框架在間接的使用 metal。

CoreAnimation 與 UIKit 框架的關(guān)系

上邊渲染框架中提到的 Core Animation 是 iOS 和 OS X 上圖形渲染和動畫的基礎(chǔ)框架,主要用來給視圖和應(yīng)用程序的其他可視元素設(shè)置動畫。Core Animation 的實現(xiàn)邏輯是將大部分實際繪圖的工作交給 GPU 加速渲染,這樣不會給 CPU 帶來負擔(dān),還能實現(xiàn)流暢的動畫。CoreAnimation 的核心類是 CALayer,UIKit 框架的核心類是 UIView,下邊詳細介紹一下這兩個類的關(guān)系。

UIView 與 CALayer 的關(guān)系

如上圖所示,UIView 和 CALayer 是一一對應(yīng)的關(guān)系,每一個 UIView 都有一個 CALayer 與之對應(yīng),一個負責(zé)布局、交互響應(yīng),一個負責(zé)頁面渲染。

他們的兩個核心關(guān)系如下:

  • CALayer 是 UIView 的屬性之一,負責(zé)渲染和動畫,提供可視內(nèi)容的呈現(xiàn)。
  • UIView 提供了對 CALayer 功能的封裝,負責(zé)了交互事件的處理。

舉一個形象一點的例子,UIView 是畫板,CALayer 就是畫布,當(dāng)你創(chuàng)建一個畫板的時候,會自動綁定一個畫布,畫板會響應(yīng)你的操作,比如你可以移動畫板,畫布則負責(zé)呈現(xiàn)具體的圖形,二者職責(zé)分明。一個負責(zé)交互,一個負責(zé)渲染繪制。

為什么要分離出 CALayer 和 UIView?

iOS 平臺和 MacOS 平臺上用戶的交互方式有著本質(zhì)的不同,但是渲染邏輯是通用的,在 iOS 系統(tǒng)中我們使用的是 UIKit 和 UIView,而在 MacOS 系統(tǒng)中我們使用的是 AppKit 和 NSView,所以在這種情況下將展示部分的邏輯分離出來跨平臺復(fù)用。

CALayer 中的 contents 屬性保存了由設(shè)備渲染流水線渲染好的位圖 bitmap(通常被稱為 backing store),也就是我們最開始說的屏幕繪制需要的最原始的數(shù)據(jù)源。而當(dāng)設(shè)備屏幕進行刷新時,會從 CALayer 中讀取生成好的 bitmap,進而呈現(xiàn)到屏幕上。

@interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>
/** Layer content properties and methods. **/

/* An object providing the contents of the layer, typically a CGImageRef,
* but may be something else. (For example, NSImage objects are
* supported on Mac OS X 10.6 and later.) Default value is nil.
* Animatable. */

@property(nullable, strong) id contents;
@end

Core Animation 流水線

其實早在 WWDC 的 Advanced Graphics and Animations for iOS Apps(WWDC14 419,關(guān)于 UIKit 和 Core Animation 基礎(chǔ)的 session)中蘋果就給出了 CoreAnimation 框架的渲染流水線,具體流程如下圖所示:

整個流水線中 app 本身并不負責(zé)渲染,渲染則是由一個獨立的進程負責(zé),即  Render Server  進程。下邊會詳細介紹一下整個 pipeline 的流程。

應(yīng)用階段

  • 視圖的創(chuàng)建
  • 布局計算
  • 對圖層進行打包,在下一次 RunLoop 時將其發(fā)送至Render Server
  • app 處理用戶的點擊操作,在這個過程中 app 可能需要更新視圖樹,如果視圖樹發(fā)生更新,圖層樹也會被更新
  • 其次,app 通過 CPU 完成對顯示內(nèi)容的計算

Render Server & GPU

  • 這一階段主要執(zhí)行 metal、Core Graphics 等相關(guān)程序,并調(diào)用 GPU 在物理層上完成對圖像的渲染
  • GPU 將渲染后的位圖數(shù)據(jù)存儲到 Frame Buffer

Display

  • 視頻控制器將幀緩沖區(qū)的位圖數(shù)據(jù)一幀一幀的顯示在物理屏幕上

如果把把上邊的步驟串在一起,會發(fā)現(xiàn)它們執(zhí)行消耗的時間超過了 16.67 ms,因此為了滿足對屏幕的 60 FPS 刷新率的支持,需要通過流水線的方式將這些步驟并行執(zhí)行,如下圖所示。每一個階段都在源源不斷的給下一個階段輸送產(chǎn)物。這時候就可以滿足 16.67 毫秒產(chǎn)生一幀數(shù)據(jù)的要求了。

安卓渲染原理

安卓上層顯示系統(tǒng)

安卓中 Activity 的一個重要的職責(zé)就是對界面生命周期的管理,這也就伴隨了對視圖窗口的管理。這中間就涉及了兩個 Android 中兩個主要的服務(wù),AMS(ActivityManagerService)和WMS(WindowManagerService)。

在 Android 中,一個 view 會有對應(yīng)的 canvas。視圖樹對應(yīng)一個 canvas 樹,Surfaceflinger 控制多個 canvas 的合成。最終渲染完成輸出位圖數(shù)據(jù),顯示到手機屏幕。

應(yīng)用層布局

View 和 ViewGroup

View 是 Android 中所有控件的基類,View 類有一個很重要的子類:ViewGroup,ViewGroup 作為其他 view 的容器使用。Android 的所有 UI 組件都是建立在 View、ViewGroup 基礎(chǔ)之上的,整體采用“組合”的思想來設(shè)計 View 和 ViewGroup:ViewGroup 是 View 的子類,所以 ViewGroup 也可以被當(dāng)做 View 使用。一個 Android app 的圖形用戶界面會對應(yīng)一個視圖樹,而視圖樹則對應(yīng)一個 canvas 樹。這個有點兒類似于 iOS 中的 UIView 和 CALayer 的概念,一個負責(zé)應(yīng)用層布局,一個負責(zé)底層渲染。

系統(tǒng)底層渲染顯示

應(yīng)用層的 view 對應(yīng)到 canvas,canvas 到系統(tǒng)進程就成了 layer。SurfaceFlinger 主要提供 layer 的渲染合成服務(wù)。SurfaceFlinger 是一個常駐的 binder 服務(wù),會隨著 init 進程的啟動而啟動。下面這張圖就詳細的介紹了上層 view 到底層 layer 的轉(zhuǎn)化,以及 SurfaceFlinger 對多個 layer 的渲染合成。

iOS 離屏渲染

離屏渲染原理以及定義

首先來介紹一下離屏渲染的原理。我們正常的渲染流程是:CPU 和 GPU 協(xié)作,不停地將內(nèi)容渲染完成后得到的位圖數(shù)據(jù)放入 Framebuffer (幀緩沖區(qū))中,視頻控制器則不斷地從 Framebuffer 中獲取內(nèi)容,顯示實時的內(nèi)容。

而離屏渲染的流程是這樣的:

與普通情況下 GPU 直接將渲染好的內(nèi)容放入 Framebuffer 中不同,離屏渲染需要先額外創(chuàng)建離屏渲染緩沖區(qū) ,將提前渲染好的內(nèi)容放入其中,等到合適的時機再將 Offscreen Buffer 中的內(nèi)容進一步疊加、渲染,完成后將結(jié)果再寫入 Framebuffer 中。

為什么先要將數(shù)據(jù)存放在離屏渲染緩沖區(qū)呢?有兩個原因,一個是被動的,一個是主動的。

  1. 一些特殊效果需要使用額外的 Offscreen Buffer 來保存渲染的中間狀態(tài)(被動)
  2. 處于效率目的,可以將內(nèi)容提前渲染保存在 Offscreen Buffer 中,達到復(fù)用的目的。(主動)

被動離屏渲染

常見的觸發(fā)被動離屏渲染的場景

透明、陰影加圓角通常被稱為 UI 三大寶,但這些效果在 iOS 的日常開發(fā)過程中卻往往會導(dǎo)致被動的離屏渲染,下邊是幾個常見的會觸發(fā)被動離屏渲染的場景。

觸發(fā)離屏渲染的原因

講離屏渲染的原因不得不提畫家算法,畫家算法的整體思想是按層繪制,首先繪制距離較遠的場景,然后用繪制距離較近的場景覆蓋較遠的部分。這里的層在 iOS 的渲染技術(shù)棧中就可以被對應(yīng)到 layer。

通常對于每一層 layer,Render Server 會遵循“畫家算法”,按次序輸出到 frame buffer,后一層覆蓋前一層,就能得到最終的顯示結(jié)果,對于這個 layer 樹則是以深度優(yōu)先的算法將 layer 輸出到 frame buffer。

作為“畫家”的 GPU 雖然可以一層一層往畫布上進行輸出,但是卻沒有辦法在某一層渲染完成之后,再回過頭來改變其中的某個部分。因為在這一層之前的若干層 layer 像素數(shù)據(jù),已經(jīng)在渲染中被合成在一起了。其實這里和 photoshop 中的圖層合并非常像,一旦多個圖層被合并在一起,就無法再單獨對某一個圖層進行修改。所以需要在離屏緩沖區(qū)中把子 layer 依次畫好,然后把四個角裁剪好之后再和之前的圖層進行混合。

GPU 離屏渲染的性能影響

一提離屏渲染,我們直觀上的感覺是會對性能有影響。因為為了滿足 60fps 的刷新頻率,GPU 的操作都是高度流水線化的。本來所有的計算工作都在有條不紊地正在向 frame buffer 輸出,這時候突然又有一些特殊的效果觸發(fā)了離屏渲染,需要切換上下文,把數(shù)據(jù)輸出到另一塊內(nèi)存,這時候流水線中很多中間產(chǎn)物只能被丟棄,這種頻繁的上下文切換對 GPU 的渲染性能有非常大的影響。

如何防止非必要離屏渲染?

  • 對于一些圓角可以創(chuàng)建四個背景顏色弧形的 layer 蓋住四個角,從視覺上制造圓角的效果
  • 對于 view 的圓形邊框,如果沒有 backgroundColor,可以放心使用 cornerRadius 來做
  • 對于所有的陰影,使用 shadowPath 來規(guī)避離屏渲染
  • 對于特殊形狀的 view,使用 layer mask 并打開 shouldRasterize 來對渲染結(jié)果進行緩存

圓角實現(xiàn)的優(yōu)化策略

使用CALayer的cornerRadius并設(shè)置 cliptobounds 以后會觸發(fā)離屏渲染(offscreen rendering)。滾動時每秒需要在 60 幀上執(zhí)行裁剪操作,即使內(nèi)容沒有發(fā)生任何變化。GPU 也必須在每幀之間切換上下文,合成整個幀和裁剪。這些對性能的消耗直接影響到 Render Server 這個獨立渲染進程,造成掉幀。為了優(yōu)化渲染性能,我們可以選擇一些其他的實現(xiàn)圓角的方案。下邊是圓角的具體實現(xiàn)需要考慮的條件。

圓角的具體實現(xiàn)需要考慮的條件

  1. 圓角下(movement underneath the corner)是否有滑動。
  2. 是否有穿過圓角滑動(movement through the corner)。
  3. 四個圓角是否處于同一個 layer 上,有沒有與其他 子 layer 相交。

圓角的具體實現(xiàn)方案

如何根據(jù)對應(yīng)的條件選取圓角的實現(xiàn)方案

上邊提到了圓角的優(yōu)化要考慮的條件以及不同的圓角實現(xiàn)方案,下邊這個流程圖就是把條件和方案對應(yīng)起來,給出了圓角的最佳實現(xiàn)方案。

總結(jié)

本文主要介紹了移動端渲染原理的相關(guān)內(nèi)容。文章開始介紹了一下渲染相關(guān)的基礎(chǔ)知識,講了渲染所需要的原始數(shù)據(jù)源-位圖以及 CPU 和 GPU 如何協(xié)同工作得到位圖數(shù)據(jù)的。后面又結(jié)合 iOS 和安卓的技術(shù)框架介紹了移動端渲染的相關(guān)原理。最后深入分析了 iOS 中的離屏渲染,講解了現(xiàn)有的圓角優(yōu)化的一些方案。

責(zé)任編輯:未麗燕 來源: 字節(jié)跳動技術(shù)團隊
相關(guān)推薦

2021-04-26 13:20:06

Vue服務(wù)端渲染前端

2017-04-12 11:46:46

前端瀏覽器渲染機制

2017-03-02 12:39:04

移動端iOS監(jiān)控體系

2016-12-08 10:57:08

渲染引擎前端優(yōu)化

2020-08-05 08:21:41

Webpack

2016-01-05 09:45:57

設(shè)計版式

2015-12-16 12:40:32

H5緩存機制移動

2024-02-22 13:47:40

2016-01-08 11:35:01

移動端設(shè)計版面

2020-11-05 11:14:29

Docker底層原理

2023-05-11 07:25:57

ReduxMiddleware函數(shù)

2009-07-16 10:23:30

iBATIS工作原理

2011-07-19 13:26:50

iPhone PhoneGap 框架

2020-11-06 15:20:45

瀏覽器前端架構(gòu)

2012-05-07 13:55:41

JavaJava Web

2019-08-05 13:20:35

Android繪制代碼

2011-04-13 15:01:39

2010-09-25 14:01:11

Java跨平臺

2012-08-30 16:19:08

移動辦公明朝萬達

2009-07-15 17:33:08

Swing客戶端
點贊
收藏

51CTO技術(shù)棧公眾號