Android APK的诞生记

261 阅读1分钟

在成为一个Android Developer后,其实一直都很好奇,一个.apk文件是怎么产生的,它是怎么从一堆.java、.xml等文件变成一个.apk的呢,随着知识体系的完善,这个问题貌似越来越清楚,但是一直未曾认认真真的梳理过一遍流程,恰好最近碰到相关问题,就借此机会总结一下,题其衣裳,以记其事~

环境准备

  • Mac OS
  • Java
  • Android SDK

准备项目

创建工程

直接使用Android Studio创建一个新的工程,比如:HelloAPK,这样创建出来的工程应该包含如下内容:

[min@TATEMIN-MB0:] HelloAPK $ ls -al
total 72
drwxr-xr-x  14 min  staff   448  5 21 10:04 .
drwxr-xr-x  33 min  staff  1056  7 10 10:22 ..
-rw-r--r--   1 min  staff   208  5 21 10:04 .gitignore
drwxr-xr-x   5 min  staff   160  5 21 10:04 .gradle
drwxr-xr-x  10 min  staff   320  5 21 13:53 .idea
-rw-r--r--   1 min  staff   862  5 21 10:04 HelloAPK.iml
drwxr-xr-x   9 min  staff   288  5 21 10:06 app
-rw-r--r--   1 min  staff   564  5 21 10:04 build.gradle
drwxr-xr-x   3 min  staff    96  5 21 10:04 gradle
-rw-r--r--   1 min  staff  1073  5 21 10:04 gradle.properties
-rwxr--r--   1 min  staff  5296  5 21 10:04 gradlew
-rw-r--r--   1 min  staff  2260  5 21 10:04 gradlew.bat
-rw-r--r--   1 min  staff   432  5 21 10:04 local.properties
-rw-r--r--   1 min  staff    43  5 21 10:04 settings.gradle

但是实际上决定APK的生成主要依赖于目录app/src/main,所以此目录就是我们编译APK的workspace。

修改工程

因为此次编译仅是向大家解释Android APK的生成过程,为了尽可能的方便,我们需要删除工程中的一些androidx的非Frameword的依赖,具体细节如下:

资源文件

  • 删除res/values/styles.xml

  • 修改res/layout/activity_main.xml文件:

    image-20200714192419028.png

代码文件

  • 修改java/com/min/helloapk/MainActivity.java文件
    image-20200714192652646.png

Manifest

  • 修改AndroidManifest.xml文件
    image-20200714192800708.png

编译项目

  • 生成签名

    [min@TATEMIN-MB0:] main $ keytool -genkey -alias android.keystore -keyalg RSA -validity 1000 -keystore android.keystore
    
  • 查看签名

    [min@TATEMIN-MB0:] main $ keytool -list -v -keystore android.keystore
    
  • 生成R.java

    [min@TATEMIN-MB0:] main $ aapt package -v -f -m -S res/ -J java/ -M AndroidManifest.xml -I /Users/min/Library/Android/sdk/platforms/android-26/android.jar
    
  • 编译代码

    [min@TATEMIN-MB0:] main $ mkdir gen # 创建一个中间文件目录
    [min@TATEMIN-MB0:] main $ javac java/com/min/helloapk/*.java -verbose -classpath $ANDROID_HOME/platforms/android-26/android.jar -d gen
    

    此时会在bin目录下生成如下文件:

    [min@TATEMIN-MB0:] main $ tree gen/
    gen/
    └── com
        └── min
            └── helloapk
                ├── MainActivity.class
                ├── R$attr.class
                ├── R$color.class
                ├── R$drawable.class
                ├── R$layout.class
                ├── R$mipmap.class
                ├── R$string.class
                └── R.class
    
  • 创建Dex文件

    [min@TATEMIN-MB0:] main $ mkdir bin
    [min@TATEMIN-MB0:] main $ dx --dex --verbose --output=bin/classes.dex gen
    

    此时会在bin目录下生成如下文件:

    [min@TATEMIN-MB0:] main $ tree bin/
    bin/
    └── classes.dex
    
  • 创建APK文件

    [min@TATEMIN-MB0:] main $ aapt package -v -f -M AndroidManifest.xml -S res -I $ANDROID_HOME/platforms/android-26/android.jar -F bin/HelloAPK.unsigned.apk bin
    

    此时会在bin目录下文件列表如下:

    [min@TATEMIN-MB0:] main $ tree bin/
    bin/
    ├── HelloAPK.unsigned.apk
    └── classes.dex
    
  • APK签名

    [min@TATEMIN-MB0:] main $ jarsigner -verbose -keystore android.keystore -signedjar bin/HelloAPK.signed.apk bin/HelloAPK.unsigned.apk android.keystore
    
  • zipalign APK

    zipalign -v -f 4 bin/HelloAPK.signed.apk bin/HelloAPK.apk
    
  • 安装APK

    [min@TATEMIN-MB0:] main $ adb install bin/HelloAPK.apk
    
  • 运行APK

    image-20200714210745961.png

附录

如下是整个编译工程的Workspace:

min@TATEMIN-MB0:] main $ tree
.
├── AndroidManifest.xml
├── android.keystore
├── bin
│   ├── HelloAPK.apk
│   ├── HelloAPK.signed.apk
│   ├── HelloAPK.unsigned.apk
│   └── classes.dex
├── gen
│   └── com
│       └── min
│           └── helloapk
│               ├── MainActivity.class
│               ├── R$attr.class
│               ├── R$color.class
│               ├── R$drawable.class
│               ├── R$layout.class
│               ├── R$mipmap.class
│               ├── R$string.class
│               └── R.class
├── java
│   └── com
│       └── min
│           └── helloapk
│               ├── MainActivity.java
│               └── R.java
└── res
    ├── drawable
    │   └── ic_launcher_background.xml
    ├── drawable-v24
    │   └── ic_launcher_foreground.xml
    ├── layout
    │   └── activity_main.xml
    ├── mipmap-anydpi-v26
    │   ├── ic_launcher.xml
    │   └── ic_launcher_round.xml
    ├── mipmap-hdpi
    │   ├── ic_launcher.png
    │   └── ic_launcher_round.png
    ├── mipmap-mdpi
    │   ├── ic_launcher.png
    │   └── ic_launcher_round.png
    ├── mipmap-xhdpi
    │   ├── ic_launcher.png
    │   └── ic_launcher_round.png
    ├── mipmap-xxhdpi
    │   ├── ic_launcher.png
    │   └── ic_launcher_round.png
    ├── mipmap-xxxhdpi
    │   ├── ic_launcher.png
    │   └── ic_launcher_round.png
    └── values
        ├── colors.xml
        └── strings.xml

20 directories, 33 files