手工构建Android应用

本文中所使用的工具信息:
系统为Mac 10.12版本。
Android build-tools版本:22.0.1
Android platforms版本:22
JDK版本:1.7
测试的手机系统:Android 5.1

请尊重博主劳动成果,转载请标明原文链接。

Android应用构建流程

以前使用Eclipse,现在使用Android Studio开发,点击“run”,等待一会儿,便能输出Apk文件。应用构建的基本流程,自己大概明白,但是没有使用过命令行来构建Apk。现在使用命令构建,并记录相关流程。

先看看Android开发文档上的两张图片。

(一)Android工程构建简化图
build0

(二)Android打包流程图
build progress

Apk文件内部可以分为4个部分:
dex文件:有一个或多个,编译所产生的class文件都被转换打包到dex。
资源包resources.arsc:以表的形式记录应用的资源文件信息。
未编译的文件:签名信息相关的文件,以及其它写入信息。
AndroidManifest.xml文件:记录版本信息,应用所需要的权限,四大组件等基本信息。

构建Apk的步骤:
1,通过appt生产R.java和资源包resources.arsc。如果有aidl文件则通过aidl工具生成对应的java文件;
2,通过jdk编译工程中的java文件;
3,将工程编译的class和引入的jar文件,通过dx工具转换生成dex文件;
4,使用ApkBuilder工具将dex文件和资源文件合并为apk文件;
5,使用jarsigner命令和keystore对apk进行签名;
6,使用zipalign命令优化签名后的apk文件。如果为debug包,则不需要进行这一步。

手动创建一个Android工程,使用命令构建一个可安装的apk。

一 创建Android工程

手工创建一个简单的PluginDemo工程,目录和文件如下:

PluginDemo
|____AndroidManifest.xml
|____build
| |____out
|____gen
|____res
| |____drawable
| | |____p_icon_play.png
| |____layout
| | |____plugin_item.xml
| |____mipmap
| | |____p_icon_lock.png
| |____values
| | |____string.xml
| | |____styles.xml
|____src
| |____com
| | |____plugin
| | | |____test
| | | | |____PluginActivity.java

二 生产R.java和resoures.ap_

生成R.java和资源文件所使用的aapt基本命令:
-f 如果编译生成的文件存在,则强制覆盖
-m 让生成的文件存放到-J指定的目录
-M 指定AndroidManifest.xml文件的路径
-J 指定R.java文件存放的目录
-A 指定asset source目录
-S 指定res资源目录
-F 指定生成的资源包文件路径
-I 指定编译使用的版本平台的android.jar路径

更多相关命令,可以在控制台执行appt,查看打印出来的Usag信息。

开启终端(Window系统则是控制台),进入到PluginDemo工程目录。使用aapt命令生成R.java文件和生成资源包文件。

../../../devTools/android/android-sdk-macosx/build-tools/22.0.1/aapt package -f -m -J gen -S res -M AndroidManifest.xml -I ../../../devTools/android/android-sdk-macosx/platforms/android-22/android.jar -F build/out/resources.ap_

三 编译java文件

使用javac编译java文件

javac -encoding GBK -bootclasspath ../../../devTools/android/android-sdk-macosx/platforms/android-22/android.jar -d ./build/out ./gen/com/plugin/test/R.java

执行javac -help,查看相关命令:

用法: javac <options> <source files>
其中, 可能的选项包括:
  -g                         生成所有调试信息
  -g:none                    不生成任何调试信息
  -g:{lines,vars,source}     只生成某些调试信息
  -nowarn                    不生成任何警告
  -verbose                   输出有关编译器正在执行的操作的消息
  -deprecation               输出使用已过时的 API 的源位置
  -classpath <路径>            指定查找用户类文件和注释处理程序的位置
  -cp <路径>                   指定查找用户类文件和注释处理程序的位置
  -sourcepath <路径>           指定查找输入源文件的位置
  -bootclasspath <路径>        覆盖引导类文件的位置
  -extdirs <目录>              覆盖所安装扩展的位置
  -endorseddirs <目录>         覆盖签名的标准路径的位置
  -proc:{none,only}          控制是否执行注释处理和/或编译。
  -processor <class1>[,<class2>,<class3>...] 要运行的注释处理程序的名称; 绕过默认的搜索进程
  -processorpath <路径>        指定查找注释处理程序的位置
  -d <目录>                    指定放置生成的类文件的位置
  -s <目录>                    指定放置生成的源文件的位置
  -implicit:{none,class}     指定是否为隐式引用文件生成类文件
  -encoding <编码>             指定源文件使用的字符编码
  -source <发行版>              提供与指定发行版的源兼容性
  -target <发行版>              生成特定 VM 版本的类文件
  -version                   版本信息
  -help                      输出标准选项的提要
  -A关键字[=值]                  传递给注释处理程序的选项
  -X                         输出非标准选项的提要
  -J<标记>                     直接将 <标记> 传递给运行时系统
  -Werror                    出现警告时终止编译
  @<文件名>                     从文件读取选项和文件名

