偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Android 編譯不得不說(shuō)的那些事兒

移動(dòng)開(kāi)發(fā) Android
作為一名 Android 工程師,我們每天都會(huì)經(jīng)歷無(wú)數(shù)次編譯。對(duì)于小項(xiàng)目來(lái)說(shuō),半分鐘或者1,2分鐘即可編譯完成,而對(duì)于大型項(xiàng)目來(lái)說(shuō),每次編譯可能需要花去一杯咖啡的時(shí)間。

 作為一名 Android 工程師,我們每天都會(huì)經(jīng)歷無(wú)數(shù)次編譯。對(duì)于小項(xiàng)目來(lái)說(shuō),半分鐘或者1,2分鐘即可編譯完成,而對(duì)于大型項(xiàng)目來(lái)說(shuō),每次編譯可能需要花去一杯咖啡的時(shí)間。可能我講具體的數(shù)字你會(huì)更有體會(huì),當(dāng)時(shí)我在微信團(tuán)隊(duì)時(shí),全量編譯 Debug 包需要 5 分鐘,而編譯 Release 包更是要超過(guò) 15 分鐘。

如果每次編譯可以減少 1 分鐘,對(duì)微信整個(gè) Android 團(tuán)隊(duì)來(lái)說(shuō)就可以節(jié)約 1200 分鐘(團(tuán)隊(duì) 40 人 × 每天編譯 30 次 × 1 分鐘)。所以說(shuō)優(yōu)化編譯速度,對(duì)于提升整個(gè)團(tuán)隊(duì)的開(kāi)發(fā)效率是非常重要的。

那應(yīng)該怎么樣優(yōu)化編譯速度呢?微信、Google、Facebook 等國(guó)內(nèi)外大廠都做了哪些努力呢?除了編譯速度之外,關(guān)于編譯你還需要了解哪些知識(shí)呢?

[[282337]]

編譯

雖然我們每天都在編譯,那到底什么是編譯呢?

你可以把編譯簡(jiǎn)單理解為,將高級(jí)語(yǔ)言轉(zhuǎn)化為機(jī)器或者虛擬機(jī)所能識(shí)別的低級(jí)語(yǔ)言的過(guò)程。對(duì)于 Android 來(lái)說(shuō),這個(gè)過(guò)程就是把 Java 或者 Kotlin 轉(zhuǎn)變?yōu)?Android 虛擬機(jī)能夠運(yùn)行的Dalvik 字節(jié)碼的過(guò)程。

編譯的整個(gè)過(guò)程會(huì)涉及詞法分析、語(yǔ)法分析 、語(yǔ)義檢查和代碼優(yōu)化等步驟。對(duì)于底層編譯原理感興趣的同學(xué),你可以挑戰(zhàn)一下編譯原理的三大經(jīng)典巨作:龍書(shū)、虎書(shū)、鯨魚(yú)書(shū)。

但今天我們的重點(diǎn)不是底層的編譯原理,而是希望一起討論 Android 編譯需要解決的問(wèn)題是什么,目前又遇到了哪些挑戰(zhàn),以及國(guó)內(nèi)外大廠又給出了什么樣的解決方案。

Android 編譯的基礎(chǔ)知識(shí)

無(wú)論是微信的編譯優(yōu)化,還是 Tinker 項(xiàng)目,都涉及比較多的編譯相關(guān)知識(shí),因此我在 Android 編譯方面研究頗多,經(jīng)驗(yàn)也比較豐富。Android 的編譯構(gòu)建流程主要包括代碼、資源以及 Native Library 三部分,整個(gè)流程可以參考官方文檔的構(gòu)建流程圖。 

Android 編譯不得不說(shuō)的那些事兒

Gradle是 Android 官方的編譯工具,它也是 GitHub 上的一個(gè)開(kāi)源項(xiàng)目。從 Gradle 的更新日志可以看到,當(dāng)前這個(gè)項(xiàng)目還更新得非常頻繁,基本上每一兩個(gè)月都會(huì)有新的版本。對(duì)于 Gradle,我感覺(jué)最痛苦的還是 Gradle Plugin 的編寫(xiě),主要是因?yàn)?Gradle 在這方面沒(méi)有完善的文檔,因此一般都只能靠看源碼或者斷點(diǎn)調(diào)試的方法。最近我所在的公司就準(zhǔn)備用Gradle搞一個(gè)渠道打包工具,對(duì)于項(xiàng)目的打包和構(gòu)建過(guò)程,也是深有體會(huì)。

