嘿!让我们来手动编译安卓项目吧

来源:互联网 发布:mysql设置初始密码 编辑:程序博客网 时间:2024/06/10 11:03

本文有点像是在呼应我的文章放弃现代的ide, 拥抱命令行 。不过这次我是认真的。
对那些所谓魔法般的功能,我已经怕了。

神秘的后台进程执行着我不知道也不晓得原理的任务。IDE就像知道我头脑里的想法似的悄悄生成代码。“这里,试试这个东西”,它们对我说。然后我真的会去试。得了吧,其实我也挺喜欢这点的。但是当所有这些神秘的功能都在一起的时候,我又觉得挺难消化的。

其中一个这样的功能就是安卓的编译过程。即使在IDE之外, Gradle把Java和XML变成APK这个功能也足以让我感到不舒服了。
一旦有可能,我更希望剥离这些魔法而自己手动做这些事情。即使只是作为一种练习。

本文就是这样的练习。

目的
手动编译和部署安卓app,只使用SDK命令行工具,

规则
不能用IDE
不能用Gradle
And for extra credit:

Let’s use the Jack toolchain
Let’s build for Android N

The project
对于这个任务,我创建了一个只有一个activity的应用。运行结果如下:
这里写图片描述

并不是一个最佳典范,但是足够演绎编译的 主要过程了:

一个 activity
一个app图标的 drawable 资源
一个包含一个InageView的 layout 资源
一个外部依赖 - Picasso

为了便于演示,我采用了一个简单的,平铺的工程目录结构:

handbuilt-android-project/├── AndroidManifest.xml├── gen/├── lib/│   └── picasso-2.5.2.jar├── out/├── res/│   ├── drawable-xhdpi/│   │   └── icon.png│   └── layout/│       └── activity_main.xml└── src/    └── pl/        └── czak/            └── handbuilt/                └── MainActivity.java

当完成之后:

gen/ 将包含生成的R.java class。
out/ 将包含编译生成的产品。
完整的源码在(目录结构稍微多点,以及简单的依赖管理)GitHub上。

我们需要事先做一些设置。

Prerequisites

Java JDK
Android SDK tools

下载最新的SDK工具包(写本文的时候是24.4.1),解压并把跟目录设置到 $ANDROID_HOME。然后使用下面的,命令启动SDK Manager:

$ $ANDROID_HOME/tools/android

我们需要下载一些额外的组建才能完全使用SDK:

Tools
Platform tools
Build tools
Android N Preview SDK
Android N System Image (如果你想在模拟器上测试)
我是走在前沿的人;) ,所以全是用的预览版:
这里写图片描述

一旦所有东西都安装完毕,确保$PATH中存在以下命令:

$JAVA_HOME/bin/java
$JAVA_HOME/bin/jarsigner
$ANDROID_HOME/platform-tools/adb
$ANDROID_HOME/tools/build-tools/24.0.0-preview/aapt
$ANDROID_HOME/tools/build-tools/24.0.0-preview/zipalign

Jack compiler放在编译工具的$ANDROID_HOME/build-tools/24.0.0-preview/jack.jar中。为了方便,我使用下面的别名来触发它:

$ alias jack='java -jar "$ANDROID_HOME/build-tools/24.0.0-preview/jack.jar"'

编译
完整的编译包含7个不同的步骤,下面分小节描述:

1,Generate R.java
2,Compile
3,Package
4,Sign
5,Zipalign
6,Upload
7,Run

步骤 1. 生成 R.java

在Android Studio中,这个步骤在后台不断的发生着,也许是整个过程最神奇的部分。你添加一个XML布局,然后大概1秒过后你就能在代码中使用R.layout.activity_main来引用它了。这是因为每当资源文件改变的时候,IDE就会生成R.java源码文件。

在我们的练习中,我们需要手动做这件事情,这是aapt的第一个工作:

$ aapt package -f                -M AndroidManifest.xml                -I "$ANDROID_HOME/platforms/android-N/android.jar"                -S res/                -J gen/                -m

这个命令使用了AndroidManifest.xml以及res/并生成了下面的源文件:

gen/pl/czak/handbuilt/R.java

/* AUTO-GENERATED FILE.  DO NOT MODIFY. * * This class was automatically generated by the * aapt tool from the resource data it found.  It * should not be modified by hand. */package pl.czak.handbuilt;public final class R {    public static final class attr {    }    public static final class drawable {        public static final int icon=0x7f020000;    }    public static final class id {        public static final int image=0x7f040000;    }    public static final class layout {        public static final int activity_main=0x7f030000;    }}

注:

解释几个关键参数。

-f 如果编译出来的文件已经存在,强制覆盖。

-m 使生成的包的目录放在-J参数指定的目录。

