AndroidBuildTech Android开发编译环境构建 Ant ecj etc

2 篇文章 0 订阅

如何检验apk是否已经被zipalign过

$ zipalign -cv 4 target.apk
....
Verfication successful (验证成功: 说明该apk经过 4 字节(32bit)对齐处理)
 

ecj + Android SDK + ANT 构建 

准备工作 

  1. eclipse的java编译器(ecj.jar)
  2. Android SDK
  3. ant

经过配置,我们假定环境如下:

# ecj的位置 export ECJ_HOME=/opt/lib/ecj.jar

# Android SDK的位置 export ANDROID_HOME=/opt/android-sdk-linux_86

使用ant构建Android工程的整个过程由以下几个ant脚本构成:

  • $ANDROID_HOME/tools/main_rules.xml
  • $ANDROID_HOME/tools/lib_rules.xml
  • $ANDROID_HOME/tools/test_rules.xml
  • $PROJECT_DIR/build.xml (可以由android create/update project 命令生成)

我们需要修改build.xml, 首先配置编译器:

  <property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/>  <!-- 使用ecj进行编译 -->
  <property name="java.target" value="1.6" />
  <property name="java.source" value="1.6" />

而后配置编译过程, 由*.java编译出*.class这个步骤是由compile这个target定义的, 因此我们需要修改其中的部分参数,包括

  • Android framework jar包的位置
  • 忽略Android对关键jar包的校验
 <target name ="compile" 
         depends ="-pre-build, -aidl, -resource-src, -pre-compile" 
         description="*.java ---> *.class">

    <if condition="${manifest.hasCode}">
      <then>
        <!-- If android rules are used for a test project, its classpath should include tested project's location -->
        <condition property="extensible.classpath" value="${tested.project.absolute.dir}/bin/classes" else=".">
          <isset property="tested.project.absolute.dir"/>
        </condition>

        <condition property="extensible.libs.classpath" value="${tested.project.absolute.dir}/libs" else="${jar.libs.dir}">
          <isset property="tested.project.absolute.dir"/>
        </condition>

        <javac encoding="${java.encoding}" 
               source="${java.source}" 
               target="${java.target}" 
               debug="true" 
               extdirs="" 
               destdir="${out.classes.absolute.dir}" 
               bootclasspath="${internal.classpath}"    <!-- framework jar 包的路径 -->
               bootclasspathref="android.target.classpath" 
               verbose="${verbose}" 
               classpath="${extensible.classpath}" 
               classpathref="jar.libs.ref">
          <src path="${source.absolute.dir}"/>
          <src path="${gen.absolute.dir}"/>
          <src refid="project.libraries.src"/>
          <!-- 下面这一句就是强制使用内部jar,忽略校验-->
          <compilerarg line="-warn:+forbidden"/>
          <classpath>
            <fileset dir="${extensible.libs.classpath}" includes="*.jar"/>
          </classpath>
        </javac>
      </then>
      <else>
        <echo>hasCode = false. Skipping...</echo>
      </else>
    </if>
  </target>

build.xml 

<?xml version="1.0" encoding="UTF-8"?>
<project name="PansiMsg" default="help">

  <!-- The local.properties file is created and updated by the 'android' tool. It contains the path to the SDK. It should *NOT* be checked into Version Control Systems. -->
  <property environment="env" />
  <property file="pansi.properties" />


  <!-- NOTE(amas): setup ejt as javac -->
  <property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/>
  <property name="java.target" value="1.6" />
  <property name="java.source" value="1.6" />
  

  <!-- ENDNOTE -->


  <!-- The build.properties file can be created by you and is never touched by the 'android' tool. This is the place to change some of the default property values used by the Ant rules. Here are some properties you may want to change/update: source.dir The name of the source directory. Default is 'src'. out.dir The name of the output directory. Default is 'bin'. Properties related to the SDK location or the project target should be updated using the 'android' tool with the 'update' action. This file is an integral part of the build system for your application and should be checked into Version Control Systems. -->
  <property file="build.properties" />
  <property name="version.code" value="911" />
  <property name="version.chid" value="" />

  <property name="opt.findbug" value="" />
  <property name="opt.patch.debugoff" value=".patch/debug-off.patch" />

  <!-- final release dir -->
  <property name="dir.final.output" value="${env.WORKSPACE}/bin" />

  

  <!-- The default.properties file is created and updated by the 'android' tool, as well as ADT. This file is an integral part of the build system for your application and should be checked into Version Control Systems. -->
  <property file="default.properties" />

  <!-- Custom Android task to deal with the project target, and import the proper rules. This requires ant 1.6.0 or above. -->
  <path id="android.antlibs">
    <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
    <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
    <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
  </path>

  <taskdef name="setup"
           classname="com.android.ant.SetupTask"
           classpathref="android.antlibs" />

  <!-- extension targets. Uncomment the ones where you want to do custom work in between standard targets -->
  <target name="-pre-build" depends="-set-channel, -debugoff">
    <tstamp>
      <format property="var.ctime" pattern="yyyyMMdd-hh-mm" />
    </tstamp>


    <exec executable="git" outputproperty="var.git.version.shot">
      <arg value="rev-parse"/>
      <arg value="--short"/>
      <arg value="HEAD"/>
    </exec>

    <mkdir dir="${dir.final.output}"/>

    <echo> [amas] : 准备开始构建 </echo>
    <echo> [amas] : 版本号   = ${version.code}           </echo>
    <echo> [amas] : 渠道号   = ${version.chid}           </echo>
    <echo> [amas] : 构建时间 = ${var.ctime}              </echo>
    <echo> [amas] : 代码版本 = ${var.git.version.shot}   </echo>
    <echo> [amas] : -------------------------------------</echo>
    <echo> [amas] : 输出到   = ${dir.final.output}       </echo>
  </target>

  <target name="-post-build">
    <echo> [amas] : 基本构建完毕 </echo>
  </target>

<!-- [This is typically used for code obfuscation. Compiled code location: ${out.classes.absolute.dir} If this is not done in place, override ${out.dex.input.absolute.dir}] <target name="-post-compile"> </target> -->


  <!-- Execute the Android Setup task that will setup some properties specific to the target, and import the build rules files. The rules file is imported from <SDK>/platforms/<target_platform>/ant/ant_rules_r#.xml To customize existing targets, there are two options: - Customize only one target: - copy/paste the target into this file, *before* the <setup> task. - customize it to your needs. - Customize the whole script. - copy/paste the content of the rules files (minus the top node) into this file, *after* the <setup> task - disable the import of the rules by changing the setup task below to <setup import="false" />. - customize to your needs. -->
 <setup />

 <target name ="compile" 
         depends ="-pre-build, -aidl, -resource-src, -pre-compile" 
         description="*.java ---> *.class">

    <if condition="${manifest.hasCode}">
      <then>
        <!-- If android rules are used for a test project, its classpath should include tested project's location -->
        <condition property="extensible.classpath" value="${tested.project.absolute.dir}/bin/classes" else=".">
          <isset property="tested.project.absolute.dir"/>
        </condition>

        <condition property="extensible.libs.classpath" value="${tested.project.absolute.dir}/libs" else="${jar.libs.dir}">
          <isset property="tested.project.absolute.dir"/>
        </condition>

        <javac encoding="${java.encoding}" 
               source="${java.source}" 
               target="${java.target}" 
               debug="true" 
               extdirs="" 
               destdir="${out.classes.absolute.dir}" 
               bootclasspath="${internal.classpath}" 
               bootclasspathref="android.target.classpath" 
               verbose="${verbose}" 
               classpath="${extensible.classpath}" 
               classpathref="jar.libs.ref">
          <src path="${source.absolute.dir}"/>
          <src path="${gen.absolute.dir}"/>
          <src refid="project.libraries.src"/>
          <!-- 下面这一句就是强制使用内部jar,忽略校验-->
          <compilerarg line="-warn:+forbidden"/>
          <classpath>
            <fileset dir="${extensible.libs.classpath}" includes="*.jar"/>
          </classpath>
        </javac>
      </then>
      <else>
        <echo>hasCode = false. Skipping...</echo>
      </else>
    </if>
  </target>

  <!--====================================================[ 设置渠道号 ] -->
  <target name = "-set-channel">
    <exec executable="xmlstarlet" output="AndroidManifest.xml">
      <arg value="ed"/>
      <arg value="-P"/>
      <arg value="-N"/>
      <arg value="android=http://schemas.android.com/apk/res/android"/>
      <arg value="-u"/>
      <arg value="/manifest/application/meta-data[@android:name='UMENG_CHANNEL']/@android:value"/>
      <arg value="-v"/>
      <arg value="${version.chid}"/>
      <arg value="AndroidManifest.xml"/>
    </exec>
  </target>

  <!--====================================================[ 关闭Log信息 ] -->
  <target name = "-debugoff">
    <exec executable="git">
      <arg value="apply"/>
      <arg value="${opt.patch.debugoff}"/>
    </exec>
  </target>

  <!--====================================================[ 优化APK大小 ] -->
  <target name = "+release" depends="release">
    <echo>压缩资源: ${out.release.file}</echo>
    <echo>输出到:${out.absolute.dir}/${version.code}_${ant.project.name}_${var.ctime}_${var.git.version.shot}_${version.chid}.final.apk</echo>
    <exec executable="repackage">
      <arg value="-apk"/>
      <arg value="${out.release.file}"/>
      <arg value="-o"/>
      <arg value="${dir.final.output}/${version.code}_${ant.project.name}_${var.ctime}_${var.git.version.shot}_${version.chid}.final.apk"/>
    </exec>
  </target>
  
  <!--====================================================[ 清理输出目录 ] -->
  <target name = "rm-output-dir">
      <delete dir="${dir.final.output}"/>
  </target>
</project>
  • -debugoff 用于关闭log信息
  • +release 用于优化最终安装包
  • rm-output-dir 清除输出目录

local.properties 

internal.classpath=${env.ANDROID_INTERNAL_LIBS_9}            # 内部jar包的路径的 key.store=${env.KEYSTORE_FILE}                               # 签名文件 key.alias=${env.KEYSTORE_ALISE}                              # 别名 key.store.password=${env.KEYSTORE_PASS_S}                    # 密码 key.alias.password=${env.KEYSTORE_PASS_A}                    # 别名密码 android.library.reference.1=${env.PATH_TO_YOUR_LIB_PROJECT}. # 依赖的第三方源码库 sdk.dir=${env.ANDROID_HOME}                                  # Android SDK安装目录 

default.properties 

此文件中主要配置两项内容:

  • 工程的APILevel
  • proguard配置文件的路径(如果不使用proguard简单注释掉即可)
    target=android-8
    proguard.config=proguard.flags
    

proguard.flags 

proguard配置文件,不再赘述.

Sun javac + Android SDK + ANT 构建 

如果不依赖于内部jar包,直接使用下面的命令创建Android工程即可

$ android create project -a MainActivity -k org.android.demo -p ./HelloWorld -n HelloWorld -t 8
--- option ---
-a             -- Name of the default Activity that is created <required>                                                                                                              
-k             -- Android package name for the application <required>                                                                                                                  
-p             -- The new projects directory <required>                                                                                                                                
--package  -n  -- Project name                                                                                                                                                         
-t             -- Target ID of the new project  <required>

Android2.2(Froyo)之后的编译环境必需在64位操作系统上方能工作

如何在Android编译环境下使用proguard

Android2.2之后的编译环境中都自动包含proguard的支持, 如果应用在编译环境下需要启用proguard, 需要修改工程下的Android.mk文件,
加入参数:

# the value should be full | custom | optonly
LOCAL_PROGUARD_ENABLED := custom
LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags

其中:

LOCAL_PROGUARD_ENABLED

full/custom/optonly

LOCAL_PROGUARD_FLAGS

自定义proguard规则文件的路径

androidi编译环境,默认只对应用进行优化,并不会进行扰乱, 如果需要扰乱,需要修改默认规则:build/core/proguard.flags, 注释掉'-dontobfuscate'

...
# Don't obfuscate. We only need dead code striping.
#-dontobfuscate
...

怎样还原被扰乱的StackTrace

  1. proguard扰乱后会保存mapping文件,首先找到这个文件,如果是android 编译环境,通常就在工程目录下:mapping-used-to-retrace-exceptions.txt
  2. 将stacktrace保存到文件中,比如: obfuscated_trace.txt
  3. 使用retrace脚本还原stactrace,

比如:

#命令格式:  retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
$ retrace.bat -verbose mapping-used-to-retrace-exceptions.txt obfuscated_trace.txt

常见错误

Warning: library class xxx depends on program class org.xmlpull.v1.XmlPullParser

Warning: library class android.content.IntentFilter depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.graphics.drawable.ClipDrawable depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.graphics.drawable.RotateDrawable depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.content.pm.XmlSerializerAndParser depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.graphics.drawable.InsetDrawable depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.content.Intent depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.graphics.drawable.AnimatedRotateDrawable depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.accounts.AccountAuthenticatorCache$MySerializer depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.accounts.AccountAuthenticatorCache$MySerializer depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.graphics.drawable.GradientDrawable depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.view.LayoutInflater depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.view.LayoutInflater depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class android.util.XmlPullAttributes depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class com.android.internal.util.XmlUtils depends on program class org.xmlpull.v1.XmlPullParser
Warning: library class com.android.internal.util.XmlUtils depends on program class org.xmlpull.v1.XmlPullParser
...