但是編譯實(shí)在太重要了,每個(gè)公司的情況又各不相同,必須強(qiáng)行造一套自己的“輪子”。已經(jīng)開(kāi)源的項(xiàng)目有 Facebook 的Buck以及 Google 的Bazel。

為什么要自己“造輪子”呢?主要有下面幾個(gè)原因:

  • 統(tǒng)一編譯工具。Facebook、Google 都有專門(mén)的團(tuán)隊(duì)負(fù)責(zé)編譯工作,他們希望內(nèi)部的所有項(xiàng)目都使用同一套構(gòu)建工具,這里包括 Android、Java、iOS、Go、C++ 等。編譯工具的統(tǒng)一優(yōu)化,所有項(xiàng)目都會(huì)受益;
  • 代碼組織管理架構(gòu)。Facebook 和 Google 的代碼管理有一個(gè)非常特別的地方,就是整個(gè)公司的所有項(xiàng)目都放到同一個(gè)倉(cāng)庫(kù)里面。因此整個(gè)倉(cāng)庫(kù)非常龐大,所以他們也不會(huì)使用 Git。目前 Google 使用的是Piper,F(xiàn)acebook 是基于HG修改的,也是一種基于分布式的文件系統(tǒng);
  • 極致的性能追求。Buck 和 Bazel 的性能的確比 Gradle 更好,內(nèi)部包含它們的各種編譯優(yōu)化。但是它們或多或少都有一些定制的味道,例如對(duì) Maven、JCenter 這樣的外部依賴支持的也不是太好。 
Android 編譯不得不說(shuō)的那些事兒

“程序員最痛恨寫(xiě)文檔,還有別人不寫(xiě)文檔”,所以它們的文檔也是比較少的,如果想做二次定制開(kāi)發(fā)會(huì)感到很痛苦。如果你想把編譯工具切換到 Buck 和 Bazel,需要下很大的決心,而且還需要考慮和其他上下游項(xiàng)目的協(xié)作。當(dāng)然即使我們不去直接使用,它們內(nèi)部的優(yōu)化思路也非常值得我們學(xué)習(xí)和參考。

Gradle、Buck、Bazel 都是以更快的編譯速度、更強(qiáng)大的代碼優(yōu)化為目標(biāo),我們下面一起來(lái)看看它們做了哪些努力。

編譯速度

回想一下我們的 Android 開(kāi)發(fā)生涯,在編譯這件事情上面究竟浪費(fèi)了多少時(shí)間和生命。正如前面我所說(shuō),編譯速度對(duì)團(tuán)隊(duì)效率非常重要。

關(guān)于編譯速度,我們最關(guān)心的可能還是編譯 Debug 包的速度,尤其是增量編譯(incremental build)的速度,我們希望可以做到更加快速的調(diào)試。正如下圖所示,我們每次代碼驗(yàn)證都要經(jīng)過(guò)編譯和安裝兩個(gè)步驟。 

Android 編譯不得不說(shuō)的那些事兒

此處,我們從編譯時(shí)間和安裝時(shí)間兩個(gè)緯度來(lái)看Android的編譯速度。

  • 編譯時(shí)間。把 Java 或者 Kotlin 代碼編譯為“.class“文件,然后通過(guò) dx 編譯為 Dex 文件。對(duì)于增量編譯,我們希望編譯盡可能少的代碼和資源,最理想情況是只編譯變化的部分。但是由于代碼之間的依賴,大部分情況這并不可行。這個(gè)時(shí)候我們只能退而求其次,希望編譯更少的模塊。Android Plugin 3.0及以后的版本使用 Implementation 代替 Compile,正是為了優(yōu)化依賴關(guān)系;
  • 安裝時(shí)間。我們要先經(jīng)過(guò)簽名校驗(yàn),校驗(yàn)成功后會(huì)有一大堆的文件拷貝工作,例如 APK 文件、Library 文件、Dex 文件等。之后我們還需要編譯 Odex 文件,這個(gè)過(guò)程特別是在 Android 5.0 和 6.0 會(huì)非常耗時(shí)。對(duì)于增量編譯,最好的優(yōu)化是直接應(yīng)用新的代碼,無(wú)需重新安裝新的 APK。

對(duì)于增量編譯,我先來(lái)講講 Gradle 的官方方案Instant Run。在 Android Plugin 2.3 之前,它使用的 Multidex 實(shí)現(xiàn)。在 Android Plugin 2.3 之后,它使用 Android 5.0 新增的 Split APK 機(jī)制。