-J 指定生成的R.java的输出目录

-S res文件夹路径

-A assert文件夹的路径

-M AndroidManifest.xml的路径

-I 某个版本平台的android.jar的路径

-F 具体指定apk文件的输出

步骤 2. 编译
现在完整的代码包含了MainActivity.java和最新生成的R.java.可以使用Jack来编译了:

$ jack --classpath "$ANDROID_HOME/platforms/android-N/android.jar"        --import lib/picasso-2.5.2.jar        --output-dex out/        src/ gen/

我们传递了N的android.jar到classpath并在import中包含了Picasso的JAR。我们还传入了两个包含了java文件的文件夹(src/ 和 gen/)。
最终,我们得到了被用来打包的out/classes.dex。

步骤 3. Package
现在我们可以开始编译APK了。我把打包过程分成两步。首先,我们用manifest和资源文件创建初始包(initial APK package):

$ aapt package -f                -M AndroidManifest.xml                -I "$ANDROID_HOME/platforms/android-N/android.jar"                -S res/                -F out/handbuilt.apk

我们又一次使用了Android Asset Packaging Tool (aapt),不过这次我使用的是-F,它告诉aapt去创建一个apk,而上次我们使用的是-J。

现在,我们加入已经编译好的classes.dex。注意aapt是对相对路径敏感的,classes.dex需要在package的根目录,因此,这一步我们在out/目录执行:

$ cd out/$ aapt add handbuilt.apk classes.dex

步骤 4. 签名
每一个APK在安装到设备或者模拟器之前都需要被签名。不管你只是在开发中尝试debug版本还是准备公开发布最终版,签名都是必须的。而使用IDE的时候这个过程是不可见的,除非你想把你的app发布到Google Play,不然不会出现任何关于key的提示。

为了方便开发,SDK提供了一个标准的debug key,在~/.android/debug.keystore中。这个key可以这样使用:

Keystore password: android
Key password: android
Key alias: androiddebugkey
知道这点之后,我们使用JDK的jarsigner来执行这个任务:

$ jarsigner -verbose             -keystore ~/.android/debug.keystore             -storepass android             -keypass android             out/handbuilt.apk             androiddebugkey

APK可以上传了,但是让我们先做一个重要的优化。

步骤 5. Zipalign
Zipalign对apk文件中未压缩的数据在4个字节边界上对齐,当资源文件通过内存映射对齐到4字节边界时,访问资源文件的代码才是有效率的。4字节对齐后,android系统就可以通过调用mmap函数读取文件,进程可以像读写内存一样对普通文件的操作,系统共享内存IPC,以在读取资源上获得较高的性能。 如果资源本身没有进行对齐处理,它就必须显式地读取它们——这个过程将会比较缓慢且会花费额外的内存。 这是一个很重要的优化。幸运的是,它非常简单:

$ zipalign -f 4 out/handbuilt.apk out/handbuilt-aligned.apk

我们的最终APK是handbuilt-aligned.apk。

注:Zipalign的参考文章:改善android性能工具篇【zipalign】。

步骤 6. 上传
现在启动一个模拟器(出于练习,这一步也是用的命令行):

$ adb install -r out/handbuilt-aligned.apk

可以启动app了,当然,我们可以点击drawer上的图标,但是那样就没有乐趣了。

步骤 7. Run
启动app:

$ adb shell am start pl.czak.handbuilt/.MainActivity

完工。

Notes
Jack工具大大简化了编译过程。以前需要多个步骤(javac,dx, ProGuard),现在都被这一个工具处理好了。
Android Studio在底层会比我们多生成一个calss-BuildConfig.java。出于练习,我把它省略了。
Gradle编译系统使用了不同命名方式-zipaligning之前的package叫做:handbuilt-unaligned.apk,而最终的APK叫handbuilt.apk。这只是一种惯例,我这里打破了它。你咬我啊!
A typical build process would also incorporate Jill – Jack’s helper tool for converting libraries into Jack’s expected jayce format. Again, I’ve omitted it in the examples here - as of version 1.2-rc2 Jack seems to accept android.jar and picasso-2.5.2.jar just fine. To speed up consecutive builds, you would want to “pre-jill” both of these just like you would “pre-dex” libraries with the old toolchain.
现在可以使用某些Java 8的特性。如果你想使用,需要在Jack指定-D jack.java.source.version=8。
总结
我又一次觉得安卓编译过程的背后并不那么糟糕。我会因此而用make取代gradle吗?不太可能!我甚至开始怀恋Android Studio了。

原文:Jack, Jill & building Android apps by hand

另附其它参考文章:

https://spin.atomicobject.com/2011/08/22/building-android-application-bundles-apks-by-hand/
https://www.douban.com/note/205203144/

0 0
原创粉丝点击