Android 加快apk的構建速度,如何把編譯時間從130秒降到17秒

2022-02-01 鴻洋

今天就不推薦了,因為本篇文章非常長,也有一定的深度,但是真的是一篇好文,值得細細學習~~

本文由typ0520投稿。

typ0520的博客地址:

http://www.jianshu.com/u/99c659a8bb36

公司的項目代碼比較多,每次調試改動java文件後要將近2分鐘才能跑起來,實在受不了。

在網上找了一大堆配置參數也沒有很明顯的效果, 嘗試使用instant run效果也不怎麼樣,然後又嘗試使用freeline編譯速度還可以但是不穩定,每次失敗後全量編譯很耗費時間,既然沒有好的方案就自己嘗試做。

項目地址: 

https://github.com/typ0520/fastdex


注: 本文對gradle task做的說明都建立在關閉instant run的前提下注: 本文所有的代碼、gradle任務名、任務輸出路徑、全部使用debug這個buildType作說明

優化構建速度首先需要找到那些環節導致構建速度這麼慢,把下面的代碼放進app/build.gradle裡把時間花費超過50ms的任務時間列印出來

執行./gradlew assembleDebug,經過漫長的等待得到以下輸出

從上面的輸出可以發現總的構建時間為100秒左右(上面的輸出不是按照真正的執行順序輸出的),transformClassesWithDexForDebug任務是最慢的耗費了65秒,它就是我們需要重點優化的任務。

首先講下構建過程中主要任務的作用,方便理解後面的hook點

mergeDebugResources的作用

解壓所有的aar包輸出到app/build/intermediates/exploded-aar,並且把所有的資源文件合併到app/build/intermediates/res/merged/debug目錄裡;

processDebugManifest的作用

把所有aar包裡的AndroidManifest.xml中的節點,合併到項目的AndroidManifest.xml中,並根據app/build.gradle中當前buildType的manifestPlaceholders配置內容替換manifest文件中的佔位符,最後輸出到app/build/intermediates/manifests/full/debug/AndroidManifest.xml

processDebugResources的作用

調用aapt生成項目和所有aar依賴的R.java,輸出到app/build/generated/source/r/debug目錄

生成資源索引文件app/build/intermediates/res/resources-debug.ap_

把符號表輸出到app/build/intermediates/symbols/debug/R.txt

compileDebugJavaWithJavac的作用

這個任務是用來把java文件編譯成class文件,輸出的路徑是app/build/intermediates/classes/debug

編譯的輸入目錄有