如下圖所示,資源和 Manifest 都放在 Base APK 中, 在 Base APK 中代碼只有 Instant Run 框架,應(yīng)用的本身的代碼都在 Split APK 中。 

Android 編譯不得不說(shuō)的那些事兒

Instant Run 有三種模式,如果是熱交換和溫交換,我們都無(wú)需重新安裝新的 Split APK,它們的區(qū)別在于是否重啟 Activity。對(duì)于冷交換,我們需要通過(guò)adb install-multiple -r -t重新安裝改變的 Split APK,應(yīng)用也需要重啟。

雖然無(wú)論哪一種模式,我們都不需要重新安裝 Base APK。這讓 Instant Run 看起來(lái)是不是很不錯(cuò),但是在大型項(xiàng)目里面,它的性能依然非常糟糕,主要原因是:

  • 多進(jìn)程問(wèn)題。“The app was restarted since it uses multiple processes”,如果應(yīng)用存在多進(jìn)程,熱交換和溫交換都不能生效。因?yàn)榇蟛糠謶?yīng)用都會(huì)存在多進(jìn)程的情況,Instant Run 的速度也就大打折扣。
  • Split APK 安裝問(wèn)題。雖然 Split APK 的安裝不會(huì)生成 Odex 文件,但是這里依然會(huì)有簽名校驗(yàn)和文件拷貝(APK 安裝的乒乓機(jī)制)。這個(gè)時(shí)間需要幾秒到幾十秒,是不能接受的。
  • Javac 問(wèn)題。在 Gradle 4.6 之前,如果項(xiàng)目中運(yùn)用了 Annotation Processor。那不好意思,本次修改以及它依賴的模塊都需要全量 javac,而這個(gè)過(guò)程是非常慢的,可能會(huì)需要幾十秒。這個(gè)問(wèn)題直到Gradle 4.7才解決,關(guān)于這個(gè)問(wèn)題原因的討論你可以參考這個(gè)Issue。

你還可以看看這一個(gè) Issue:“full rebuild if a class contains a constant”,假設(shè)修改的類中包含一個(gè)“public static final”的變量,那同樣也不好意思,本次修改以及它依賴的模塊都需要全量 javac。這是為什么呢?因?yàn)槌A砍厥菚?huì)直接把值編譯到其他類中,Gradle 并不知道有哪些類可能使用了這個(gè)常量。

詢問(wèn) Gradle 的工作人員,他們出給的解決方案是下面這個(gè):

  1. // 原來(lái)的常量定義: 
  2. public static final int MAGIC = 23 
  3.  
  4. // 將常量定義替換成方法:  
  5. public static int magic() { 
  6.  return 23; 

對(duì)于大型項(xiàng)目來(lái)說(shuō),這肯定是不可行的。正如我在 Issue 中所寫(xiě)的一樣,無(wú)論我們是不是真正改到這個(gè)常量,Gradle 都會(huì)無(wú)腦的全量 javac,這樣肯定是不對(duì)的。事實(shí)上,我們可以通過(guò)比對(duì)這次代碼修改,看看是否有真正改變某一個(gè)常量的值。

但是可能用過(guò)阿里的Freeline或者蘑菇街的極速編譯的同學(xué)會(huì)有疑問(wèn),它們的方案為什么不會(huì)遇到 Annotation 和常量的問(wèn)題?

事實(shí)上,它們的方案在大部分情況比 Instant Run 更快,那是因?yàn)闋奚苏_性。也就是說(shuō)它們?yōu)榱俗非蟾斓乃俣龋苯雍雎粤?Annotation 和常量改變可能帶來(lái)錯(cuò)誤的編譯產(chǎn)物。Instant Run 作為官方方案,它優(yōu)先保證的是 100% 的正確性。

當(dāng)然 Google 的人也發(fā)現(xiàn)了 Instant Run 的種種問(wèn)題,在 Android Studio 3.5 之后,對(duì)于 Android 8.0 以后的設(shè)備將會(huì)使用新的方案“Apply Changes”代替 Instant Run。目前我還沒(méi)找到關(guān)于這套方案更多的資料,不過(guò)我認(rèn)為應(yīng)該是拋棄了 Split APK 機(jī)制。

一直以來(lái),我心目中都有一套理想的編譯方案,這套方案安裝的 Base APK 依然只是一個(gè)殼 APK,真正的業(yè)務(wù)代碼放到 Assets 的 ClassesN.dex 中,它的架構(gòu)圖如下。 

