<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] ## 編譯流程 ![](https://img.kancloud.cn/c8/ec/c8ec9deb9994f101e7b707ce8a6e06ef_950x1068.png) 1. 通過 aapt 打包 res 資源文件,生成 R.java、resources.arsc 和 res 文件(二進制 & 非二進制如 res/raw 和 pic 保持原樣); 2. 處理 .aidl 文件,生成對應的 Java 接口文件; 3. 通過 Java Compiler 編譯 R.java、Java 接口文件、Java 源文件,生成 .class 文件; 4. 通過 dex 命令,將 .class 文件和第三方庫中的 .class 文件處理生成 classes.dex; 5. 通過 apkbuilder 工具,將 aapt 生成的 resources.arsc 和 res 文件、assets 文件和 classes.dex 一起打包生成 apk; 6. 通過 Jarsigner 工具,對上面的 apk 進行 debug 或 release 簽名; 7. 通過 zipalign 工具,將簽名后的 apk 進行對齊處理。 看起來我們貌似已經回答出了這個問題的答案,但是今天是來屠龍的,所以我們不能就這么簡單的放過這個題目。 ## 從gradle Task看編譯流程 先貼一段gradle打印task耗時的代碼 1. 項目根目錄build.gradle打開 2. 加入下面代碼 ~~~ import java.util.concurrent.TimeUnit // Log timings per task. class TimingsListener implements TaskExecutionListener, BuildListener { private long startTime private timings = [] @Override void beforeExecute(Task task) { startTime = System.nanoTime() } @Override void afterExecute(Task task, TaskState taskState) { def ms = TimeUnit.MILLISECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); timings.add([ms, task.path]) task.project.logger.warn "${task.path} took ${ms}ms" } @Override void buildFinished(BuildResult result) { println "Task timings:" for (timing in timings) { if (timing[0] >= 50) { printf "%7sms %s\n", timing } } } @Override void buildStarted(Gradle gradle) {} @Override void projectsEvaluated(Gradle gradle) {} @Override void projectsLoaded(Gradle gradle) {} @Override void settingsEvaluated(Settings settings) {} } gradle.addListener new TimingsListener() 復制代碼 ~~~ 當項目運行完之后會輸出類似如下的日志,表示一個run執行之后gradle所執行的task的時間以及任務名。 ~~~ 1543ms :compiler:kaptGenerateStubsKotlin 144ms :RouterLib:packageDebugResources 1166ms :compiler:kaptKotlin 816ms :compiler:compileKotlin 401ms :compiler:compileJava 65ms :compiler:jar 122ms :app:mergeDebugResources 56ms :EmptyLoader:compileJava 170ms :app:processDebugManifest 171ms :RouterLib:parseDebugLocalResources 60ms :app:checkDebugDuplicateClasses 2416ms :RouterLib:compileDebugKotlin 122ms :RouterLib:compileDebugJavaWithJavac 124ms :secondmoudle:mergeDebugNativeLibs 1185ms :app:processDebugResources 70ms :secondmoudle:kaptGenerateStubsDebugKotlin 202ms :RouterLib:mergeDebugNativeLibs 350ms :secondmoudle:kaptDebugKotlin 158ms :secondmoudle:compileDebugJavaWithJavac 1108ms :app:kaptGenerateStubsDebugKotlin 91ms :secondmoudle:bundleLibRuntimeToJarDebug 129ms :app:mergeDebugNativeLibs 430ms :app:kaptDebugKotlin 1008ms :app:compileDebugKotlin 120ms :app:compileDebugJavaWithJavac 265ms :app:mergeDebugJavaResource 181ms :app:transformClassesAndResourcesWithAuto_registerForDebug 7262ms :app:dexBuilderDebug 1308ms :app:mergeProjectDexDebug 344ms :app:packageDebug 復制代碼 ~~~ 從上述Task列表中可以看出,其實最上面這張圖所說的編譯流程其實并不完整。 ## kapt和apt 我上篇文章說了,javaCompiler執行之前會先執行apt,生成java代碼,其任務名就是kaptGenerateStubsDebugKotlin。 [聊聊AbstractProcessor和Java編譯流程](https://juejin.cn/post/6844904197775687694 "https://juejin.cn/post/6844904197775687694") ## compiler 混入了奇怪的東西 kotlin已經被引入了很多版本了,但是kotlin的compiler其實和java compiler是不一樣的。 如果按照標準答案去回答這個問題吧,總感覺還是有所欠缺的,所以我們需要補充的一個點就是**compileDebugKotlin**。 ## 當然少不了transform 當我們使用字節碼插樁之后其實就增加了個transform的流程,也就是這個**transformClassesAndResourcesWithAuto\_registerForDebug**。 ## 那么是不是還有什么可以補充的呢? AGP在不同版本的差異還是比較大的。特別是在3.2版本之上的版本被引入了D8編譯器之后。 低版本先使用DX編譯器將class轉化為dex。 而高版本采用**d8**編譯器將class轉化為dex。 ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/7/2/1730dfb360e912a5~tplv-t2oaga2asx-watermark.awebp) ### desugar是干嘛的? Android Studio 為使用部分 Java 8 語言功能及利用這些功能的第三方庫提供內置支持。默認工具鏈對 javac 編譯器的輸出執行字節碼轉換(稱為 desugar),從而實現新語言功能。 **語法糖香歸香,但是最后.dex可是不認識你的。** ### 那么D8的優勢是什么呢??? 話不多,直接上圖。 ![](https://img.kancloud.cn/30/4e/304ec052a6949300074db3201572428d_1200x742.png) ![](https://img.kancloud.cn/b1/d1/b1d158e23afa1f921c5c3d6d93a7de72_1200x744.png) 可以看到D8在編譯速度以及編譯出來的文件體積上有了明顯的提升。 ## 那么混淆呢?? 看看最一開始的圖,有沒有發現少了混淆的流程呢!!! 在AGP3.4版本上引入了R8,也就是混淆升級版本。而且在高版本上,整體流程也其實發生了微妙的變更,將原先的流程進行了合并。 1. R8開啟前的編譯流程 ![](https://img.kancloud.cn/a1/40/a140d293fa35baeed094237cf3149315_1710x347.png) 2. R8開啟后的編譯流程 ![](https://img.kancloud.cn/d1/61/d16176d80b23d385b36591c29efe091c_1712x448.png) 說句題外話,但是R8更吃內存,機器辣雞的老哥慎重點。 ## 關于簽名 之前寫的東西有點遺漏啊,谷歌官方有說明,下面是引用啊 > 注意:您必須在應用構建過程中的兩個特定時間點之一使用 zipalign,具體在哪個時間點使用,取決于您所使用的應用簽名工具: > 如果您使用的是 apksigner,則只能在為 APK 文件簽名之前執行 zipalign。如果您在使用 apksigner 為 APK 簽名之后對 APK 做出了進一步更>改,簽名便會失效。 > 如果您使用的是 jarsigner,則只能在為 APK 文件簽名之后執行 zipalign。 [鏈接地址](https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.android.google.cn%2Fstudio%2Fcommand-line%2Fzipalign.html "https://developer.android.google.cn/studio/command-line/zipalign.html") 那么當使用V1簽名時,編譯流程順序還是6-7 而當使用的是V2的簽名時,則編譯流程順序是7-6 ## Gradle 生命周期 Gradle的構建過程可以分為三部分:**初始化階段**、**配置階段**和**執行階段**。 簡單的說下就是buildSrc先編譯,之后是根目錄的settings.gradle, 根build.gradle,最后才是module build ![](https://img.kancloud.cn/90/91/9091d64636269616285b57e5f3e5c128_633x525.png) ## apt是編譯中哪個階段 APT解析的是java 抽象語法樹(AST),屬于javac的一部分流程。大概流程:.java -> AST -> .class > [聊聊AbstractProcessor和Java編譯流程](https://juejin.cn/post/6844904197775687694 "https://juejin.cn/post/6844904197775687694") ## Dex和class有什么區別 [鏈接傳送門](https://link.juejin.cn?target=https%3A%2F%2Fwww.dazhuanlan.com%2Fharmless%2Ftopics%2F1137050 "https://www.dazhuanlan.com/harmless/topics/1137050") Class與dex的區別 1)虛擬機: class用jvm執行,dex用dvm執行 2)文檔: class中冗余信息多,dex會去除冗余信息,包含所有類,查找方便,適合手機端 JVM與DVM 1)JVM基于棧(使用棧幀,內存),DVM基于寄存器,速度更快,適合手機端 2)JVM執行Class字節碼,DVM執行DEX 3)JVM只能有一個實例,一個應用啟動運行在一個DVM DVM與ART 1)DVM:每次運行應用都需要一次編譯,效率降低。JIT 2)ART:Android5.0以上默認為ART,系統會在進程安裝后進行一次預編譯,將代碼轉為機器語言存在本地,這樣在每次運行時不用再進行編譯,提高啟動效率;。 AOP & JIT ## Transform是如何被執行的 Transform 在編譯過程中會被封裝成Task 依賴其他編譯流程的Task執行。 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3fc7dbb584af49aabf7692132a6ee414~tplv-k3u1fbpfcp-watermark.awebp?) ## Transform和其他系統Transform執行的順序 其實這個題目已經是個過期了,后面對這些都合并整合了,而且最新版的api也做了替換,要不然考慮下回懟下面試官? [Transform和Task之間有關?](https://juejin.cn/post/6875141808825991181 "https://juejin.cn/post/6875141808825991181") ## 如何監控編譯速度變慢問題 ~~~ ./gradlew xxxxx -- scan 復制代碼 ~~~ 之后會生成一個gradle的網頁,填寫下你的郵箱就好了。 另外一個相對來說比較簡單了。通過gradle原生提供的listener進行就行了。 ~~~ // 耗時統計kt化 class TimingsListener : TaskExecutionListener, BuildListener { private var startTime: Long = 0L private var timings = linkedMapOf<String, Long>() override fun beforeExecute(task: Task) { startTime = System.nanoTime() } override fun afterExecute(task: Task, state: TaskState) { val ms = TimeUnit.MILLISECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) task.path timings[task.path] = ms project.logger.warn("${task.path} took ${ms}ms") } override fun buildFinished(result: BuildResult) { project.logger.warn("Task timings:") timings.forEach { if (it.value >= 50) { project.logger.warn("${it.key} cos ms ${it.value}\n") } } } override fun buildStarted(gradle: Gradle) { } override fun settingsEvaluated(settings: Settings) { } override fun projectsLoaded(gradle: Gradle) { } override fun projectsEvaluated(gradle: Gradle) { } } gradle.addListener(TimingsListener()) 復制代碼 ~~~ ## Gradle中如何給一個Task前后插入別的任務 最簡單的可以考慮直接獲取到Task實例,之后在after和before插入一些你所需要的代碼。 另外一個就是通過`dependOn`前置和`finalizedBy`掛載一個任務 mustAfter [Gradle 使用指南 -- Gradle Task](https://link.juejin.cn?target=https%3A%2F%2Fwww.heqiangfly.com%2F2016%2F03%2F13%2Fdevelopment-tool-gradle-task%2F "https://www.heqiangfly.com/2016/03/13/development-tool-gradle-task/") 9. ksp APT Transform的區別 ksp 是kotlin專門獨立的ast語法樹 apt 是java 的ast語法樹 transform是 agp 專門修改字節碼的一個方法。 反殺時刻`AsmClassVisitorFactory`,可以看看我之前寫的那篇文章。 ## Transform上的編譯優化能做哪些? 雖然是個即將過期的api,但是大家對他的改動還是都比較多的。 首先肯定是需要完成增量編譯的,具體的可以參考我的demo工程。記住,所有的transfrom都要全量。 另外可以考慮多線程優化,將轉化操作移動到子線程內,建議使用gradle內部的共享線程。 參考agp最新做法,抽象出一個新的interface,之后通過spi串聯,之后將asm鏈式調用。我的文章也介紹過,具體的點在哪里自己盤算。 [現在準備好告別Transform了嗎](https://juejin.cn/post/7016147287889936397 "https://juejin.cn/post/7016147287889936397") ## aar 源碼切換插件原理 這個前幾天剛介紹過,原理和方案業內都差不多,`mulite-repo`應該都需要這個東西的。我的版本也比較簡陋,大廠內部肯定都會有些魔改的。 相對來說功能肯定會更豐富,更全面一點。 > [aar和源碼切換插件Plus](https://juejin.cn/post/7028599249675747341 "https://juejin.cn/post/7028599249675747341") ## 你們有哪些保證代碼質量的手段 最簡單的方式還是通過靜態掃描+pipline 處理,之后在合并mr之前進行一次攔截。 靜態掃描方式比較多,下面給大家簡單的介紹下 阿里的sonar 但是對kt的支持很糟糕,因為阿里使用,所以有很多現成的規則可以使用,但是如果從0-1接入,你可能會直接放棄。 原生的lint,可以基于原生提供的lint api,對其進行開發,支持種類也多,基本上算是一個非常優秀的方案了,但是由于文檔資料較少,對于開發的要求可能會較高。 > [AndroidLint](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FLeifzhang%2FAndroidLint "https://github.com/Leifzhang/AndroidLint") 13. 如何對第三方的依賴做靜態檢查? 魔高一尺道高一丈。lint還是能解決這個問題的。 [Tree Api+ClassScanner = 識別三方隱私權限調用](https://juejin.cn/post/7009210297340657701 "https://juejin.cn/post/7009210297340657701") 14. R.java code too large 解決方案 又是一個過期的問題,盡早升級agp版本,讓R8幫你解決這個問題,R文件完全可以內聯的。 或者用別的AGP插件的R inline也可以解決這個問題。 15. R inline 你需要注意些什么? 預掃描,先收集調用的信息,之后在進行替換。還有javac 的時候可能就因為文件過大,直接掛掉了。 16. 一個類替換父類 比如所有activity實現類替換baseactivity `class node` 直接替換 `superName` ,想起了之前另外一個問題,感覺主要是要對構造函數進行修改,否則也會出異常。 17. R8 D8 以及混淆相關的,還有R8除了混淆還能干些什么? 混淆規則有沒有碰到什么奇怪的問題? `D8`和`Dx`的區別,主要涉及到編譯速度以及編譯產物的體積,包體積大概小11%。 `R8` 則是變更了整個編譯流程的,其中我覺得最微妙的就是`java8 lambda`相關的,脫糖前后的差別還是比較大的。同時R8也少了很多之前的Transform。 R8的混淆部分,混淆除了能增加代碼閱讀難度意外,更多的是對于代碼優化方面的。 比如無效代碼優化, 同時也刪除代碼等等都可以做。 18. 編譯的時候有沒有碰到javac的常量優化 javac會將靜態常量直接優化成具體的數值。但是尤其是多模塊場景下尤其容易出現異常,看起來是個實際的常量引用,但是產物上卻是一個具體的常量值了。 。 ## 參考資料 [聊聊Android編譯流程](https://juejin.cn/post/6845166890759749645) [Android 基礎架構組面試題 | 面試](https://juejin.cn/post/7032625978023084062)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看