




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、Android窗口管理服務WindowManagerService計算窗口Z軸位置的過程分析通過前面幾篇文章的學習,我們知道了在Android系統(tǒng)中,無論是普通的Activity窗口,還是特殊的輸入法窗口和壁紙窗口,它們都是被WindowManagerService服務組織在一個窗口堆棧中的,其中,Z軸位置較大的窗口排列在Z軸位置較小的窗口的上面。有了這個窗口堆棧之后,WindowManagerService服務就可以按照一定的規(guī)則計算每一個窗口的Z軸位置了,本文就詳細分析這個計算過程。 基于窗口堆棧來計算窗口的Z軸位置是比較有意思的。按照一般的理解,應該是先計算好窗口的Z軸位置,然后再按照Z
2、軸位置的大小來將各個窗口排列在堆棧中。但是,事實上,窗口是按照其它規(guī)則排列在堆棧中。這些規(guī)則與窗口的類型、創(chuàng)建順序和運行狀態(tài)等有關。例如,狀態(tài)欄窗口總是位于堆棧的頂端,輸入法窗口總是位于需要輸入法的窗口的上面,而壁紙窗口總是位于需要顯示壁紙的窗口的下面。又如,當一個Activity組件從后臺激活到前臺時,與它所對應的窗口就會被相應地移動到窗口堆棧的上面去。 從前面和這兩個系列的文章可以知道,窗口的UI最終是需要通過SurfaceFlinger服務來統(tǒng)一渲染的,而SurfaceFlinger服務在渲染窗口的UI之前,需要計算基于各個窗口的Z軸位置來計算它們的可見區(qū)域。因此,WindowManag
3、erService服務計算好每一個窗口的Z軸位置之后,還需要將它們設置到SurfaceFlinger服務中去,以便SurfaceFlinger服務可以正確地渲染每一個窗口的UI。 上述窗口的Z軸位置計算和設置過程如圖1所示:接下來,我們就首先分析兩個需要重新計算窗口Z軸位置的情景,接著再分析窗口的Z軸位置的計算過程,最后分析WindowManagerService服務將窗口的Z軸設置到SurfaceFlinger服務中去的過程。 一. 需要重新計算窗口Z軸位置的情景 這里主要分析兩個需要重新計算窗口Z軸位置的情景:應用程序增加一個窗口到WindowManagerService服務和應用程序請求
4、WindowManagerService服務重新布局一個窗口。 從前面一文可以知道,應用程序請求增加一個窗口到WindowManagerService服務的時候,最終會調用到WindowManagerService類的成員函數addWindow。接下來我們就主要分析這個函數與重新計算窗口Z軸位置相關的邏輯,如下所示:java view plain copy 在CODE上查看代碼片派生到我的代碼片public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor . public in
5、t addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) . synchronized(mWindowMap) . WindowToken token = mTokenMap.get(attrs.token); . win = new WindowState(session, client, token, attachedWindow, attrs,
6、viewVisibility); . if (attrs.type = TYPE_INPUT_METHOD) mInputMethodWindow = win; addInputMethodWindowToListLocked(win); . else if (attrs.type = TYPE_INPUT_METHOD_DIALOG) mInputMethodDialogs.add(win); addWindowToListInOrderLocked(win, true); adjustInputMethodDialogsLocked(); . else addWindowToListInO
7、rderLocked(win, true); if (attrs.type = TYPE_WALLPAPER) . adjustWallpaperWindowsLocked(); else if (attrs.flags&FLAG_SHOW_WALLPAPER) != 0) adjustWallpaperWindowsLocked(); . assignLayersLocked(); . . . 這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。 WindowMana
8、gerService類的成員函數addWindow的具體實現可以參考和這兩篇文章。我們注意到,WindowManagerService類的成員函數addWindow會根據當前正在添加的窗口的類型來調用不同的成員函數來向窗口堆棧的合適位置插入一個WindowState對象,即: 1. 如果添加的是一個輸入法窗口,那么就調用成員函數addInputMethodWindowToListLocked將它放置在需要顯示輸入法的窗口的上面去; 2. 如果添加的是一個輸入法對話框,那么就先調用成員函數addWindowToListInOrderLocked來將它插入到窗口堆棧中,接著再調用成員函數adjus
9、tInputMethodDialogsLocked來將它放置在輸入法窗口的上面; 3. 如果添加的是一個普通窗口,那么就直接調用成員函數addWindowToListInOrderLocked來將它插入到窗口堆棧中; 4. 如果添加的是一個普通窗口,并且這個窗口需要顯示壁紙,那么就先調用成員函數addWindowToListInOrderLocked來將它插入到窗口堆棧中,接著再調用成員函數adjustWallpaperWindowsLocked來將壁紙窗口放置在它的下面。 5. 如果添加的是一個壁紙窗口,那么就先調用成員函數addWindowToListInOrderLocked來將它插入到
10、窗口堆棧中,接著再調用成員函數adjustWallpaperWindowsLocked來將它放置在需要顯示壁紙的窗口的下面。 無論如何,WindowManagerService類的成員函數addWindow最終都會調用成員函數assignLayersLocked來重新計算系統(tǒng)中所有窗口的Z軸位置,這是因為前面往窗口堆棧增加了一個新的窗口。 從前面一文可以知道,應用程序進程請求WindowManagerService服務重新布局一個窗口的時候,最終會調用到WindowManagerService類的成員函數relayoutWindow。接下來我們就主要分析這個函數與重新計算窗口Z軸位置相關的邏輯
11、,如下所示:java view plain copy 在CODE上查看代碼片派生到我的代碼片public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor . public int relayoutWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, boolean
12、insetsPending, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) . synchronized(mWindowMap) WindowState win = windowForClientLocked(session, client, false); . int attrChanges = 0; int flagChanges = 0; if (attrs != null) flagChanges = win.mAttrs
13、.flags = attrs.flags; attrChanges = win.mAttrs.copyFrom(attrs); . boolean imMayMove = (flagChanges&( WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0; boolean focusMayChange = win.mViewVisibility != viewVisibility | (flagChanges&WindowMan
14、ager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) | (!win.mRelayoutCalled); boolean wallpaperMayMove = win.mViewVisibility != viewVisibility && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; . if (focusMayChange) . if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES) imMayMove = fal
15、se; . / updateFocusedWindowLocked() already assigned layers so we only need to / reassign them at this point if the IM window state gets shuffled boolean assignLayers = false; if (imMayMove) if (moveInputMethodWindowsIfNeededLocked(false) | displayed) / Little hack here - we -should- be able to rely
16、 on the / function to return true if the IME has moved and needs / its layer recomputed. However, if the IME was hidden / and isn't actually moved in the list, its layer may be / out of data so we make sure to recompute it. assignLayers = true; if (wallpaperMayMove) if (adjustWallpaperWindowsLoc
17、ked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) assignLayers = true; . if (assignLayers) assignLayersLocked(); . . return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0) | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0); . 這個函數定義在文件frameworks/base/services/java/com/android/serv
18、er/WindowManagerService.java中。 WindowManagerService類的成員函數relayoutWindow具體實現可以參考和這兩篇文章,與窗口Z軸位置計算相關的邏輯大概是這樣的: 1. 如果系統(tǒng)當前獲得焦點的窗口可能發(fā)生了變化,那么就會調用成員函數updateFocusedWindowLocked來重新計算系統(tǒng)當前應該獲得焦點的窗口。如果系統(tǒng)當前獲得焦點的窗口真的發(fā)生了變化,即窗口堆棧的窗口排列發(fā)生了變化,那么在調用成員函數updateFocusedWindowLocked的時候,就會調用成員函數assignLayersLocked來重新計算系統(tǒng)中所有窗口的
19、Z軸位置。 2. 如果系統(tǒng)中的輸入法窗口可能需要移動,那么就會調用成員函數moveInputMethodWindowsIfNeededLocked來檢查是否真的需要移動輸入法窗口。如果需要移動,那么成員函數moveInputMethodWindowsIfNeededLocked的返回值就會等于true,這時候就說明輸入法窗口在窗口堆棧中的位置發(fā)生了變化,因此,就會將變量assignLayers的值設置為true,表示接下來需要重新計算系統(tǒng)中所有窗口的Z軸位置。 3. 如果當前正在請求調整其布局的窗口是由不可見變化可見的,即變量displayed的值等于true,那么接下來也是需要重新計算系統(tǒng)中
20、所有窗口的Z軸位置的,因此,就會將assignLayers的值設置為true。 4. 如果系統(tǒng)中的壁紙窗口可能需要移動,那么就會調用成員函數adjustWallpaperWindowsLocked來檢查是否真的需要移動壁紙窗口。如果需要移動,那么成員函數adjustWallpaperWindowsLocked的返回值的ADJUST_WALLPAPER_LAYERS_CHANGED位就會等于1,這時候就說明壁紙窗口在窗口堆棧中的位置發(fā)生了變化,因此,就會將變量assignLayers的值設置為true,表示接下來需要重新計算系統(tǒng)中所有窗口的Z軸位置。 經過上述的一系列操作后,如果得到的變量ass
21、ignLayers的值設置等于true,那么WindowManagerService類的成員函數relayoutWindow就會調用成員函數assignLayersLocked來重新計算系統(tǒng)中所有窗口的Z軸位置。 二. 計算系統(tǒng)中所有窗口的Z軸位置 從前面第一部分的內容可以知道,一旦窗口堆棧中的窗口發(fā)生了變化,那么WindowManagerService類的成員函數assignLayersLocked就會調用來計算系統(tǒng)中所有窗口的Z軸位置。 窗口的Z軸位置除了與它在窗口堆棧中的位置有關之外,還與窗口的類型有關。窗口的類型在創(chuàng)建的時候就已經是確定了的,WindowManagerService服務
22、在為它創(chuàng)建一個WindowState對象的時候,就會根據它的類型得到一個BaseLayer值,這個BaseLayer值在計算它的Z軸位置的時候會用到。 接下來我們就通過WindowState類的構造函數來分析一個窗口的BaseLayer值是如何確定的,如下所示:java view plain copy 在CODE上查看代碼片派生到我的代碼片public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor . /* How much to multiply the policy
23、9;s type layer, to reserve room * for multiple windows of the same type and Z-ordering adjustment * with TYPE_LAYER_OFFSET. */ static final int TYPE_LAYER_MULTIPLIER = 10000; /* Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above * or below others in the same layer. */ static final
24、 int TYPE_LAYER_OFFSET = 1000; . private final class WindowState implements WindowManagerPolicy.WindowState . final int mBaseLayer; final int mSubLayer; . WindowState(Session s, IWindow c, WindowToken token, WindowState attachedWindow, WindowManager.LayoutParams a, int viewVisibility) . if (mAttrs.t
25、ype >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) / The multiplier here is to reserve space for multiple / windows in the same type layer. mBaseLayer = mPolicy.windowTypeToLayerLw( attachedWindow.mAttrs.type) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; mSubLayer = mPolicy.sub
26、WindowTypeToLayerLw(a.type); . else / The multiplier here is to reserve space for multiple / windows in the same type layer. mBaseLayer = mPolicy.windowTypeToLayerLw(a.type) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; mSubLayer = 0; . . . . 這個函數定義在文件frameworks/base/services/Java/com/android/server/
27、WindowManagerService.java中。 一個窗口除了有一個BaseLayer值之外,還有一個SubLayer值,分別保存在一個對應的WindowState對象的成員變量mBaseLayer和mSubLayer。SubLayer值是用來描述一個窗口是否是另外一個窗口的子窗口的。 假設一個窗口是另外一個窗口的子窗口,那么參數attachedWindow所描述的窗口就是父窗口,這時候子窗口的BaseLayer值就等于父窗口的BaseLayer值,而SubLayer值要么大于0,要么小于0,這與它自己的具體類型有關。 假設一個窗口不是另外一個窗口的子窗口,那么這個窗口的BaseLaye
28、r值就與它自己的具體類型有關,而SubLayer值就等于0。 現在的關鍵就是要根據窗口的類型來計算它的BaseLayer值和SubLayer值,它們分別是通過調用WindowManagerService類的成員變量mPolicy所指向的一個PhoneWindowManager對象的成員函數windowTypeToLayerLw和subWindowTypeToLayerLw來計算得到的。這里有兩個地方是需要注意的。 第一個地方是PhoneWindowManager對象的成員函數windowTypeToLayerLw的返回值并且不是一個窗口的最終的BaseLayer值,而是要將它的返回值乘以一個常
29、量TYPE_LAYER_MULTIPLIER,再加上另外一個常量TYPE_LAYER_OFFSET之后,才得到最終的BaseLayer值。這是因為在Android系統(tǒng)中,相同類型的窗口的Z軸位置都是有著相同的值域,而不同類型的窗口的Z軸位置都是處于兩個不相交的值域。例如,假設有兩種不同類型的窗口,它們的Z軸位置的值域分別為a, b和c, d,那么a, b和c, d的交集一定等于空。又由于每一種類型的窗口的數量是不確定的,因此,WindowManagerService服務就需要為每一種類型的窗口都預留一個范圍足夠大的值域,以便可以滿足要求。 WindowManagerService服務是如何為類
30、型相同的窗口的Z軸位置預留一個范圍足夠大的值域的呢?我們假設類型為t的窗口的Z軸位置的值域為a, b,并且以t為參數調用PhoneWindowManager對象的成員函數windowTypeToLayerLw的返回值為T,那么a的值就等于T * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET,而b的值就等于(T - 1) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET - 1,即從T * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET開始,一共預留了TYPE_LAYER_MULTIP
31、LIER個值作為類型為t窗口的Z軸位置。由于TYPE_LAYER_MULTIPLIER的值定義為10000,而TYPE_LAYER_OFFSET的值定義為1000,因此,每一種類型的窗口都預留有一個足夠大的值域來作為Z軸位置。 第二個地方是窗口的SubLayer值并不直接參與窗口的Z軸位置的計算,但是它會影響到窗口在窗口堆棧的位置。接下來我們就會看到,窗口在窗口堆棧的位置是會影響到它的Z軸位置的計算的,因此,窗口的SubLayer間接地參與了窗口的Z軸位置的計算。 窗口的SubLayer值是如何影響到窗口在窗口堆棧的位置的呢?在前面一文中,在分析WindowManagerService類的成員
32、函數addWindowToListInOrderLocked的實現時提到,如果一個窗口是另外一個窗口的子窗口,那么當它的SubLayer值小于0的時候,它就會位于父窗口的下面,否則的話,就會位于父窗口的上面。 在繼續(xù)分析WindowManagerService類的成員函數assignLayersLocked之前,我們首先分析PhoneWindowManager類的成員函數windowTypeToLayerLw和subWindowTypeToLayerLw的實現,以便可以了解一個窗口的BaseLayer值和SubLayer值是如何確定的。 PhoneWindowManager類的成員函數wind
33、owTypeToLayerLw的實現如下所示:java view plain copy 在CODE上查看代碼片派生到我的代碼片public class PhoneWindowManager implements WindowManagerPolicy . public int windowTypeToLayerLw(int type) if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) return APPLICATION_LAYER; switch (type) case
34、 TYPE_STATUS_BAR: return STATUS_BAR_LAYER; case TYPE_STATUS_BAR_PANEL: return STATUS_BAR_PANEL_LAYER; case TYPE_SYSTEM_DIALOG: return SYSTEM_DIALOG_LAYER; case TYPE_SEARCH_BAR: return SEARCH_BAR_LAYER; case TYPE_PHONE: return PHONE_LAYER; case TYPE_KEYGUARD: return KEYGUARD_LAYER; case TYPE_KEYGUARD
35、_DIALOG: return KEYGUARD_DIALOG_LAYER; case TYPE_SYSTEM_ALERT: return SYSTEM_ALERT_LAYER; case TYPE_SYSTEM_ERROR: return SYSTEM_ERROR_LAYER; case TYPE_INPUT_METHOD: return INPUT_METHOD_LAYER; case TYPE_INPUT_METHOD_DIALOG: return INPUT_METHOD_DIALOG_LAYER; case TYPE_SYSTEM_OVERLAY: return SYSTEM_OVE
36、RLAY_LAYER; case TYPE_SECURE_SYSTEM_OVERLAY: return SECURE_SYSTEM_OVERLAY_LAYER; case TYPE_PRIORITY_PHONE: return PRIORITY_PHONE_LAYER; case TYPE_TOAST: return TOAST_LAYER; case TYPE_WALLPAPER: return WALLPAPER_LAYER; Log.e(TAG, "Unknown window type: " + type); return APPLICATION_LAYER; .
37、這個函數定義在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java文件中。 從這里就可以看出,每一種窗口類型type都對應有一個BaseLayer值,即每一個TYPE_XXX值都對應有一個XXX_LAYER值,其中,TYPE_XXX值定義在WindowManager.LayoutParams類中,而XXX_LAYER值就定義在PhoneWindowManager類中,它們的對應關系如圖2所示:注意,如果參數type的值小于FIRST_APPLICATION_WINDOW,或者大于L
38、AST_APPLICATION_WINDOW,或者不是圖2列出來的其中一個值,那么PhoneWindowManager類的成員函數windowTypeToLayerLw就會返回一個APPLICATION_LAYER(2)值給調用者。 PhoneWindowManager類的成員函數subWindowTypeToLayerLw的實現如下所示:java view plain copy 在CODE上查看代碼片派生到我的代碼片public class PhoneWindowManager implements WindowManagerPolicy . public int subWindowType
39、ToLayerLw(int type) switch (type) case TYPE_APPLICATION_PANEL: case TYPE_APPLICATION_ATTACHED_DIALOG: return APPLICATION_PANEL_SUBLAYER; case TYPE_APPLICATION_MEDIA: return APPLICATION_MEDIA_SUBLAYER; case TYPE_APPLICATION_MEDIA_OVERLAY: return APPLICATION_MEDIA_OVERLAY_SUBLAYER; case TYPE_APPLICATI
40、ON_SUB_PANEL: return APPLICATION_SUB_PANEL_SUBLAYER; Log.e(TAG, "Unknown sub-window type: " + type); return 0; . 這個函數定義在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java文件中。 從這里就可以看出,只有類型為TYPE_APPLICATION_PANEL、TYPE_APPLICATION_MEDIA、TYPE_APPLICATION_MEDIA
41、_OVERLAY和TYPE_APPLICATION_SUB_PANEL的窗口才對應有一個SubLayer值,它們的對應關系如圖3所示:在圖3中,TYPE_XXX值定義在WindowManager.LayoutParams類中,而XXX_LAYER值就定義在PhoneWindowManager類中。注意,有兩種特殊的多媒體窗口TYPE_APPLICATION_MEDIA和TYPE_APPLICATION_MEDIA_OVERLAY,它們是用來顯示多媒體的,例如,用來顯示視頻,并且它們都是附加在應用程序窗口之上的,但是由于它們的SubLayer值為負數,因此它們實際上是位于宿主窗口之下的。類型為T
42、YPE_APPLICATION_MEDIA的窗口有一個魔術,它會在宿主窗口里面挖一個洞,以便可以將自己顯示出來,而類型為TYPE_APPLICATION_MEDIA_OVERLAY背景一般都是透明的,位于類型為TYPE_APPLICATION_MEDIA的窗口,可以用來顯示視頻的字幕之類的東西。實際上,類型為TYPE_APPLICATION_MEDIA和TYPE_APPLICATION_MEDIA_OVERLAY的窗口也稱為SurfaceView。SurfaceView很特殊,它與普通的View的最大區(qū)別就在于它們有獨立的繪圖表面,于是它們就可以在一個獨立的子線程里面進行UI渲染。 理解了窗口
43、的BaseLayer值和SubLayer值的計算過程之外,接下來我們就可以分析WindowManagerService類的成員函數assignLayersLocked的實現了,如下所示:java view plain copy 在CODE上查看代碼片派生到我的代碼片public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor . /* How much to increment the layer for each window, to reserve room * for e
44、ffect surfaces between them. */ static final int WINDOW_LAYER_MULTIPLIER = 5; . private final void assignLayersLocked() int N = mWindows.size(); int curBaseLayer = 0; int curLayer = 0; int i; for (i=0; i<N; i+) WindowState w = mWindows.get(i); if (w.mBaseLayer = curBaseLayer | w.mIsImWindow | (i
45、> 0 && w.mIsWallpaper) curLayer += WINDOW_LAYER_MULTIPLIER; w.mLayer = curLayer; else curBaseLayer = curLayer = w.mBaseLayer; w.mLayer = curLayer; if (w.mTargetAppToken != null) w.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment; else if (w.mAppToken != null) w.mAnimLayer = w.mLayer + w.mAppToken
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年高線密度玻璃纖維直接無捻粗紗項目合作計劃書
- 2025辦公室租賃合同AA
- 2025年高密度電阻率儀項目合作計劃書
- 2025年甲基丙烯酸甲酯項目合作計劃書
- 煙囪吊裝施工方案
- 圍墻刷漆施工方案
- 假植喬木施工方案
- 2025執(zhí)業(yè)醫(yī)師資格考試考試題庫帶答案
- 家具定制服務銷售代表工作協(xié)議3篇
- 寵物轉讓合同示例3篇
- 江西卷-2025屆高考歷史4月模擬預測卷(解析版)
- bim安全教育試題及答案
- 運輸公司機務管理制度
- 婦科管理制度
- 新晉管理者培訓
- 2025-2030中國二氯乙烷行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略研究報告
- 供電公司故障搶修服務規(guī)范
- 防高處墜落 物體打擊專項施工方案
- 食品銷售初級考試試題及答案
- 全國第三屆職業(yè)技能大賽(增材制造)選拔賽理論考試題庫(含答案)
- 人教部編版初中語文七年級下冊 《15.青春之光》課件
評論
0/150
提交評論