Android 編譯不得不說(shuō)的那些事兒
  • 無(wú)需安裝。依然使用類似 Tinker 熱修復(fù)的方法,每次只把修改以及依賴的類插入到 pathclassloader 的最前方就可以,不熟悉的同學(xué)可以參考《微信 Android 熱補(bǔ)丁實(shí)踐演進(jìn)之路》中的 Qzone 方案;
  • Oatmeal。為了解決首次運(yùn)行時(shí) Assets 中 ClassesN.dex 的 Odex 耗時(shí)問(wèn)題,我們可以使用“安裝包優(yōu)化“中講過(guò)的 ReDex 中的黑科技:Oatmeal。它可以在 100 毫秒以內(nèi)生成一個(gè)完全解釋執(zhí)行的 Odex 文件;
  • 關(guān)閉JIT。我們通過(guò)在 AndroidManifest 中添加android:vmSafeMode=“true”來(lái)關(guān)閉虛擬機(jī)的 JIT 優(yōu)化,這樣也就不會(huì)出現(xiàn) Tinker 在Android N 混合編譯遇到的問(wèn)題。

對(duì)于編譯速度的優(yōu)化,我還有幾個(gè)建議:

更換編譯機(jī)器。對(duì)于實(shí)力雄厚的公司,直接更換 Mac 或者其他更給力的設(shè)備作為編譯機(jī),這種方式是最簡(jiǎn)單的;

Build Cache??梢詫⒋蟛糠植怀8淖兊捻?xiàng)目拆離出去,并使用遠(yuǎn)端 Cache模式保留編譯后的緩存;

升級(jí) Gradle 和 SDK Build Tools。我們應(yīng)該及時(shí)去升級(jí)最新的編譯工具鏈,享受 Google 的最新優(yōu)化成果;

使用 Buck。無(wú)論是 Buck 的 exopackage,還是代碼的增量編譯,Buck 都更加高效。但我前面也說(shuō)過(guò),一個(gè)大型項(xiàng)目如果要切換到 Buck,其實(shí)顧慮還是比較多的。在 2014 年初微信就接入了 Buck,但是因?yàn)楦渌?xiàng)目協(xié)作的問(wèn)題,導(dǎo)致在 2015 年切換回 Gradle 方案。

相比之下,可能目前最熱的 Flutter 中Hot Reload秒級(jí)編譯功能會(huì)更有吸引力。

當(dāng)然最近幾個(gè) Android Studio 版本,Google 也做了大量的其他優(yōu)化,例如使用AAPT2替代了 AAPT 來(lái)編譯 Android 資源。AAPT2 實(shí)現(xiàn)了資源的增量編譯,它將資源的編譯拆分成 Compile 和 Link 兩個(gè)步驟。前者資源文件以二進(jìn)制形式編譯 Flat 格式,后者合并所有的文件再打包。

除了 AAPT2,Google 還引入了 d8 和 R8,下面分別是 Google 提供的一些測(cè)試數(shù)據(jù),如下圖。 

Android 編譯不得不說(shuō)的那些事兒
Android 編譯不得不說(shuō)的那些事兒

那什么是 d8 和 R8 呢?除了編譯速度的優(yōu)化,它們還有哪些其他的作用?可以參考下面的介紹:Android D8 和 R8

代碼優(yōu)化

對(duì)于 Debug 包編譯,我們更關(guān)心速度。但是對(duì)于 Release 包來(lái)說(shuō),代碼的優(yōu)化更加重要,因?yàn)槲覀儠?huì)更加在意應(yīng)用的性能。

下面我就分別講講 ProGuard、d8、R8 和 ReDex 這四種我們可能會(huì)用到的代碼優(yōu)化工具。

ProGuard

在微信 Release 包 12 分鐘的編譯過(guò)程里,單獨(dú) ProGuard 就需要花費(fèi) 8 分鐘。盡管 ProGuard 真的很慢,但是基本每個(gè)項(xiàng)目都會(huì)使用到它。加入了 ProGuard 之后,應(yīng)用的構(gòu)建過(guò)程流程如下: 

Android 編譯不得不說(shuō)的那些事兒

ProGuard 主要有混淆、裁剪、優(yōu)化這三大功能,它的整個(gè)處理流程如下: 

Android 編譯不得不說(shuō)的那些事兒

其中優(yōu)化包括內(nèi)聯(lián)、修飾符、合并類和方法等 30 多種,具體介紹與使用方法你可以參考官方文檔。

D8

