嘿!让我们来手动编译安卓项目吧
2016-06-06 11:45
621 查看
本文有点像是在呼应我的文章放弃现代的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
为了便于演示,我采用了一个简单的,平铺的工程目录结构:
当完成之后:
gen/ 将包含生成的R.java class。
out/ 将包含编译生成的产品。
完整的源码在(目录结构稍微多点,以及简单的依赖管理)GitHub上。
我们需要事先做一些设置。
Prerequisites
Java JDK
Android SDK tools
下载最新的SDK工具包(写本文的时候是24.4.1),解压并把跟目录设置到 $ANDROID_HOME。然后使用下面的,命令启动SDK Manager:
我们需要下载一些额外的组建才能完全使用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中。为了方便,我使用下面的别名来触发它:
编译
完整的编译包含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的第一个工作:
这个命令使用了AndroidManifest.xml以及res/并生成了下面的源文件:
gen/pl/czak/handbuilt/R.java
注:
解释几个关键参数。
-f 如果编译出来的文件已经存在,强制覆盖。
-m 使生成的包的目录放在-J参数指定的目录。
-J 指定生成的R.java的输出目录
-S res文件夹路径
-A assert文件夹的路径
-M AndroidManifest.xml的路径
-I 某个版本平台的android.jar的路径
-F 具体指定apk文件的输出
步骤 2. 编译
现在完整的代码包含了MainActivity.java和最新生成的R.java.可以使用Jack来编译了:
我们传递了N的android.jar到classpath并在import中包含了Picasso的JAR。我们还传入了两个包含了java文件的文件夹(src/ 和 gen/)。
最终,我们得到了被用来打包的out/classes.dex。
步骤 3. Package
现在我们可以开始编译APK了。我把打包过程分成两步。首先,我们用manifest和资源文件创建初始包(initial APK package):
我们又一次使用了Android Asset Packaging Tool (aapt),不过这次我使用的是-F,它告诉aapt去创建一个apk,而上次我们使用的是-J。
现在,我们加入已经编译好的classes.dex。注意aapt是对相对路径敏感的,classes.dex需要在package的根目录,因此,这一步我们在out/目录执行:
步骤 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来执行这个任务:
APK可以上传了,但是让我们先做一个重要的优化。
步骤 5. Zipalign
Zipalign对apk文件中未压缩的数据在4个字节边界上对齐,当资源文件通过内存映射对齐到4字节边界时,访问资源文件的代码才是有效率的。4字节对齐后,android系统就可以通过调用mmap函数读取文件,进程可以像读写内存一样对普通文件的操作,系统共享内存IPC,以在读取资源上获得较高的性能。 如果资源本身没有进行对齐处理,它就必须显式地读取它们——这个过程将会比较缓慢且会花费额外的内存。 这是一个很重要的优化。幸运的是,它非常简单:
我们的最终APK是handbuilt-aligned.apk。
注:Zipalign的参考文章:改善android性能工具篇【zipalign】。
步骤 6. 上传
现在启动一个模拟器(出于练习,这一步也是用的命令行):
可以启动app了,当然,我们可以点击drawer上的图标,但是那样就没有乐趣了。
步骤 7. Run
启动app:
完工。
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/
对那些所谓魔法般的功能,我已经怕了。
神秘的后台进程执行着我不知道也不晓得原理的任务。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/
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories