用Android SDK Build Tools手動構建APK
我們平時都是用Android Studio進行Android應用的開發,Android Studio構建APK是通過呼叫Gradle指令碼實現的,而Gradle指令碼最終是通過呼叫Android SDK Build Tools裡的各種命令列工具實現的。
下面嘗試直接用Build Tools構建一個極簡的Hello World APK,瞭解一下這個過程和各個工具的基本用法。
整個構建過程大致分為以下幾步:
- 用aapt2編譯資原始檔,生成中間二進位制檔案
- 用aapt2連結合併中間檔案,生成不包含程式碼的APK,並生成R.java
- 用javac編譯java原始檔,得到.class java位元組碼檔案
- 用d8將.class編譯成DEX位元組碼檔案
- 將DEX檔案匯入APK中
- 對APK進行簽名
建立專案原始檔
專案的目錄結構及檔案原始碼如下:
D:\helloworld>tree /F │ AndroidManifest.xml │ ├─compiled │ ├─java │└─com │└─cdjtest │└─helloworld │MainActivity.java │ └─res ├─drawable │ic_launcher.png │ ├─layout │activity_main.xml │ └─values strings.xml 複製程式碼
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cdjtest.helloworld"> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 複製程式碼
MainActivity.java
package com.cdjtest.helloworld; import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } 複製程式碼
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="Hello World!"/> 複製程式碼
strings.xml
<resources> <string name="app_name">helloworld</string> </resources> 複製程式碼
用aapt2編譯資原始檔
先設定一下環境變數,將Build Tools 28.0.3的路徑加到PATH中,方便呼叫
D:\helloworld>set PATH=%PATH%;$ANDROID_HOME%\build-tools\28.0.3\ 複製程式碼
編譯res目錄下的3個資原始檔,生成.flat中間二進位制檔案
D:\helloworld>aapt2 compile res\values\strings.xml -o compiled\ D:\helloworld>aapt2 compile res\layout\activity_main.xml -o compiled\ D:\helloworld>aapt2 compile res\drawable\ic_launcher.png -o compiled\ 複製程式碼
連結.flat檔案,生成helloworld.unsigned.apk(還未包含DEX位元組碼),--java java
引數指定在java目錄生成R.java檔案,和MainActivity.java在同一目錄
D:\helloworld>aapt2 link -o helloworld.unsigned.apk ^ -I %ANDROID_HOME%\platforms\android-28\android.jar ^ compiled\values_strings.arsc.flat ^ compiled\layout_activity_main.xml.flat ^ compiled\drawable_ic_launcher.png.flat ^ --manifest AndroidManifest.xml --java java\ 複製程式碼
用javac和d8編譯原始碼
用javac將MainActivity.java和R.java編譯成.class檔案
D:\helloworld>javac java\com\cdjtest\helloworld\*.java -classpath %ANDROID_HOME%\platforms\android-28\android.jar 複製程式碼
用d8將.class編譯成classes.dex
,(d8和dx的對比可參考Jake大神的這篇文章)
D:\helloworld>d8 --lib %ANDROID_HOME%\platforms\android-28\android.jar --release --output . java\com\cdjtest\helloworld\*.class 複製程式碼
將classes.dex
匯入APK中
D:\helloworld>aapt add helloworld.unsigned.apk classes.dex 複製程式碼
APK簽名
用zipalign優化APK,主要作用是記憶體對齊,提高執行時讀取資源的效率
D:\helloworld>zipalign -p 4 helloworld.unsigned.apk helloworld.unsigned.aligned.apk 複製程式碼
用JDK自帶的keytool工具生成keystore檔案my-release-key.jks
D:\helloworld>keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias 複製程式碼
用apksigner和my-release-key.jks
簽名APK,生成helloworld-release.apk
D:\helloworld>apksigner sign --ks my-release-key.jks --out helloworld-release.apk helloworld.unsigned.aligned.apk Keystore password for signer #1: 複製程式碼
最終的目錄結構:
D:\helloworld>tree /F │AndroidManifest.xml │classes.dex │helloworld-release.apk │helloworld.unsigned.aligned.apk │helloworld.unsigned.apk │my-release-key.jks │ ├─compiled │drawable_ic_launcher.png.flat │layout_activity_main.xml.flat │values_strings.arsc.flat │ ├─java │└─com │└─cdjtest │└─helloworld │MainActivity.class │MainActivity.java │R$drawable.class │R$layout.class │R$string.class │R.class │R.java │ └─res ├─drawable │ic_launcher.png │ ├─layout │activity_main.xml │ └─values strings.xml 複製程式碼
安裝APK
D:\helloworld>adb install helloworld-release.apk Success 複製程式碼
成功執行螢幕中間可見"Hello World!"。