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. OSG快速生成一个带有纹理的四边形Geometry

    可以使用Geometry头文件中的 Geometry* createTexturedQuadGeometry osg::ref_ptr<osg::Texture2D> texture = ...

  2. FTL标签

    <#if blockObject ??> <#else> </if>判断对象是否存在 <#if componentid ?? &&compon ...

  3. [Android Pro] Gradle Tips#1-tasks

    reference to : http://trickyandroid.com/gradle-tip-1-tasks/ http://blog.csdn.net/lzyzsd/article/deta ...

  4. [Ubuntu] ubuntu10.04系统维护之Wine的安装

    在介绍安装wine之前,我想是有必要先介绍一下Wine的.当然,如果是Liunx的高手,我想是没必要看的,但是对于笔者这样的菜鸟级人物还是需要看一下的. Wine是一款Liunx下的模拟器软件,但是W ...

  5. PHP之MVC项目实战

    本文主要包括以下内容 类文件自动加载 路径管理 页面跳转 注册自动加载方法 配置文件系统 cookie session 类文件自动加载 在PHP中使用别的类时,需要载入类文件,如果类很多的话,需要重复 ...

  6. hdu 1005

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1005 思路:找规律题 #include<stdio.h> main() { ]; int ...

  7. java 接口与继承

    一.继承条件下的构造方法调用 运行 TestInherits.java 示例,观察输出,注意总结父类与子类之间构造方法的调用关系修改Parent构造方法的代码,显式调用GrandParent的另一个构 ...

  8. ListView + PopupWindow实现滑动删除

    原文:ListView滑动删除 ,仿腾讯QQ(鸿洋_) 文章实现的功能是:在ListView的Item上从右向左滑时,出现删除按钮,点击删除按钮把Item删除. 看过文章后,感觉没有必要把dispat ...

  9. md5sum

    [root@NB index]# ls index()().html index()().html index()().html index()().html index()().html [root ...

  10. probe函数何时调用的

    转自:http://blog.csdn.net/xiafeng1113/article/details/8030248 Linux中 probe函数何时调用的 所以的驱动教程上都说:只有设备和驱动的名 ...