Android Studio 3.0 推出了d8,并在 3.1 正式成為默認(rèn)工具。它的作用是將“.class”文件編譯為 Dex 文件,取代之前的 dx 工具。 

Android 編譯不得不說(shuō)的那些事兒

d8 除了更快的編譯速度之外,還有一個(gè)優(yōu)化是減少生成的 Dex 大小。根據(jù) Google 的測(cè)試結(jié)果,大約會(huì)有 3%~5% 的優(yōu)化。 

Android 編譯不得不說(shuō)的那些事兒

R8

R8 在 Android Studio 3.1 中引入,它的志向更加高遠(yuǎn),它的目標(biāo)是取代 ProGuard 和 d8。我們可以直接使用 R8 把“.class”文件變成 Dex。 

Android 編譯不得不說(shuō)的那些事兒

同時(shí),R8 還支持 ProGuard 中混淆、裁剪、優(yōu)化這三大功能。由于目前 R8 依然處于實(shí)驗(yàn)階段,網(wǎng)上的介紹資料并不多,你可以參考下面這些資料:

ProGuard 和 R8 對(duì)比:

ProGuard and R8: a comparison of optimizers。

Jake Wharton 大神的博客最近有很多 R8 相關(guān)的文章:https://jakewharton.com/blog/。

R8 的最終目的跟 d8 一樣,一個(gè)是加快編譯速度,一個(gè)是更強(qiáng)大的代碼優(yōu)化。

ReDex

如果說(shuō) R8 是未來(lái)想取代的 ProGuard 的工具,那 Facebook 的內(nèi)部使用的ReDex其實(shí)已經(jīng)做到了。Facebook 內(nèi)部的很多項(xiàng)目都已經(jīng)全部切換到 ReDex,不再使用 ProGuard 了。跟 ProGuard 不同的是,它直接輸入的對(duì)象是 Dex,而不是“.class”文件,也就是它是直接針對(duì)最終產(chǎn)物的優(yōu)化,所見(jiàn)即所得。

在前面的文章中,我已經(jīng)不止一次提到 ReDex 這個(gè)項(xiàng)目,因?yàn)樗锩娴墓δ軐?shí)在是太強(qiáng)大了,具體可以參考專欄前面的文章《包體積優(yōu)化(上):如何減少安裝包大小?》。

  • Interdex:類重排和文件重排、Dex 分包優(yōu)化;
  • Oatmeal:直接生成的 Odex 文件;
  • StripDebugInfo:去除 Dex 中的 Debug 信息。

此外,ReDex 中例如Type Erasure和去除代碼中的Aceess 方法也是非常不錯(cuò)的功能,它們無(wú)論對(duì)包體積還是應(yīng)用的運(yùn)行速度都有幫助,因此我也鼓勵(lì)你去研究和實(shí)踐一下它們的用法和效果。

但是 ReDex 的文檔也是萬(wàn)年不更新的,而且里面摻雜了一些 Facebook 內(nèi)部定制的邏輯,所以它用起來(lái)的確非常不方便。目前我主要還是直接研究它的源碼,參考它的原理,然后再直接單獨(dú)實(shí)現(xiàn)。

事實(shí)上,Buck 里面其實(shí)也還有很多好用的東西,但是文檔里面依然什么都沒(méi)有提到,所以還是需要“read the source code”。

Library Merge 和 Relinker

  • 多語(yǔ)言拆分
  • 分包支持
  • ReDex 支持

持續(xù)交付

Gradle、Buck、Bazel 它們代表的都是狹義上的編譯,我認(rèn)為廣義的編譯應(yīng)該包括打包構(gòu)建、Code Review、代碼工程管理、代碼掃描等流程,也就是業(yè)界最近經(jīng)常提起的持續(xù)集成。 

Android 編譯不得不說(shuō)的那些事兒

目前最常用的持續(xù)集成工具有 Jenkins、GitLab CI、Travis CI 等,GitHub 也有提供自己的持續(xù)集成服務(wù)。每個(gè)大公司都有自己的持續(xù)集成方案,例如騰訊的 RDM、阿里的摩天輪、大眾點(diǎn)評(píng)的MCI等。

下面我來(lái)簡(jiǎn)單講一下我對(duì)持續(xù)集成的一些經(jīng)驗(yàn)和看法:

  • 自定義代碼檢查。每個(gè)公司都會(huì)有自己的編碼規(guī)范,代碼檢查的目的在于防止不符合規(guī)范的代碼提交到遠(yuǎn)程倉(cāng)庫(kù)中。比如微信就定義了一套代碼規(guī)范,并且寫(xiě)了專門(mén)的插件來(lái)檢測(cè)。例如日志規(guī)范、不能直接使用 new Thread、new Handler 等,而且違反者將會(huì)得到一定的懲罰。自定義代碼檢測(cè)可以通過(guò)完全自己實(shí)現(xiàn)或者擴(kuò)展 Findbugs 插件,例如美團(tuán)它們就利用 Findbugs 實(shí)現(xiàn)了Android 漏洞掃描工具 Code Arbiter;
  • 第三方代碼檢查。業(yè)界比較常用的代碼掃描工具有收費(fèi)的 Coverity,以及 Facebook 開(kāi)源的Infer,例如空指針、多線程問(wèn)題、資源泄漏等很多問(wèn)題都可以掃描出來(lái)。除了增加檢測(cè)流程,我最大的體會(huì)是需要同時(shí)增加人員的培訓(xùn)。我遇到很多開(kāi)發(fā)者為了解決掃描出來(lái)的問(wèn)題,空指針就直接判空、多線程就直接加鎖,最后可能會(huì)造成更加嚴(yán)重的問(wèn)題;
  • Code Review。關(guān)于 Code Review,集成 GitLab、Phabricator 或者 Gerrit 都是不錯(cuò)的選擇。我們一定要重視 Code Review,這也是給其他人展示我們“偉大”代碼的機(jī)會(huì)。而且我們自己應(yīng)該是第一個(gè) Code Reviewer,在給別人 Review 之前,自己先以第三者的角度審視一次代碼。這樣先通過(guò)自己這一關(guān)的考驗(yàn),既尊重了別人的時(shí)間,也可以為自己樹(shù)立良好的技術(shù)品牌。

持續(xù)集成涉及的流程有很多,你需要結(jié)合自己團(tuán)隊(duì)的現(xiàn)狀。如果只是一味地去增加流程,有時(shí)候可能適得其反。

總結(jié)

在 Android 8.0,Google 引入了Dexlayout庫(kù)實(shí)現(xiàn)類和方法的重排,F(xiàn)acebook 的 Buck 也第一時(shí)間引入了 AAPT2。ReDex、d8、R8 其實(shí)都是相輔相成,可以看到 Google 也在攝取社區(qū)的知識(shí),但同時(shí)我們也會(huì)從 Google 的新技術(shù)發(fā)展里尋求思路。

我在寫(xiě)今天的內(nèi)容時(shí)還有另外一個(gè)體會(huì),Google 為了解決 Android 編譯速度的問(wèn)題,花了大量的力氣結(jié)果卻不盡如人意。我想說(shuō)如果我們敢于跳出系統(tǒng)的制約,可能才會(huì)徹底解決這個(gè)問(wèn)題,正如在 Flutter 上面就可以完美實(shí)現(xiàn)秒級(jí)編譯。其實(shí)做人、做事也是如此,我們經(jīng)常會(huì)陷入局部最優(yōu)解的困局,或者走進(jìn)“思維怪圈”,這時(shí)如果能跳出路徑依賴,從更高的維度重新思考、審視全局,得到的體會(huì)可能會(huì)完全不一樣。

 

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2012-10-31 10:07:00

JS前端Web

2010-08-27 10:37:43

無(wú)線標(biāo)準(zhǔn)WAPI

2012-10-31 10:36:17

js前端JavaScript頁(yè)面構(gòu)建

2011-04-27 10:31:29

兼容墨盒用戶體驗(yàn)

2016-09-27 23:47:42

2018-07-20 22:22:21

紅帽混合云API

2019-12-24 14:04:59

PythonExcel數(shù)據(jù)處理

2019-10-18 17:55:03

安全運(yùn)營(yíng)

2020-06-15 08:19:00

ZooKeeperEureka

2011-04-26 09:44:05

Power Cloud

2010-05-26 15:17:06

Windows Emb

2018-08-06 11:59:00

混合云數(shù)據(jù)中心上云

2015-08-31 14:12:12

DockerKubernetesPaaS

2017-08-10 16:54:47

MySQL優(yōu)化MySQL

2024-02-04 00:00:03

運(yùn)維Linux磁盤(pán)

2014-10-21 11:05:52

英特爾Linux

2015-01-16 16:44:50

2018-09-17 15:30:39

區(qū)塊鏈金融行業(yè)支付

2015-12-11 11:58:51

光合資本

2014-04-15 10:18:24

中文女工科男
點(diǎn)贊
收藏

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