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

转载 2016年06月06日 11:45:10

本文有点像是在呼应我的文章放弃现代的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/

相关文章推荐

Ant自动编译打包android项目

背景:     Eclipse用起来虽然方便,但是编译打包android项目还是比较慢,尤其当要讲应用打包发布到各个渠道时,用Eclipse手动打包各种渠道包就有点不且实际了,这时候我们用到Ant帮我...

Android命令行手动编译打包详解

Android 命令行手动编译打包过程图 【详细步骤】: Step1:使用aapt生成R.java类文件。 例: F:/explorer/android-sdk-wind...

AS-->如何用Gradle命令行,构建Android工程

准备工作: 请自行下载Gradle工具,本文使用的是gradle-2.10-all版本,你可以在后序提供的群中,下载此文件;在构建之前,需要约定项目的结构,并且需要一些必备的Gradle脚本文件1:...
  • angcyo
  • angcyo
  • 2016-01-15 23:39
  • 3174

手动编译Android源码

在Android Studio代码调试一文中,简单的介绍了代码调试的一些技巧.现在我们来谈谈android源码编译的一些事.(俺认为,作为android developer人人都应该有一份自己Andr...

在命令行使用已存在的keystore对apk包进行签名

在命令行使用已存在的keystore对apk包进行签名

apk文件签名之命令行法

写了一个法语词典,想传到Google play上,发现需要apk签名,犹豫是初次尝试,故查阅了相关资料,结合自己的实践经历,我这里讲如何操作,至于为什么这么做,网上一搜一大片,我就不赘述了: ...

带参数的批处理

批处理文件中还可以像C语言一样使用参数,这只需用到一个参数表示符%。 %表示参数,参数是指在运行批处理文件时在文件名后加的字符串。变量可以从 %0到%9,%0表示文件名本身,字符串用%1到%9顺序表示...

bat批处理(二):%0 %1——给批处理脚本传递参数

bat批处理(二):%0 %1——给批处理脚本传递参数 标签: bat传入参数形参0-9 2016-10-11 15:01 280人阅读 评论(0) 收藏 举报 本...

命令行传递给批处理的参数

命令行传递给批处理的参数 %0 批处理文件本身 %1 第一个参数 %9 第九个参数 %* 从第一个参数开始的所有参数 批参数(%n)的替代已被增强。您可以使用以下语法:       %~1   ...

如何制作带参数的批处理文件

可以用记事本编辑,然后将后缀改为.bat 后缀是bat的文件就是批处理文件,是一种文本文件。简单的说,它的作用就是自动的连续执行多条命令,批处理文件的内容就是一条一条的命令。那它有什么用呢? 比如,在...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)