http://www.ikoding.com/build-android-project-with-maven/

之前一直在做WEB前端项目,前段时间接手第一个Android项目,拿到代码之后,先试着run起来再说,导入eclipse,一堆错误,设置classpath依赖,折腾半天,还是编译错误,于是联系项目接口人,得知他有一个Android库项目没有提交到SVN,晕。。。

对于习惯使用Maven管理Java项目的我来说,自然想到能否用Maven构建Android项目呢?于是开始Google、百度,发现已经有前人做过这样的实践了,不过在使用过程中还是遇到不少问题,后面经过各种努力终于能比较顺地使用了,这篇文章对如何使用Maven构建Android项目作了简要总结。如果你和我一样饱受项目依赖管理的折磨,和我一样讨厌项目打包发布时的繁琐,希望能通过Maven让这一切自动化完成。那么,这篇文章或许对你有用。

1. 环境搭建

    • JDK与Android SDK安装

做Android开发,这里无需多说,但安装完成后需要正确设置JAVA_HOME、CLASSPATH、ANDROID_HOME等环境变量。其中ANDROID_HOME为Android SDK安装的根目录。并将%ANDROID_HOME%\tools和%ANDROID_HOME%\platform-tools值添加到Path变量中。

    • Maven安装

这里无需多说,下载安装Maven并正确设置环境变量即可。

    • IDE支持
      • Eclipse

大多数人都在使用Eclipse开发Android应用,如果你用Eclipse做Android开发,推荐下载eclipse-with-m2e-and-adt,该版本已经安装了ADT、m2e、m2e-android等重要插件,支持在Eclipse中使用Maven进行Android应用开发。当时为了安装这些插件费了好大劲,所以,如果你看到这里,可以直接下载它,不用像我一样去做那些没有意义又浪费时间的事情。

      • IntelliJ IDEA

做Java开发,你不能不知道的神器,完美支持使用Maven构建Android应用,强烈推荐。即使是装了插件的Eclipse对使用Maven构建Android应用仍然支持不好。如果你是Eclipse的信徒,你也可以试试它,如果你能习惯它,你一定会被它的强大所吸引。

      • NetBeans IDE

NetBeans也支持Android开发,但没怎么了解,用的人应该也比较少。

2. 项目构建

以下是我的项目中使用的pom文件,因为涉及保密,部分地方做过修改,但整体结构没有改变,可以清楚地说明问题。

<?xml version="1.0" encoding="UTF-8"?>
<project 
   xmlns="http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
    <groupId>com.ikoding.android</groupId>
    <artifactId>android-app-quickstart</artifactId>
    <version>1.0.0</version>
    <packaging>apk</packaging>
    <name>Android Application Quick Satrt</name>

<dependencies>
        <dependency>
            <groupId>com.google.android</groupId>
            <artifactId>android</artifactId>
            <version>4.1.1.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.android</groupId>
            <artifactId>support-v4</artifactId>
            <version>r7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.james</groupId>
            <artifactId>apache-mime4j</artifactId>
            <version>0.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
            <version>4.2.2</version>
        </dependency>
        <dependency>
            <groupId>com.ikoding.android</groupId>
            <artifactId>android-base</artifactId>
            <version>2.0.0</version>
            <type>apklib</type>
        </dependency>
    </dependencies>

<properties>
        <keystore.filename>app-quickstart.keystore</keystore.filename>
        <keystore.storepass>2013@ikoding</keystore.storepass>
        <keystore.keypass>2013@ikoding</keystore.keypass>
        <keystore.alias>ikoding-android-app</keystore.alias>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

<build>
        <finalName>${project.artifactId}-${project.version}-${manifest.metadata.id}</finalName>
        <sourceDirectory>src</sourceDirectory>
        <resources>
            <resource>
                <directory>.</directory>
                <filtering>true</filtering>
                <targetPath>../filtered-resources</targetPath>
                <includes>
                    <include>AndroidManifest.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>**/*</include>
                </includes>
                <excludes>
                    <exclude>**/env-*.properties</exclude>
                </excludes>
            </resource>
        </resources>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                    <artifactId>android-maven-plugin</artifactId>
                    <version>3.5.0</version>
                    <extensions>true</extensions>
                    <executions>
                        <execution>
                            <id>run</id>
                            <goals>
                                <goal>deploy</goal>
                                <goal>run</goal>
                            </goals>
                            <phase>install</phase>
                        </execution>
                    </executions>
                    <configuration>
                        <proguardConfig>proguard.cfg</proguardConfig>
                        <proguardSkip>${project.build.proguardSkip}</proguardSkip>
                        <manifestDebuggable>${manifest.debuggable}</manifestDebuggable>
                        <androidManifestFile>target/filtered-resources/AndroidManifest.xml</androidManifestFile>
                        <release>${project.build.release}</release>
                        <run>
                            <debug>${project.build.debug}</debug>
                        </run>
                        <runDebug>${project.build.runDebug}</runDebug>
                        <sign>
                            <debug>${project.build.sign.debug}</debug>
                        </sign>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                <artifactId>android-maven-plugin</artifactId>
                <configuration>
                    <sdk>
                        <platform>15</platform>
                    </sdk>                
</configuration>            
</plugin>            
<plugin>                
<groupId>org.codehaus.mojo</groupId>                
<artifactId>keytool-maven-plugin</artifactId>                
<version>1.2</version>                
<!--<executions>-->                    
<!--<execution>-->                        
<!--<goals>-->                            
<!--<goal>clean</goal>-->                            
<!--<goal>generateKeyPair</goal>-->                        
<!--</goals>-->                        
<!--<phase>generate-resources</phase>-->                    
<!--</execution>-->                
<!--</executions>-->                
<configuration>                    
<keystore>${keystore.filename}</keystore>                    
<storepass>${keystore.storepass}</storepass>                    
<keypass>${keystore.keypass}</keypass>                    
<alias>${keystore.alias}</alias>                    
<dname>CN=iKoding, OU=iKoding, O=iKoding, C=CN</dname>                    
<sigalg>SHA1withDSA</sigalg>                    
<validity>10000</validity>                    
<keyalg>DSA</keyalg>                    
<keysize>1024</keysize>                
</configuration>            
</plugin>            
<plugin>                
<groupId>org.apache.maven.plugins</groupId>                
<artifactId>maven-compiler-plugin</artifactId>                
<configuration>                    
<source>1.6</source>                    
<target>1.6</target>                    
<encoding>UTF8</encoding>                
</configuration>            
</plugin>            
<plugin>                
<groupId>org.apache.maven.plugins</groupId>                
<artifactId>maven-eclipse-plugin</artifactId>                
<version>2.9</version>                
<configuration>                    
<projectnatures>                        
<projectnature>org.eclipse.m2e.core.maven2Nature</projectnature>                        
<projectnature>com.android.ide.eclipse.adt.AndroidNature</projectnature>                        
<projectnature>org.eclipse.jdt.core.javanature</projectnature>                    
</projectnatures>                
</configuration>            
</plugin>            
<plugin>                
<groupId>org.apache.maven.plugins</groupId>                
<artifactId>maven-resources-plugin</artifactId>                
<version>2.6</version>                
<executions>                    
<execution>                        
<phase>initialize</phase>                        
<goals>                            
<goal>resources</goal>                        
</goals>                    
</execution>                
</executions>            
</plugin>        
</plugins>    
</build>

<profiles>        
<profile>            
<id>debug</id>            
<activation>                
<activeByDefault>true</activeByDefault>            
</activation>            
<build>                
<filters>                    
<filter>resources/env-debug.properties</filter>                
</filters>            
</build>            
<properties>                
<project.build.debug>true</project.build.debug>                
<project.build.runDebug>false</project.build.runDebug>                
<project.build.proguardSkip>true</project.build.proguardSkip>                
<project.build.release>false</project.build.release>                
<project.build.sign.debug>true</project.build.sign.debug>                
<manifest.debuggable>true</manifest.debuggable>            
</properties>        
</profile>        
<profile>            
<id>release</id>            
<properties>                
<project.build.debug>false</project.build.debug>                
<project.build.runDebug>false</project.build.runDebug>                
<project.build.proguardSkip>false</project.build.proguardSkip>                
<project.build.release>true</project.build.release>                
<project.build.sign.debug>false</project.build.sign.debug>                
<manifest.debuggable>false</manifest.debuggable>            
</properties>            
<build>                
<filters>                    
<filter>resources/env-release.properties</filter>                
</filters>                
<plugins>                    
<plugin>                        
<groupId>org.apache.maven.plugins</groupId>                        
<artifactId>maven-jarsigner-plugin</artifactId>                        
<version>1.2</version>                        
<executions>                            
<execution>                                
<id>sign</id>                                
<goals>                                    
<goal>sign</goal>                                
</goals>                                
<phase>package</phase>                                
<inherited>true</inherited>                                
<configuration>                                    
<includes>                                        
<include>${project.build.outputDirectory}/*.apk</include>                                    
</includes>                                    
<keystore>${keystore.filename}</keystore>                                    
<storepass>${keystore.storepass}</storepass>                                    
<keypass>${keystore.keypass}</keypass>                                    
<alias>${keystore.alias}</alias>                                
</configuration>                            
</execution>                        
</executions>                    
</plugin>                
</plugins>            
</build>        
</profile>        
<!-- 渠道profiles -->        
<profile>            
<id>channel-test</id>            
<activation>                
<activeByDefault>true</activeByDefault>            
</activation>            
<properties>                
<manifest.metadata.id>test</manifest.metadata.id>                
<manifest.metadata.channel>test</manifest.metadata.channel>            
</properties>        
</profile>        
<profile>            
<id>channel-91</id>            
<properties>                
<manifest.metadata.id>91-market</manifest.metadata.id>                
<manifest.metadata.channel>91 market</manifest.metadata.channel>            
</properties>        
</profile>        
<profile>            
<id>channel-yingyonghui</id>            
<properties>                
<manifest.metadata.id>yingyonghui-market</manifest.metadata.id>                
<manifest.metadata.channel>yingyonghui market</manifest.metadata.channel>            
</properties>        
</profile>        
<profile>            
<id>channel-tongbutui</id>            
<properties>                
<manifest.metadata.id>tongbutui-market</manifest.metadata.id>                
<manifest.metadata.channel>tongbutui market</manifest.metadata.channel>            
</properties>        
</profile>        
<profile>            
<id>channel-tengxun</id>            
<properties>                
<manifest.metadata.id>tengxun-market</manifest.metadata.id>                
<manifest.metadata.channel>tengxun market</manifest.metadata.channel>            
</properties>        
</profile>        
<profile>            
<id>channel-anzhi</id>            
<properties>                
<manifest.metadata.id>anzhi-market</manifest.metadata.id>                
<manifest.metadata.channel>anzhi market</manifest.metadata.channel>            
</properties>        
</profile>        
<profile>            
<id>channel-gfan</id>            
<properties>                
<manifest.metadata.id>gfan</manifest.metadata.id>                
<manifest.metadata.channel>gfan</manifest.metadata.channel>            
</properties>        
</profile>    
</profiles>
</project>

以上的pom文件基本上涉及到了一个Android应用构建过程中的各个方面,以下针对其中比较重要的一些点作简要说明。

    • 依赖管理

这也是我们使用Maven构建Android项目的重要原因之一,值得注意的是以下部分:

<dependency>
    <groupId>com.ikoding.android</groupId>
    <artifactId>android-base</artifactId>
    <version>2.0.0</version>
    <type>apklib</type>
</dependency>

以上依赖的type为apklib,这就是我们项目中的Library Project,此外,我们还可以使用Maven管理Native库,亦即我们项目中所依赖的那些so库文件。

    • 资源过滤

项目中总会有一些和环境相关的配置,比如线下测试环境和线上环境的配置可能不一样,为此我们在pom中分别定义了id为debug和release两个profile,并使用不同的filter进行资源过滤。

    • 生成keystore

我们使用了keytool-maven-plugin来生成签名时所需的keystore文件,我在properties中定义了生成keystore文件所需的信息,如下所示:

<properties>
    <keystore.filename>app-quickstart.keystore</keystore.filename>
    <keystore.storepass>2013@ikoding</keystore.storepass>
    <keystore.keypass>2013@ikoding</keystore.keypass>
    <keystore.alias>ikoding-android-app</keystore.alias>
</properties>

可以运行mvn keytool:generateKeyPair命令来生成keystore文件,之后在应用正式打包发布时候就会使用该keystore文件进行签名。

    • 生成渠道包

项目最终发布时需要根据渠道号生成不同的渠道包,我们可以利用Maven的资源过滤对AndroidManifest.xml文件中的渠道信息进行替换,例如我们上面的pom文件对Manifest文件中${manifest.metadata.channel}的渠道信息进行了替换,还有一些其他信息如Manifest文件的debuggable属性也可以针对不同配置进行替换,比如我们在日常开发中需要将该值设为true,而在项目正式发布时需要将该值设为false,我们以上的pom文件中就定义了多个针对不同渠道的profile。

    • 代码混淆

项目正式发布时需要对代码进行混淆处理,我们只需在android-maven-plugin配置中指定proguardConfig文件,比如我们在上面的pom中指定proguard文件为proguard.cfg,项目构建过程中会自动运行代码混淆,并且可以通过proguardSkip属性指定是否运行代码混淆任务,这样我们就可以在日常开发调试过程中关闭混淆,而在项目发布时开启混淆。

    • 签名

我们使用了maven-jarsigner-plugin对apk文件进行签名,签名使用的证书就是之前生成的keystore文件。

    • 调试

我在使用Maven构建Android项目之初遇到的问题就是无法进行断点调试,这显然会降低开发效率,后来发现可以通过android-maven-plugin的runDebug属性开启调试,这样可以在应用程序启动时连接debugger进行调试。

    • IDE支持

再次回到这个话题上,在导入eclipse之前,你需要先运行mvn eclipse:eclipse命令,生成eclipse项目文件,因为eclipse对android-maven-plugin支持不好。

3. 常用命令

以下是使用Maven构建Android项目中常用的一些命令,你可以根据需要选择合适的命令。

    • mvn clean package

打包,但不部署。

    • mvn clean install

打包,部署并运行。

    • mvn clean package android:redeploy android:run

这个命令通常用于手机上已经安装了要部署的应用,但签名不同,所以我们打包的同时使用redeploy命令将现有应用删除并重新部署,最后使用run命令运行应用。

    • mvn android:redeploy android:run

不打包,将已生成的包重新部署并运行。

    • mvn android:deploy android:run

部署并运行已生成的包,与redeploy不同的是,deploy不会删除已有部署和应用数据。

4. 总结

使用Maven构建Android应用很好地解决了依赖管理的问题,而且我们也可以将很多繁琐的任务自动化完成。在使用过程中主要的问题是Eclipse的m2e-android插件支持不够理想,而IntelliJ IDEA在这方便显得非常强大,让我得以将Maven和IDE的优势结合在一起。你可以在此基础上开发出一些常用类型项目的maven archetype,这样你就可以更快的开始一个新的项目。

5. 坚持?改变?

事实上我所在的团队成员最初都在使用Eclipse进行开发,后来在我的一再“忽悠”下,大家都接受了这种新的开发方式,并逐渐转向IntelliJ IDEA。这需要你有做出改变的勇气,毕竟改变是痛苦的,没有充分的理由就很难说服别人改变,但当你适应后就会有新的收获。

附件

下面是Android Library Project的pom文件,它比文中的Application Project的pom文件看起来要简单很多:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.ikoding.android</groupId>
    <artifactId>android-lib-quickstart</artifactId>
    <version>1.0.0</version>
    <packaging>apklib</packaging>
    <name>Android Library Project Quick Start</name>

<dependencies>
        <dependency>
            <groupId>com.google.android</groupId>
            <artifactId>android</artifactId>
            <version>4.1.1.4</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

<build>
        <sourceDirectory>src</sourceDirectory>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                    <artifactId>android-maven-plugin</artifactId>
                    <version>3.5.0</version>
                    <extensions>true</extensions>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                <artifactId>android-maven-plugin</artifactId>
                <configuration>
                    <sdk>
                        <platform>15</platform>
                    </sdk>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <projectnatures>
                        <projectnature>org.eclipse.m2e.core.maven2Nature</projectnature>
                        <projectnature>com.android.ide.eclipse.adt.AndroidNature</projectnature>
                        <projectnature>org.eclipse.jdt.core.javanature</projectnature>
                    </projectnatures>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

使用Maven构建Android项目的更多相关文章

  1. gradle学习系列之eclipse中简单构建android项目

    看不到图片能够去訪问这个网址看看:http://pan.baidu.com/s/1o6FrFkA 一.什么是Gradle 官网www.gradle.org上介绍Gradle是升级版(evolved)的 ...

  2. 使用Gradle构建Android项目

    阅读目录 Gradle是什么? 环境需求 Gradle基本结构 任务task的执行 基本的构建定制 目录配置 签名配置 代码混淆设置 依赖配置 输出不同配置的应用 生成多个渠道包(以Umeng为例) ...

  3. 转】用Maven构建Mahout项目

    原博文出自于: http://blog.fens.me/hadoop-mahout-maven-eclipse/ 感谢! 用Maven构建Mahout项目 Hadoop家族系列文章,主要介绍Hadoo ...

  4. 转】用Maven构建Hadoop项目

    原博文出自于: http://blog.fens.me/hadoop-maven-eclipse/ 感谢!   用Maven构建Hadoop项目 Hadoop家族系列文章,主要介绍Hadoop家族产品 ...

  5. 使用Eclipse maven构建springmvc项目

    Eclipse maven构建springmvc项目 Listener 监听器 架构 使用Log4J监控系统日志邮件警报 2014-12-16 13:09:16 控制器在完成逻辑处理后,通常会产生一些 ...

  6. Maven学习:Eclipse使用maven构建web项目(转)

    Maven学习:Eclipse使用maven构建web项目(转) 8.更改class路径:右键项目,Java Build Path -> Source 下面应该有4个文件夹.src/main/j ...

  7. Maven管理Android项目1

    maven-android-plugin网站:https://code.google.com/p/maven-android-plugin/wiki/GettingStarted   android ...

  8. 利用Eclipse中的Maven构建Web项目(三)

    利用Eclipse中的Maven构建Web项目 1.将Maven Project转换成动态Web项目,鼠标右键项目,输入"Project Facets" 2.依据Dynamic W ...

  9. Eclipse Maven构建WebApp项目资源目录显示不全的原因与解决方式

    一.问题展示 1.Eclipse在使用Maven构建WebApp项目的时候,首先Maven的安装和配置都没有问题的,但是构建项目之后,Maven项目要求的几个必须要有的资源目录显示不了: 问题如下图: ...

随机推荐

  1. IOS- 单例

    单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 1.单例模式的要点: 显然单例模式的要点有三个:一是某个类只能有一个实例: ...

  2. yii和wp做博客

    第一步,安装yii和wp: 第二步,创建protected/components/ExceptionHandler.php文件 <?php class ExceptionHandler { pu ...

  3. [Android Pro] AES加密

    reference to :http://blog.csdn.net/wfung_kwok/article/details/7766427 package com.secufity.aes; impo ...

  4. September 18th 2016 Week 39th Sunday

    Be the king of the spiritual kingdom that is your heart. 在心灵的国土上,做自己的国王. Most often we are not able ...

  5. NSOperation使用

    1.继承NSOperation DownLoadImageTask.h #import <Foundation/Foundation.h> #import <UIKit/UIKit. ...

  6. jsp 过滤器 Filter 配置

    .如果要映射过滤应用程序中所有资源: <filter>    <filter-name>loggerfilter</filter-name>    <filt ...

  7. AngularJS模型 ng-model 指令

    ng-model 指令用于绑定应用程序数据到 HTML 控制器(input, select, textarea)的值. ng-model 指令可以将输入域的值与 AngularJS 创建的变量绑定.例 ...

  8. kvm 网桥

    root@ok Downloads]# cat /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 HWADDR=54:EE:75:4E:37: ...

  9. 重温WCF之WCF抛出异常的处理SOAP Fault(十二)

    1.(服务端)抛出和(客户端)捕获SOAP Fault 当我们需要客户端获取到WCF服务端的抛出的异常的时候,使用FaultException类 WCF类库在System.ServiceModel命名 ...

  10. Python 反编译工具uncompyle2

    如何反编译pyc uncompyle2 是一个可以将pyc文件转换为py源码的工具 下载地址:https://github.com/wibiti/uncompyle2 安装: setup.py ins ...