原因是proguard发现org.xmlpull.v1.XmlPullParser出现在多个jar包中,因为android.jar中本身包含此包,所以可以考虑去掉.

如何在Android编译环境下引入第三方Jar包

1. 假如我们的应用使用了3part.jar, 首先再应用目录下建立libs目录, 然后将3part.jar拷贝到其中。

2. 修改Android.mk文件,在include $(BUILD_PACKAGE)指令后加上如下设置 :

include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := 3part:libs/3part.jar
include $(BUILD_MULTI_PREBUILT)

其中LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES的配置格式如下:
<库名>:<路径>

比如本例中 3part:libs/3part.jar, 库名为"3part", Jar文件路径为"libs/3part.jar"

引入多个jar包可参考一下示例(Android.mk):

LOCAL_STATIC_JAVA_LIBRARIES += android-common libA libB
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES += libA:libs/A.jar
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES += libB:libs/B.jar

3. 修改LOCAL_STATIC_JAVA_LIBRARIES选项

在Android.mk文件中,找到LOCAL_STATIC_JAVA_LIBRARIES, 将<库名>加入到其中。 比如:

LOCAL_STATIC_JAVA_LIBRARIES += android-common 3part

CentOS + Ant 1.6.5 + Android SDK Build Error

Started by user zhoujiabo
Checkout:workspace / /home/hudson/.hudson/jobs/AOTKiller/workspace - hudson.remoting.LocalChannel @78715052
Using strategy: Default
Checkout:workspace / /home/hudson/.hudson/jobs/AOTKiller/workspace - hudson.remoting.LocalChannel @78715052
GitAPI created
Fetching changes from the remote Git repository
Fetching upstream changes from git @192 .168. 4.31 :netqin-task-killer.git
[workspace] $ /usr/bin/git fetch -t git @192 .168. 4.31 :netqin-task-killer.git +refs/heads/*:refs/remotes/origin/*
[workspace] $ /usr/bin/git ls-tree HEAD
Seen branch in repository origin/master
Commencing build of Revision c47c5ba96702b8c502835fd2f2943fbaf0c6954d (origin/master)
GitAPI created
Checking out Revision c47c5ba96702b8c502835fd2f2943fbaf0c6954d (origin/master)
[workspace] $ /usr/bin/git checkout -f c47c5ba96702b8c502835fd2f2943fbaf0c6954d
[workspace] $ /usr/bin/git tag -a -f -m "Hudson Build #2" hudson-AOTKiller- 2
No change to record in branch origin/master
[workspace] $ ant -file ant.xml clean release
Buildfile: ant.xml
     [setup] Android SDK Tools Revision 8
     [setup] Project Target: Android 2.2
     [setup] API level: 8
 
     [setup] ------------------
     [setup] Resolving library dependencies:
     [setup] No library dependencies.
 
     [setup] ------------------
 
     [setup] WARNING: Attribute minSdkVersion in AndroidManifest.xml ( 7 ) is lower than the project target API level ( 8 )
 
     [setup] Importing rules file: tools/ant/main_rules.xml
 
clean:
    [delete] Deleting directory /home/hudson/.hudson/jobs/AOTKiller/workspace/bin
    [delete] Deleting directory /home/hudson/.hudson/jobs/AOTKiller/workspace/gen
 
-set-release-mode:
 
-release-obfuscation-check:
 
-dirs:
      [echo] Creating output directories if needed...
     [mkdir] Created dir: /home/hudson/.hudson/jobs/AOTKiller/workspace/bin
     [mkdir] Created dir: /home/hudson/.hudson/jobs/AOTKiller/workspace/gen
     [mkdir] Created dir: /home/hudson/.hudson/jobs/AOTKiller/workspace/bin/classes
 
-pre-build:
 
-resource-src:
      [echo] Generating R.java / Manifest.java from the resources...
      [ null ] /usr/android/ default /platform-tools/aapt: /usr/lib/libz.so. 1 : no version information available (required by /usr/android/ default /platform-tools/aapt)
 
-aidl:
      [echo] Compiling aidl files into Java classes...
 
BUILD FAILED
java.lang.NoSuchMethodError: org.apache.tools.ant.types.FileSet.iterator()Ljava/util/Iterator;
 
Total time: 0 seconds
Finished: FAILURE

解决方法: 升级ant到1.7以上

CentOSのantのバージョンは 1.6 . 5 でした。古い・・・ソース確認しましたが、 1.6 . 5 のantにはFileSet.iterator()が無いのでエラーになったみたいです。
多分 1.7 以上が必要です。最新版をダウンロードしてきましょう。
http: //ant.apache.org/bindownload.cgi
最新版のantでビルドします。

为什么通过Android2.1/2.2环境编译出的apk中,不包含hdpi相关目录

默认编译脚本的行为就是如此,你可以到build目录下,检索mdpi关键字:

find . -type f -name '*' -print0 | xargs - 0 grep --color -n mdpi

其中: 

core/product_config.mk:200 行可以加入打包的dpi类型(比如: 我们加入hdpi):

./core/product_config.mk: 200 :  $( if $(filter %dpi,$(PRODUCT_LOCALES)),,mdpi,hdpi))

重新编译,打包apk中会加入hdpi相关资源,

另外,从脚本中不难看出,也可以通过PRODUCT_LOCALES来指定额外支持的dpi, 且待日后研究.

Build Flow

The good thing about building manually your apk is that you don’t have to name your resources directory to res, you can name it anything you want.

Step 1: Generate Resource java code and packaged Resources

aapt  package -f -M ${manifest.file} -F ${packaged.resource.file} -I ${path.to.android-jar.library} -S ${android-resource-directory} [-m -J ${folder.to.output.the.R.java}]

Step 2: Compile java source codes + R.java

use javac

Step 3: Convert classes to Dalvik bytecodes

use dx.bat

dx.bat  –dex  –output=${output.dex.file}  ${compiled.classes.directory}  ${jar files..}

Step 4: Create unsigned APK

use apkbuilder

apkbuilder  ${output.apk.file} -u -z  ${packagedresource.file} -f  ${dex.file}

or

apkbuilder  ${output.apk.file} -u -z  ${packagedresource.file} -f  ${dex.file}  -rf  ${source.dir}  -rj  ${libraries.dir}

-rf = resources required for compiled source files?
-rj = resources required for jar files

Step 5: Generate a key

use keytool

Step 6: Sign APK

use jarsigner

jarsigner  -keystore ${keystore} -storepass  ${keystore.password} -keypass ${keypass} -signedjar ${signed.apkfile} ${unsigned.apkfile} ${keyalias}

Step 7: Publish

use adb

adb -d install -r ${signed.apk}

Inspecting your APK file:

aapt list -v latest.apk

Open questions:
1. Can you include more than one dex file in the apk?
2. Can you have dex file named other than classes.dex in the apk?
3. Does an apk have to have a packaged resource?

Note: If upon installing your app using adb you see this error code FAILED_INSTALL_DEXOPT then most likely that either you don’t have classes.dex or you don’t have a packaged resource in the apk

Build Android With Java1.6

虽然Android官方只允许使用Java1.5作为编译工具,但是通过修改Makefile,可以跳过Java Version Check, 从而使用Java1.6进行编译。

如果本地有些常用工具依赖Java1.6, 同时你也想在Android Build环境下进行编译,下面的方法不妨一试。

$ cat build/core/main.mk | grep java_version
# 将java_version写死为 1.5 , 或者注释掉相关内容,比如:
----------------------------------------
119 # Check for the correct version of javac
120 #javac_version := $(shell javac -version 2 >& 1 | head -n 1 | grep '[ "]1\.5[\. "$$]' )
121 # ifeq ($(strip $(javac_version)),)
122 # $(info ************************************************************)
123 # $(info You are attempting to build with the incorrect version)
124 # $(info of javac.)
125 # $(info $(space))
126 # $(info Your version is: $(shell javac -version 2 >& 1 | head -n 1 ).)
127 # $(info The correct version is: 1.5 .)
128 # $(info $(space))
129 # $(info Please follow the machine setup instructions at)
130 # $(info $(space)$(space)$(space)$(space)http: //source.android.com/download)
131 # $(info ************************************************************)
132 # $(error stop)
133 #endif
----------------------------------------

@hide 注记

@hide has a very important meaning. It is used to mark public APIs 
that are not to be exported in the SDK. This is used by the Android 
team to have APIs accessible across packages without having them 
available to applications. APIs marked with @hide are considered 
private to the Android platform and can change at any time, you cannot 
rely on them.

从APK文件中删除签名信息

$ zip -d PansiMsg.apk 'META-INF*'
deleting: META-INF/MANIFEST.MF
deleting: META-INF/CERT.SF
deleting: META-INF/CERT.RSA
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值