四 将class文件转换为dex文件

将编译生成的class文件转换成dex文件

../../../devTools/android/android-sdk-macosx/build-tools/22.0.1/dx --dex --output=build/out/classes.dex build/out/

由于系统默认的JDK版本为1.8,所以在使用22.0.1的dx工具生成dex文件时报错了。

UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)
    at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)
    at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)
    at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)
    at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)
    at com.android.dx.command.dexer.Main.processClass(Main.java:704)
    at com.android.dx.command.dexer.Main.processFileBytes(Main.java:673)
    at com.android.dx.command.dexer.Main.access$300(Main.java:83)
    at com.android.dx.command.dexer.Main$1.processFileBytes(Main.java:602)
    at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:170)
    at com.android.dx.cf.direct.ClassPathOpener.processDirectory(ClassPathOpener.java:229)
    at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:158)
    at com.android.dx.cf.direct.ClassPathOpener.processDirectory(ClassPathOpener.java:229)
    at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:158)
    at com.android.dx.cf.direct.ClassPathOpener.processDirectory(ClassPathOpener.java:229)
    at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:158)
    at com.android.dx.cf.direct.ClassPathOpener.processDirectory(ClassPathOpener.java:229)
    at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:158)
    at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
    at com.android.dx.command.dexer.Main.processOne(Main.java:632)
    at com.android.dx.command.dexer.Main.processAllFiles(Main.java:510)
    at com.android.dx.command.dexer.Main.runMonoDex(Main.java:280)
    at com.android.dx.command.dexer.Main.run(Main.java:246)
    at com.android.dx.command.dexer.Main.main(Main.java:215)
    at com.android.dx.command.Main.main(Main.java:106)
...while parsing com/plugin/test/PluginActivity.class

这是因为22.0.1版本的dx工具无法识别JDK 1.8编译的class文件中的一些内容。将JDK版本切换到1.7版本,重新编译。然后执行dx打包命令,可以正常生产dex文件。

dx基本命令:
–dex 指定生成dex文件
–output=<file> file为生成的dex文件的路径
[<file>.class | <file>.{zip,jar,apk} | <directory>] 指定class或jar文件路径

更多dx命令,可以使用dx -help查看。

五 合并dex和资源文件为apk

使用ApkBuilder将资源包和dex文件合并为apk文件

java -cp ../../../devTools/android/android-sdk-macosx/tools/lib/sdklib.jar com.android.sdklib.build.ApkBuilderMain build/out/test.apk -u -v -z build/out/resources.ap_ -f build/out/classes.dex

-cp为java命令,用来指定目录和 zip/jar 文件的类搜索路径。后面的-u -v -z -f为ApkBuilder的命令。

ApkBuilder基本命令:

A command line tool to package an Android application from various sources.
Usage: apkbuilder <out archive> [-v][-u][-storetype STORE_TYPE] [-z inputzip]
            [-f inputfile] [-rf input-folder] [-rj -input-path]

    -v      Verbose.
    -d      Debug Mode: Includes debug files in the APK file.
    -u      Creates an unsigned package.
    -storetype Forces the KeyStore type. If omitted the default is used.

    -z      Followed by the path to a zip archive.
            Adds the content of the application package.

    -f      Followed by the path to a file.
            Adds the file to the application package.

    -rf     Followed by the path to a source folder.
            Adds the java resources found in that folder to the application
            package, while keeping their path relative to the source folder.

    -rj     Followed by the path to a jar file or a folder containing
            jar files.
            Adds the java resources found in the jar file(s) to the application
            package.

    -nf     Followed by the root folder containing native libraries to
            include in the application package.

-rf用来绑定源码,-rj将指定的jar文件中的资源文件添加到应用包,-nf将so文件添加到应用包中。

六 对apk签名

如果没有keystore,或者想生成一个,可以使用keytool命令生成keystore文件。
生成一个别名为test,有效期为20000天,密码为123456,文件名称为test.keystore的签名文件。命令和步骤如下:

xxx:PluginDemo liu1359041$ keytool -genkey -alias test -validity 20000 -keystore test.keystore
输入密钥库口令:  
再次输入新口令: 
您的名字与姓氏是什么?
  [Unknown]:  test
您的组织单位名称是什么?
  [Unknown]:  test
您的组织名称是什么?
  [Unknown]:  test
您所在的城市或区域名称是什么?
  [Unknown]:  test
您所在的省/市/自治区名称是什么?
  [Unknown]:  test
该单位的双字母国家/地区代码是什么?
  [Unknown]:  cn
CN=test, OU=test, O=test, L=test, ST=test, C=cn是否正确?
  [否]:  y

输入 <test> 的密钥口令
    (如果和密钥库口令相同, 按回车):  
再次输入新口令: 

执行完命令,便会在PluginDemo目录里生成一个test.keystore的签名文件。

使用test.keystore对apk进行签名

jarsigner -verbose -keystore test.keystore -storepass 123456 -keypass 123456 -signedjar build/out/test_s.apk build/out/test.apk test

输出的日志信息:

   正在添加: META-INF/MANIFEST.MF
   正在添加: META-INF/TEST.SF
   正在添加: META-INF/TEST.DSA
  正在签名: AndroidManifest.xml
  正在签名: res/drawable/p_icon_play.png
  正在签名: res/layout/plugin_item.xml
  正在签名: res/mipmap/p_icon_lock.png
  正在签名: resources.arsc
  正在签名: classes.dex
jar 已签名。

警告: 
未提供 -tsa 或 -tsacert, 此 jar 没有时间戳。如果没有时间戳, 则在签名者证书的到期日期 (2072-05-07) 或以后的任何撤销日期之后, 用户可能无法验证此 jar

对apk签名成功。

SHA-256算法在Android 4.2及以版本报错问题。原因是JDK 1.6以前默认采用SHA1算法,而1.6及以后版本则采用了更安全的SHA-256算法,所以在JDK 1.6+环境使用jarsigner签名,默认使用了SHA-256算法。因为未做兼容,在Android 4.2及以下的系统会出现签名验证失败问题。可以在jarsigner命令后追加-sigalg SHA1withRSA -digestalg SHA1命令,强制使用SHA1算法。

这个问题在使用Android 4.0的模拟器测试动态加载签名的jar文件时出现过。

七 优化apk

使用zipalign对apk进行优化,减少包的体积

../../../devTools/android/android-sdk-macosx/build-tools/22.0.1/zipalign -f 4 build/out/test.apk build/out/test_opt.apk

zipalign用法和命令:

Usage: zipalign [-f] [-v] [-z] <align> infile.zip outfile.zip
       zipalign -c [-v] <align> infile.zip

  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment
  -c: check alignment only (does not modify file)
  -f: overwrite existing outfile.zip
  -v: verbose output
  -z: recompress using Zopfli

执行完以上命令,再查看PluginDemo工程目录和文件如下:

PluginDemo
|____AndroidManifest.xml
|____build
| |____out
| | |____classes.dex
| | |____com
| | | |____plugin
| | | | |____test
| | | | | |____PluginActivity.class
| | | | | |____R$attr.class
| | | | | |____R$drawable.class
| | | | | |____R$id.class
| | | | | |____R$layout.class
| | | | | |____R$mipmap.class
| | | | | |____R$string.class
| | | | | |____R$style.class
| | | | | |____R.class
| | |____resources.ap_
| | |____test.apk
| | |____test_opt.apk
| | |____test_s.apk
|____gen
| |____com
| | |____plugin
| | | |____test
| | | | |____R.java
|____res
| |____drawable
| | |____p_icon_play.png
| |____layout
| | |____plugin_item.xml
| |____mipmap
| | |____p_icon_lock.png
| |____values
| | |____string.xml
| | |____styles.xml
|____src
| |____com
| | |____plugin
| | | |____test
| | | | |____PluginActivity.java
|____test.keystore

构建过程中生成了不少的文件。最终能够运行的是test_opt.apk(经过优化的apk)和test_s.apk这两个文件。

安装运行test_opt.apk,如下图:
test
手工创建的工程和命令构建的Apk正常运行。虽然系统是5.1,由于没有配置Activity的主题和继承framework中的Actvity,而不是v4或v7包的,所以显示的像非常老旧2.3系统上的应用。

参考文章

Android官方开发文档

手动命令行编译 APK
https://juejin.im/entry/57cb841e2e958a0068dcf679

Android 打包过程
http://www.jianshu.com/p/7c288a17cda8

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值