項目源碼目錄,默認路徑是app/src/main/java,可以通過sourceSets的dsl配置,允許有多個(列印project.android.sourceSets.main.java.srcDirs可以查看當前所有的源碼路徑,具體配置可以參考android-doc(http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Sourcesets-and-Dependencies)

app/build/generated/source/aidl

app/build/generated/source/buildConfig

app/build/generated/source/apt(繼承javax.annotation.processing.AbstractProcessor做動態代碼生成的一些庫,輸出在這個目錄,具體可以參考Butterknife 和 Tinker的代碼

transformClassesWithJarMergingForDebug的作用

把compileDebugJavaWithJavac任務的輸出app/build/intermediates/classes/debug,和app/build/intermediates/exploded-aar中所有的classes.jar和libs裡的jar包作為輸入,合併起來輸出到app/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar,我們在開發中依賴第三方庫的時候有時候報duplicate entry:xxx 的錯誤,就是因為在合併的過程中在不同jar包裡發現了相同路徑的類

transformClassesWithMultidexlistForDebug的作用

這個任務花費的時間也很長將近8秒,它有兩個作用

掃描項目的AndroidManifest.xml文件和分析類之間的依賴關係,計算出那些類必須放在第一個dex裡面,最後把分析的結果寫到app/build/intermediates/multi-dex/debug/maindexlist.txt文件裡面

生成混淆配置項輸出到app/build/intermediates/multi-dex/debug/manifest_keep.txt文件裡

項目裡的代碼入口是manifest中application節點的屬性android.name配置的繼承自Application的類,在android5.0以前的版本系統只會加載一個dex(classes.dex),classes2.dex ..classesN.dex 一般是使用android.support.multidex.MultiDex加載的,所以如果入口的Application類不在classes.dex裡5.0以下肯定會掛掉,另外當入口Application依賴的類不在classes.dex時初始化的時候也會因為類找不到而掛掉,還有如果混淆的時候類名變掉了也會因為對應不了而掛掉,綜上所述就是這個任務的作用

transformClassesWithDexForDebug的作用

這個任務的作用是把包含所有class文件的jar包轉換為dex,class文件越多轉換的越慢
輸入的jar包路徑是app/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar
輸出dex的目錄是build/intermediates/transforms/dex/debug/folders/1000/1f/main

注意編寫gradle插件時如果需要使用上面這些路徑不要硬編碼的方式寫死,最好從Android gradle api中去獲取路徑,防止以後發生變化

結合上面的這些信息重點需要優化的是transformClassesWithDexForDebug這個任務,我的思路是:

第一次全量打包執行完transformClassesWithDexForDebug任務後把生成的dex緩存下來,並且在執行這個任務前對當前所有的java源文件做快照,以後補丁打包的時候通過當前所有的java文件信息和之前的快照做對比,找出變化的java文件進而得到那些class文件發生變化,然後把app/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar中沒有變化的class移除掉,僅把變化class送去生成dex,然後選擇一種熱修複方案把這個dex當做補丁dex加載進來,有思路了後面就是攻克各個技術點。

(1)如何拿到transformClassesWithDexForDebug任務執行前後的生命周期

參考了Tinker項目的代碼,找到下面的實現

把上面的代碼放進app/build.gradle執行./gradlew assembleDebug

從上面的日誌輸出證明這個hook點是有效的,在全量打包時執行transform前可以對java源碼做快照,執行完以後把dex緩存下來;在補丁打包執行transform之前對比快照移除沒有變化的class,執行完以後合併緩存的dex放進dex輸出目錄

(2)如何做快照與對比快照並拿到變化的class列表

執行下面的代碼可以獲取所有的項目源碼目錄

project.android.sourceSets.main.java.srcDirs.each { srcDir->    println("==srcDir: ${srcDir}")}

sample工程沒有配置sourceSets,因此輸出的是app/src/main/java

給源碼目錄做快照,直接通過文件複製的方式,把所有的srcDir目錄下的java文件複製到快照目錄下

(這裡有個坑,不要使用project.copy {}它會使文件的lastModified值發生變化,直接使用流copy並且要用源文件的lastModified覆蓋目標文件的lastModified)

通過java文件的長度和上次修改時間兩個要素對比可以得知同一個文件是否發生變化,通過快照目錄沒有某個文件而當前目錄有某個文件可以得知增加了文件,通過快照目錄有某個文件但是當前目錄沒有可以得知刪除文件(為了效率可以不處理刪除,僅造成緩存裡有某些用不到的類而已)


舉個例子來說假如項目源碼的路徑為/Users/tong/fastdex/app/src/main/java,做快照時把這個目錄複製到/Users/tong/fastdex/app/build/fastdex/snapshoot下,當前快照裡的文件樹為

com└── dx168    └── fastdex        └── sample            ├── CustomView.java            ├── MainActivity.java            └── SampleApplication.java

如果當前源碼路徑的內容發生變化,當前的文件樹為

com└── dx168    └── fastdex        └── sample            ├── CustomView.java            ├── MainActivity.java(內容已經被修改)            ├── New.java            └── SampleApplication.java

通過文件遍歷對比可以得到這個變化的相對路徑列表

通過這個列表進而可以得知變化的class有

但是java文件編譯的時候如果有內部類還會有其它的一些class輸出,比如拿R文件做下編譯,它的編譯輸出如下

另外如果使用了butterknife,還會生成binder類,比如編譯MainActivity.java時生成了

com/dx168/fastdex/sample/MainActivity$$ViewBinder.class

結合上面幾點可以獲取所有變化class的匹配模式

com/dx168/fastdex/sample/MainActivity.class

com/dx168/fastdex/sample/MainActivity$*.class

com/dx168/fastdex/sample/New.class

com/dx168/fastdex/sample/New$*.class

有了上面的匹配模式就可以在補丁打包執行transform前把app/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar中沒有變化的class全部移除掉

然後就可以使用patchJar作為輸入jar生成補丁dex

注: 這種映射方案如果開啟了混淆就對應不上了,需要解析混淆以後產生的mapping文件才能解決,不過我們也沒有必要在開啟混淆的buildType下做開發開發調試,所以暫時可以不做這個事情

(3)熱修複方案的選擇

有了補丁dex,就可以選擇一種熱修複方案把補丁dex加載進來,這裡方案有好幾種,為了簡單直接選擇android.support.multidex.MultiDex以dex插樁的方式來加載,只需要把dex按照google標準(classes.dex、classes2.dex、classesN.dex)排列好就行了,這裡有兩個技術點

由於patch.dex和緩存下來dex裡面有重複的類,當加載引用了重複類的類時會造成pre-verify的錯誤,具體請參考QQ空間團隊寫的安卓App熱補丁動態修復技術介紹(https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a)


這篇文章詳細分析了造成pre-verify錯誤的原因,文章裡給的解決方案是往所有引用被修復類的類中插入一段代碼,並且被插入的這段代碼所在的類的dex必須是一個單獨的dex,這個dex我們事先準備好,叫做fastdex-runtime.dex(https://github.com/typ0520/fastdex/tree/master/runtime/src/main/java/com/dx168/fastdex/runtime),它的代碼結構是

AntilazyLoad.java就是在注入時被引用的類
MultiDex.java是用來加載classes2.dex - classesN.dex的包,為了防止項目沒有依賴MultiDex,所以把MultiDex的代碼copy到了我們的package下
FastdexApplication.java的作用後面在說

結合我們的項目需要在全量打包前把app/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar中所有的項目代碼的class全部動態插入代碼(第三方庫由於不在我們的修復範圍內所以為了效率忽略掉),具體的做法是往所有的構造方法中添加對com.dx168.fastdex.runtime.antilazyload.AntilazyLoad的依賴,如下面的代碼所示

動態往class文件中插入代碼使用的是asm(http://asm.ow2.org/),我把做測試的時候找到的一些相關資料和代碼都放到了github上面點我查看(https://github.com/typ0520/fastdex-test-project/tree/master/asm-test),代碼比較多隻貼出來一部分,具體請查看ClassInject.groovy(https://github.com/typ0520/fastdex/blob/master/buildSrc/src/main/groovy/com/dx168/fastdex/build/util/ClassInject.groovy)

處理完pre-verify問題,接下來又出現坑了,當補丁dex打好後假如緩存的dex有兩個(classes.dex classes2.dex),那麼合併dex後的順序就是

fastdex-runtime.dex 、patch.dex、classes.dex 、classes2.dex (patch.dex必須放在緩存的dex之前才能被修復)

fastdex-runtime.dex  => classes.dexpatch.dex            => classes2.dexclasses.dex          => classes3.dexclasses2.dex         => classes4.dex

在講解transformClassesWithMultidexlistForDebug任務時有說過程序入口Application的問題,假如patch.dex中不包含入口Application,apk啟動的時候肯定會報類找不到的錯誤,那麼怎麼解決這個問題呢

第一個方案:

把transformClassesWithMultidexlistForDebug任務中輸出的maindexlist.txt中所有的class都參與patch.dex的生成

第二種方案:

對項目的入口Application做代理,並把這個代理類放在第一個dex裡面,項目的dex按照順序放在後面

第一種方案方案由於必須讓maindexlist.txt中大量的類參與了補丁的生成,與之前儘量減少class文件參與dex生成的思想是相衝突的,效率相對於第二個方案比較低,另外一個原因是無法保證項目的Application中使用了MultiDex;

第二種方案沒有上述問題,但是如果項目代碼中有使用getApplication()做強轉就會出問題(參考issue#2 https://github.com/typ0520/fastdex/issues/2),instant run也會有同樣的問題,它的做法是hook系統的api運行期把Application還原回來,所以強轉就不會有問題了,請參考MonkeyPatcher.java(https://android.googlesource.com/platform/tools/base/+/gradle_2.2.0/instant-run/instant-run-server/src/main/java/com/android/tools/fd/runtime/MonkeyPatcher.java需要翻牆才能打開,如果看不了就參考FastdexApplication.java https://github.com/typ0520/fastdex/blob/master/runtime/src/main/java/com/dx168/fastdex/runtime/FastdexApplication.java的monkeyPatchApplication方法)

綜上所述最終選擇了第二種方案以下是fastdex-runtime.dex中代理Application的代碼

根據之前的任務說明生成manifest文件的任務是processDebugManifest,我們只需要在這個任務執行完以後做處理,創建一個實現類為FastdexManifestTask的任務,核心代碼如下

使用下面的代碼把這個任務加進去並保證在processDebugManifest任務執行完畢後執行

處理完以後manifest文件application節點android.name屬性的值就變成了com.dx168.fastdex.runtime.FastdexApplication,並且把原來項目的Application的名字寫入到meta-data中,用來運行期給FastdexApplication去讀取

開發完以上功能後做下面的四次打包做時間對比(其實只做一次並不是太準確,做幾十次測試取時間的平均值這樣才最準)

1.刪除build目錄第一次全量打包(不開啟fastdex)

2.刪除build目錄第一次全量打包(開啟fastdex)

3.在開啟fastdex第一次全量打包完成後,關掉fastdex修改sample工程的MainActivity.java

4.在開啟fastdex第一次全量打包完成後,仍然開啟fastdex修改sample工程的MainActivity.java

打包編號總時間transform時間

1

1 mins 46.678s

61770 ms

2

1 mins 57.764s

78990 ms

3

1 mins 05.394s

60718 ms

4

16.5s

1005 ms

通過1和2對比發現,開啟fastdex進行第一次全量的打包時的時間花費比不開啟多了10秒左右,這個主要是注入代碼和IO上的開銷

通過2和3對比發現,開啟fastdex進行補丁打包時的時間花費比不開啟快了60秒左右,這就是期待已久的構建速度啊^_^

剛激動一會就尼瑪報了一個錯誤,當修改activity_main.xml時往裡面增加一個控制項

打出來的包啟動的時候就直接crash掉了

錯誤信息裡的意思是為CustomView的tv1欄位,尋找id=2131493007的view時沒有找到,先反編譯報錯的apk,找到報錯的地方

CustomView$$ViewBinder.bind

CustomView$$ViewBinder這個類是ButterKnife動態生成的,這個值的來源是CustomView的tv1欄位上面的註解,CustomView.class反編譯後如下

看到這裡是不是覺得奇怪,CustomView的源碼明明是

在編譯以後R.id.tv1怎麼就變成數字2131493007了呢,原因是java編譯器做了一個性能優化,如果發現源文件引用的是一個帶有final描述符的常量,會直接做值copy

反編譯最後一次編譯成功時的R.class結果如下(
app/build/intermediates/classes/debug/com/dx168/fastdex/sample/R.class)

經過分析,當全量打包時R.id.tv1 = 2131493007,由於R文件中的id都是final的,所以引用R.id.tv1的地方都被替換為它對應的值2131493007了;

當在activity_layout.xml中添加名字為tv2的控制項,然後進行補丁打包時R.id.tv1的值變成了2131493008,而緩存的dex對應節點的值還是2131493007,所以在尋找id為2131493007對應的控制項時因為找不到而掛掉

我的第一個想法是如果在執行完processDebugResources任務後,把R文件裡id類的所有欄位的final描述符去掉就可以把值copy這個編譯優化繞過去 =>

去掉以後在執行compileDebugJavaWithJavac時編譯出錯了


出錯的原因是註解只能引用帶final描述符的常量,除此之外switch語句的case也必須引用常量,具體請查看oracle對常量表達式(http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28)的說明

如果採取這個方案,對id的引用就不能使用常量表達式,像ButterKnife這樣的view依賴注入的框架都不能用了,限制性太大這個想法就放棄了

還有一個思路就是修改aapt的源碼,使多次打包時名字相同id的值保持一致,這個肯定能解決不過工作量太大了就沒有這樣做,之後採用了一個折中的辦法,就是每次把項目中的所有類(除去第三方庫)都參與dex的生成,雖然解決了這個問題但效率一下子降低好多,需要將近40秒才能跑起來還是很慢

這個問題困擾了好久,直到tinker開源後閱讀它的源碼TinkerResourceIdTask.groovy時,發現它們也碰到了同樣的問題,並有了一個解決方案,我們的場景和tinker場景在這個問題上是一模一樣的,直接照抄代碼就解決了這個問題,重要的事情說三遍,感謝tinker、感謝tinker、感謝tinker!!

tinker的解決方案是,打補丁時根據用戶配置的resourceMapping文件(每次構建成功後輸出的app/build/intermediates/symbols/debug/R.txt),生成public.xml和ids.xml然後放進app/build/intermediates/res/merged/debug/values目錄裡,aapt在處理的時候會根據文件裡的配置規則去生成,具體這塊的原理請看老羅的文章Android應用程式資源的編譯和打包過程分析(http://blog.csdn.net/luoshengyang/article/details/8744683)(在裡面搜索public.xml)這裡面有詳細的說明

同上並結合我們的場景,第一次全量打包成功以後把app/build/intermediates/symbols/debug/R.txt緩存下來,補丁打包在執行processResources任務前,根據緩存的符號表R.txt去生成public.xml和ids.xml然後放進app/build/intermediates/res/merged/debug/values目錄裡,這樣相同名字的id前後的兩次構建值就能保持一致了,代碼如下FastdexResourceIdTask.groovy

如果項目中的資源特別多,第一次補丁打包生成public.xml和ids.xml時會佔用一些時間,最好做一次緩存,以後的補丁打包直接使用緩存的public.xml和ids.xml**

解決了上面的原理性問題後,接下來繼續做優化,上面有講到 transformClassesWithMultidexlistForDebug任務的作用,由於採用了隔離Application的做法,所有的項目代碼都不在classes.dex中,這個用來分析那些項目中的類需要放在classes.dex的任務就沒有意義了,直接禁掉它

禁掉以後,執行./gradle assembleDebug,在構建過程中掛掉了

從上面的日誌的第一行發現transformClassesWithMultidexlistForDebug任務確實禁止掉了,後面跟著一個SKIPPED的輸出,但是執行transformClassesWithDexForDebug任務時報app/build/intermediates/multi-dex/debug/maindexlist.txt (No such file or directory)
,原因是transformClassesWithDexForDebug任務會檢查這個文件是否存在,既然這樣就在執行transformClassesWithDexForDebug任務前創建一個空文件,看是否還會報錯,代碼如下

再次執行./gradle assembleDebug

這次構建成功說明創建空文件的這種方式可行;

我們公司的項目在使用的過程中,發現補丁打包時雖然只改了一個java類,但構建時執行compileDebugJavaWithJavac任務還是花了13秒

經過分析由於我們使用了butterknife和tinker,這兩個裡面都用到了javax.annotation.processing.AbstractProcessor這個接口做代碼動態生成,所以項目中的java文件如果很多,挨個掃描所有的java文件並且做操作會造成大量的時間浪費,其實他們每次生成的代碼幾乎都是一樣的,因此如果補丁打包時能把這個任務換成自己的實現,僅編譯和快照對比變化的java文件,並把結果輸出到app/build/intermediates/classes/debug,覆蓋原來的class,能大大提高效率,部分代碼如下,詳情看FastdexCustomJavacTask.groovy

執行./gradlew assembleDebug ,再來一次

一下子快了10秒左右,good


既然有緩存,就有緩存過期的問題,假如我們添加了某個第三方庫的依賴(依賴關係發生變化),並且在項目代碼中引用了它,如果不清除緩存打出來的包運行起來後肯定會包類找不到,所以需要處理這個事情。


首先怎麼拿到依賴關係呢?通過以下代碼可以獲取一個依賴列表

輸入如下

可以在第一次全量打包時,和生成項目源碼目錄快照的同一個時間點,獲取一份當前的依賴列表並保存下來,當補丁打包時在獲取一份當前的依賴列表,與之前保存的作對比,如果發生變化就把緩存清除掉

另外最好提供一個主動清除緩存的任務

先來一個清除所有緩存的任務

project.tasks.create("fastdexCleanAll", FastdexCleanTask)

然後在根據buildType、flavor創建對應的清除任務

提高穩定性和容錯性,這個是最關鍵的

目前補丁打包的時候,是把沒有變化的類從app/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar中移除,如果能hook掉transformClassesWithJarMergingForDebug這個任務,僅把發生變化的class參與combined.jar的生成,能夠在IO上省出很多的時間

目前給項目源碼目錄做快照,使用的是文件copy的方式,如果能僅僅只把需要的信息寫在文本文件裡,能夠在IO上省出一些時間

目前還沒有對libs目錄中發生變化做監控,後續需要補上這一塊

apk的安裝速度比較慢(尤其是ART下由於在安裝時對應用做AOT編譯,所以造成安裝速度特別慢,具體請參考張邵文大神的文章Android N混合編譯與對熱補丁影響解析),通過socket把代碼補丁和資源補丁發送給app,做到免安裝


全量打包時的流程:

合併所有的class文件生成一個jar包

掃描所有的項目代碼並且在構造方法裡添加對com.dx168.fastdex.runtime.antilazyload.AntilazyLoad類的依賴這樣做的目的是為了解決class verify的問題,安卓App熱補丁動態修復技術介紹(https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a)

對項目代碼做快照,為了以後補丁打包時對比那些java文件發生了變化

對當前項目的所以依賴做快照,為了以後補丁打包時對比依賴是否發生了變化,如果變化需要清除緩存

調用真正的transform生成dex

緩存生成的dex,並且把fastdex-runtime.dex插入到dex列表中,假如生成了兩個dex,classes.dex classes2.dex 需要做一下操作

fastdex-runtime.dex => classes.dex
classes.dex => classes2.dex
classes2.dex => classes3.dex

然後運行期在入口Application(com.dx168.fastdex.runtime.FastdexApplication)使用MultiDex把所有的dex加載進來

@see com.dx168.fastdex.build.transform.FastdexTransform

 7.保存資源映射表,為了保持id的值一致,詳情看

@see com.dx168.fastdex.build.task.FastdexResourceIdTask


補丁打包時的流程

1.檢查緩存的有效性

2.掃描所有變化的java文件並編譯成class

3.合併所有變化的class並生成jar包

4.生成補丁dex

5.把所有的dex按照一定規律放在 

transformClassesWithMultidexlistFor${variantName}任務的輸出目錄
fastdex-runtime.dex => classes.dex
patch.dex => classes2.dex
dex_cache.classes.dex => classes3.dex
dex_cache.classes2.dex => classes4.dex
dex_cache.classesN.dex => classes(N + 2).dex

整個項目的代碼目前已經開源了
https://github.com/typ0520/fastdex

3月29日,100offer 邀請了被稱作「架構師的搖籃」的阿里中間件參加知乎 Live。

阿里中間件承載了世界上最有挑戰的場景,應對了一次次雙十一的流量洪峰,他們對人才的要求指引優秀架構師之路的方向。

掃描二維碼,參與 Live 會讓你了解怎麼樣的架構師才能勝任頂尖團隊中的工作

如果你有想學習的文章直接留言,我會整理徵稿。如果你有好的文章想和大家分享歡迎投稿,直接向我投遞文章連結即可。

歡迎長按下圖->識別圖中二維碼或者掃一掃關注我的公眾號:

相關焦點

  • Android減包 - 減少APK大小
    res/: 包含了沒被被編譯到resources.arsc的資源。lib/: 包含了針對處理器層面的被編譯的代碼。這個目錄針對每個平臺類型都有一個子目錄,比如armeabi, armeabi-v7a, arm64-v8a, x86, x86_64和mips。
  • WSL2 編譯 Android 版本 emu-ex-plus-alpha 的 Snes9x EX+
    /configuremakesudo make install## 2.5 準備編譯必須的 *.m4 文件刪除原目錄,軟連接 /usr/share/aclocal 到 /usr/local/share/aclocalsudo rm -rf /usr/local/share/aclocalsudo
  • 深入探索 Android 包瘦身(上)
    和 6.0 系統來說,耗費的時間比較久,而 Android 7.0 之後有了 混合編譯,所以還可以接受。開啟後,Android 構建工具會通過 ResourceUsageAnalyzer 來檢查哪些資源是無用的,當檢查到無用的資源時會把該資源替換成預定義的版本。主要是針對 .png、.9.png、.xml 提供了 TINY_PNG、TINY_9PNG、TINY_XML 這 3 個 byte 數組的預定義版本。
  • Android APK瘦身實踐
    2G和3G網絡,或者按流量收費的設備是多麼的浪費,這篇文章介紹了如何對APK進行瘦身,幫助更多用戶毫不猶顧忌地下載你們的APP。APK大小 10903 KB。如何知道哪些xml和圖片未被使用到?使用Android Studio的Lint,步驟:點擊菜單欄 Analyze ->  Run Inspection by Name -> unused resources ->  Moudule 『app』 -> OK,這樣會搜出來哪些未被使用到未使用到xml和圖片,如下:
  • xmake-gradle 插件 v1.0.7 發布,集成 xmake 快速構建 Android JNI...
    ,對新手友好,另外功能也很強大,具體有那些功能,大家可以到 xmake 項目主頁看下相關介紹。而之前想要用 xmake 編譯 android so 庫,只能通過命令行的方式比如:xmake f -p android --ndk=xxxxxmake雖然已經很簡單了,但是如果要跟 android apk/aar 一起打包集成,還是需要很多額外的工作,為了提高開發者的效率,我最近新整了這個 grafle 插件,來無縫集成到
  • Android NDK層編譯OpenCV代碼開發詳解
    開發中會經常遇到的要求,因為OpenCV4Android SDK多數Java代碼都是基於JNI調用,如果對於實時性與應用要求比較高的場合來說,多次頻繁調用JNI層本身就會導致很大的資源開銷,這個時候就需要將全部的處理封裝在C++層,在C++中調用OpenCV相關API函數,同時通過在JNI層面定義本地方法,力爭做到每次處理圖像,只調用JNI一次,這樣就會達到比較高的運行速度與好的用戶體驗。
  • 微信 for Android v8.0.6 正式版
    30秒以內,群主可以直接在輸入框中「@所有人」了,群公告加入了橫幅樣式。微信 8.0.3 for Android 正式版(2021-04-22)- 朋友圈短視頻15秒限制增加至30秒以內。- 群主可以直接在輸入框中「@所有人」了,效果和QQ類似。- 群公告加入了橫幅樣式,多公告功能依然不支持,發布新公告前依舊要將老公告刪除。- 下拉麵板搜索欄改進,此外如果小程序過多,由舊版窗口右上角的「…」改為了「更多」。
  • kotlin-android-extensions插件也被廢棄了?扶我起來
    某天我將項目工程的Gradle版本升級到了最新,然後構建項目時發現了這樣一個警告提示:Google明確地告訴我們,kotlin-android-extensions插件已被廢棄,現在推薦使用ViewBinding來進行替代。
  • Android藍牙框架
    代碼來源於Android P,本文相關代碼:client:frameworks/base/core/java/android/bluetooth/*system/bt/binder/android/bluetooth/**.aidlservie:framework/base/services/core/java/com/android/server
  • 外媒分析:如何以每公裡2分43秒速度奔跑?
    參考消息網12月8日報導如何以每公裡2分43秒的速度奔跑?坎迪將世界紀錄縮短了近30秒,從卡姆沃羅爾在2019年創造的58:01縮短至57:32,相當於平均每公裡用時2分43秒。比利亞科塔繼續說,當時比賽情況非常好:「場地很好,賽道也很不錯……風向雖說不是順風,但在逆風區得到了保護。」但是即便如此,選手也必須以很高的速度跑步,這意味著後面更大的體力消耗。
  • 從零開始的Android新項目11 - 組件化實踐(1)
    而且代碼一多吧,編譯也會很慢(我們公司現在的工程已經需要 5 - 6 分鐘了,手空使用 ant 都需要 5 分鐘,而 手Q 使用 ant 則需要 10 分鐘,改成 gradle 的話姑且乘個2,都是幾十分鐘的級別)。插件化可以加快編譯速度,從而提高開發效率。
  • Android模擬器之Genymotion
    之前已經介紹了android原生自帶的模擬器,它的運行速度確實很慢,不管是對開發還是測試同學來說,速度慢肯定會影響工作的效率,今天介紹
  • 是時候讓 Android Tools 屬性拯救你了
    Android studio 支持很多在 XML 文件中以 tools 為命名空間的屬性,當構建 App 時這些屬性會被擦除,對 APK 的大小和運行時行為沒有任何影響,這也就是我們文章最初想要的結果。
  • Android 沉浸式狀態欄攻略 讓你的狀態欄變色吧
    一、概述近期注意到QQ新版使用了沉浸式狀態欄,ok,先聲明一下:本篇博客效果下圖:
  • 今日頭條APK瘦身之路
    Android Studio在2.2版本添加 APK Analyzer功能,可以直接打開apk文件,如下圖所示 :APK文件主要有如下幾部分組成:res:主要是存放圖片資源lib:主要是存放so庫,各個cpu架構classes.dex:是java源碼編譯後生成的java字節碼文件,因方法數限制拆分了多個dexassets
  • Android測試 常用adb 命令總結
    ,請區別於 apk 文件名'-k' means keep the data and cache directories , -k 選項,卸載時保存數據和緩存目錄adb pull , 將 Android 設備上的文件或者文件夾複製到本地例如複製 Sdcard 下的 pull.txt 文件到 D 盤:adb pull sdcard/pull.txt
  • Android 7.0下載Apk後自動安裝
    本篇來自 cfy137000 的投稿,分享了如何在7.0下下載並安裝apk,希望對大家有所幫助。cfy137000 的博客地址:http://blog.csdn.net/cfy1370001.開始下載在使用 DownloadManager 進行下載的時候,就會用到 DownloadManager.Request
  • 把你的程序放到桌面——Android桌面部件Widget
    最後在 AndroidManifest.xml 裡,將 AppWidgetProvider類 和 xml屬性 註冊到一塊。通常我們會加一個 Service 來控制 Widget 的更新時間,後面再講為什麼。做完這些,如果不出錯,就完成了桌面部件。
  • Android 高新技術之SVG矢量動畫機制
    L = lineto(L X,Y):畫直線到指定的位置。H = horizontal lineto( H X):畫水平線到指定的X坐標位置。V = vertical lineto(V Y ):畫垂直線到指定的Y坐標。C = curveto(C ,X1,Y1,X2,Y2,ENDX,ENDY):三次貝塞爾曲線。
  • Android 7.0+使用VirtualXposed+Charles進行抓包
    這裡我選擇的辦法是用VirtualXposed工具進行繞過,如果你不想使用這個方法,也可以考慮通過將手機進行Root處理,並將Charles的證書添加到系統級的證書中,不過這並不在本文討論的範疇。手機下載好VirtualXposed和TrustMeAlready兩個apk文件,安裝VirtualXposed.apk之後運行。