mkdir D:\testapk\classes
javac D:\testapk\src\aimoxiu\theme\moxiutheme\*.java -classpath D:\testapk\android.jar -d D:\testapk\classes\
//这句命令是调用javac工具编译源代码,因此想编译得确保目标计算机已经安装JDK,输出存放的目录是刚才创建的目录
cd D:\testapk\classes
jar cvf aimoxiu.jar *.* //这句命令是把目录下*.class文件打成jar包,为了后面能够创建apk需要的classes.dex文件
cd..
dx --dex --output=classes.dex D:\testapk\classes\aimoxiu.jar //这句命令把刚才的jar包转换成android手机能够运行的dex文件
CreateApk.bat文件内容:
aapt package -z -u -x -f -M AndroidManifest.xml -S res -I android.jar -F moxiu.apk //这句命令生成应用程序apk文件
aapt add moxiu.apk classes.dex //这句命令把前面生成的classes.dex文件压缩到apk文件,最终生成可运行的apk应用程序
java -jar signapk.jar aimoxiu.x509.pem aimoxiu.pk8 moxiu.apk moxiu_theme.apk //这句命令是给生成的apk进行自动签名,无签名的apk是无法安装。因此一个android应用程序apk最终出炉啦!
del moxiu.apk /q //删除刚才生成的无签名的apk文件
Windows操作系统实现不用Eclipse工具实现apk程序编译及打包使用该工具,本人已经测试通过,生成的apk文件也能在android真机上运行,工具见附件
Linux操作系统实现也没任何问题,只要命令进行稍微处理即可!
aapt是Android Asset Packaging Tool的首字母缩写,这个工具包含在SDK的tools/目录下。查看、创建、更新与zip兼容的归档文件(zip、jar、apk)。它也能将资源文件编译成二进制包。
Windows:aapt.exe
注意:tools/目录是指android SDK目录下的/platforms/android-X/tools/
原始出处:http://zhaohaiyang.blog.51cto.com/2056753/486598
“打包“这个词听起来比较土,比较正式的说法应该是”构建项目软件包“,具体说就是将项目中的各种文件,比如源代码、编译生成的字节码、配置文件、文档,按照规范的格式生成归档,最常见的当然就是JAR包和WAR包了,复杂点的例子是Maven官方下载页面的分发包,它有自定义的格式,方便用户直接解压后就在命令行使用。作为一款”打包工具“,Maven自然有义务帮助用户创建各种各样的包,规范的JAR包和WAR包自然不再话下,略微复杂的自定义打包格式也必须支持,本文就介绍一些常用的打包案例以及相关的实现方式,除了前面提到的一些包以外,你还能看到如何生成源码包、Javadoc包、以及从命令行可直接运行的CLI包。
Packaging的含义
任何一个Maven项目都需要定义POM元素packaging(如果不写则默认值为jar)。顾名思义,该元素决定了项目的打包方式。实际的情形中,如果你不声明该元素,Maven会帮你生成一个JAR包;如果你定义该元素的值为war,那你会得到一个WAR包;如果定义其值为POM(比如是一个父模块),那什么包都不会生成。除此之外,Maven默认还支持一些其他的流行打包格式,例如ejb3和ear。你不需要了解具体的打包细节,你所需要做的就是告诉Maven,”我是个什么类型的项目“,这就是约定优于配置的力量。
为了更好的理解Maven的默认打包方式,我们不妨来看看简单的声明背后发生了什么,对一个jar项目执行mvn package操作,会看到如下的输出:
[INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ git-demo ---
[INFO] Building jar: /home/juven/git_juven/git-demo/target/git-demo-1.2-SNAPSHOT.jar
相比之下,对一个war项目执行mvn package操作,输出是这样的:
[INFO] --- maven-war-plugin:2.1:war (default-war) @ webapp-demo ---
[INFO] Packaging webapp
[INFO] Assembling webapp [webapp-demo] in [/home/juven/git_juven/webapp-demo/target/webapp-demo-1.0-SNAPSHOT]
[INFO] Processing war project
[INFO] Copying webapp resources [/home/juven/git_juven/webapp-demo/src/main/webapp]
[INFO] Webapp assembled in [90 msecs]
[INFO] Building war: /home/juven/git_juven/webapp-demo/target/webapp-demo-1.0-SNAPSHOT.war
对应于同样的package生命周期阶段,Maven为jar项目调用了maven-jar-plugin,为war项目调用了maven-war-plugin,换言之,packaging直接影响Maven的构建生命周期。了解这一点非常重要,特别是当你需要自定义打包行为的时候,你就必须知道去配置哪个插件。一个常见的例子就是在打包war项目的时候排除某些web资源文件,这时就应该配置maven-war-plugin如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<webResources>
<resource>
<directory>src/main/webapp</directory>
<excludes>
<exclude>**/*.jpg</exclude>
</excludes>
</resource>
</webResources>
</configuration>
</plugin>
源码包和Javadoc包
本专栏的《坐标规划》一文中曾解释过,一个Maven项目只生成一个主构件,当需要生成其他附属构件的时候,就需要用上classifier。源码包和Javadoc包就是附属构件的极佳例子。它们有着广泛的用途,尤其是源码包,当你使用一个第三方依赖的时候,有时候会希望在IDE中直接进入该依赖的源码查看其实现的细节,如果该依赖将源码包发布到了Maven仓库,那么像Eclipse就能通过m2eclipse插件解析下载源码包并关联到你的项目中,十分方便。由于生成源码包是极其常见的需求,因此Maven官方提供了一个插件来帮助用户完成这个任务:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
类似的,生成Javadoc包只需要配置插件如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin> 为了帮助所有Maven用户更方便的使用Maven中央库中海量的资源,中央仓库的维护者强制要求开源项目提交构件的时候同时提供源码包和Javadoc包。这是个很好的实践,读者也可以尝试在自己所处的公司内部实行,以促进不同项目之间的交流。
可执行CLI包
除了前面提到了常规JAR包、WAR包,源码包和Javadoc包,另一种常被用到的包是在命令行可直接运行的CLI(Command Line)包。默认Maven生成的JAR包只包含了编译生成的.class文件和项目资源文件,而要得到一个可以直接在命令行通过java命令运行的JAR文件,还要满足两个条件:
JAR包中的/META-INF/MANIFEST.MF元数据文件必须包含Main-Class信息。
项目所有的依赖都必须在Classpath中。
Maven有好几个插件能帮助用户完成上述任务,不过用起来最方便的还是maven-shade-plugin,它可以让用户配置Main-Class的值,然后在打包的时候将值填入/META-INF/MANIFEST.MF文件。关于项目的依赖,它很聪明地将依赖JAR文件全部解压后,再将得到的.class文件连同当前项目的.class文件一起合并到最终的CLI包中,这样,在执行CLI JAR文件的时候,所有需要的类就都在Classpath中了。下面是一个配置样例:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.juvenxu.mavenbook.HelloWorldCli</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
上述例子中的,我的Main-Class是com.juvenxu.mavenbook.HelloWorldCli,构建完成后,对应于一个常规的hello-world-1.0.jar文件,我还得到了一个hello-world-1.0-cli.jar文件。细心的读者可能已经注意到了,这里用的是cli这个classifier。最后,我可以通过java -jar hello-world-1.0-cli.jar命令运行程序。
自定义格式包
实际的软件项目常常会有更复杂的打包需求,例如我们可能需要为客户提供一份产品的分发包,这个包不仅仅包含项目的字节码文件,还得包含依赖以及相关脚本文件以方便客户解压后就能运行,此外分发包还得包含一些必要的文档。这时项目的源码目录结构大致是这样的:
pom.xml
src/main/java/
src/main/resources/
src/test/java/
src/test/resources/
src/main/scripts/
src/main/assembly/
README.txt
除了基本的pom.xml和一般Maven目录之外,这里还有一个src/main/scripts/目录,该目录会包含一些脚本文件如run.sh和run.bat,src/main/assembly/会包含一个assembly.xml,这是打包的描述文件,稍后介绍,最后的README.txt是份简单的文档。
我们希望最终生成一个zip格式的分发包,它包含如下的一个结构:
bin/
lib/
README.txt
其中bin/目录包含了可执行脚本run.sh和run.bat,lib/目录包含了项目JAR包和所有依赖JAR,README.txt就是前面提到的文档。
描述清楚需求后,我们就要搬出Maven最强大的打包插件:maven-assembly-plugin。它支持各种打包文件格式,包括zip、tar.gz、tar.bz2等等,通过一个打包描述文件(该例中是src/main/assembly.xml),它能够帮助用户选择具体打包哪些文件集合、依赖、模块、和甚至本地仓库文件,每个项的具体打包路径用户也能自由控制。如下就是对应上述需求的打包描述文件src/main/assembly.xml:
<assembly>
<id>bin</id>
<formats>
<format>zip</format>
</formats>
<dependencySets>
<dependencySet>
<useProjectArtifact>true</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<outputDirectory>/</outputDirectory>
<includes>
<include>README.txt</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/scripts</directory>
<outputDirectory>/bin</outputDirectory>
<includes>
<include>run.sh</include>
<include>run.bat</include>
</includes>
</fileSet>
</fileSets>
</assembly>首先这个assembly.xml文件的id对应了其最终生成文件的classifier。
其次formats定义打包生成的文件格式,这里是zip。因此结合id我们会得到一个名为hello-world-1.0-bin.zip的文件。(假设artifactId为hello-world,version为1.0)
dependencySets用来定义选择依赖并定义最终打包到什么目录,这里我们声明的一个depenencySet默认包含所有所有依赖,而useProjectArtifact表示将项目本身生成的构件也包含在内,最终打包至输出包内的lib路径下(由outputDirectory指定)。
fileSets允许用户通过文件或目录的粒度来控制打包。这里的第一个fileSet打包README.txt文件至包的根目录下,第二个fileSet则将src/main/scripts下的run.sh和run.bat文件打包至输出包的bin目录下。
打包描述文件所支持的配置远超出本文所能覆盖的范围,为了避免读者被过多细节扰乱思维,这里不再展开,读者若有需要可以去参考这份文档。
最后,我们需要配置maven-assembly-plugin使用打包描述文件,并绑定生命周期阶段使其自动执行打包操作:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>运行mvn clean package之后,我们就能在target/目录下得到名为hello-world-1.0-bin.zip的分发包了。
小结
打包是项目构建最重要的组成部分之一,本文介绍了主流Maven打包技巧,包括默认打包方式的原理、如何制作源码包和Javadoc包、如何制作命令行可运行的CLI包、以及进一步的,如何基于个性化需求自定义打包格式。这其中涉及了很多的Maven插件,当然最重要,也是最为复杂和强大的打包插件就是maven-assembly-plugin。事实上Maven本身的分发包就是通过maven-assembly-plugin制作的,感兴趣的读者可以直接查看源码一窥究竟。
案例:
1、 环境介绍:
项目采用的是 SSH 框架技术,模板视图用的是 FreeMarker ,对于编码问题做了以下的配
置:
① tomcat 服务器没配置 URIEncoding 参数。
② struts2 配置文件配置了如下的参数:
<!-- 编码 -->
< constant name = "struts.i18n.encoding" value = "UTF-8" />
③ web.xml 进行了如下配置:
<!-- 编码处理过滤器 -->
< filter >
< filter-name > encodingFilter </ filter-name >
< filter-class >
org.springframework.web.filter.CharacterEncodingFilter
</ filter-class >
< init-param >
< param-name > encoding </ param-name >
< param-value > utf-8 </ param-value >
</ init-param >
< init-param >
< param-name > forceEncoding </ param-name >
< param-value > true </ param-value >
</ init-param >
</ filter >
现在来看一下这三者分别的用途,
A 、 URIEncoding 的作用是什么呢?
解析请求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,这个方法把传过来的 URL 的 byte[] 设置到 org.apache.coyote.Request 的相应的属性中。这里的 URL 仍然是 byte 格式,转成 char 是在 org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的:
protected void convertURI(MessageBytes uri, Request request) throws Exception {
ByteChunk bc = uri.getByteChunk();
int length = bc.getLength();
CharChunk cc = uri.getCharChunk();
cc.allocate(length, -1);
String enc = connector.getURIEncoding();
if (enc != null ) {
B2CConverter conv = request.getURIConverter();
try {
if (conv == null ) {
conv = new B2CConverter(enc);
request.setURIConverter(conv);
}
} catch (IOException e){...}
if (conv != null ) {
try {
conv.convert(bc, cc, cc.getBuffer().length - cc.getEnd());
uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength());
return ;
} catch (IOException e) {...}
}
}
// Default encoding: fast conversion
byte [] bbuf = bc.getBuffer();
char [] cbuf = cc.getBuffer();
int start = bc.getStart();
for ( int i = 0; i < length; i++) {
cbuf [ i ] = ( char ) ( bbuf [ i + start ] & 0xff);
}
uri.setChars(cbuf, 0, length);
}
也就是说步骤是大致是这样的 ( 因为没仔细研究 tomcat 源码,所以顺序可能不会很正确 ) :
从上面的代码中可以知道对 URL 的 URI 部分 ( 也就是具体的请求资源部分不包括?后面的参数 ) 进行解码的字符集是在 connector 的 <Connector URIEncoding=”UTF-8”/> 中定义的,如果没有定义,那么将以默认编码 ISO-8859-1 解析。所以如果有中文 URL 时最好把 URIEncoding 设置成 UTF-8 编码。
QueryString 又如何解析? GET 方式 HTTP 请求的 QueryString 与 POST 方式 HTTP 请求的表单参数都是作为 Parameters 保存,都是通过 request.getParameter 获取参数值。对它们的解码是在 request.getParameter 方法第一次被调用时进行的。 request.getParameter 方法被调用时将会调用 org.apache.catalina.connector.Request 的 parseParameters 方法。这个方法将会对 GET 和 POST 方式传递的参数进行解码,但是它们的解码字符集有可能不一样。 POST 表单的解码将在后面介绍, QueryString 的解码字符集是在哪定义的呢?它本身是通过 HTTP 的 Header 传到服务端的,并且也在 URL 中,是否和 URI 的解码字符集一样呢?从前面浏览器对 PathInfo 和 QueryString 的编码采取不同的编码格式不同可以猜测到解码字符集肯定也不会是一致的。的确是这样 QueryString 的解码字符集要么是 Header 中 ContentType 中定义的 Charset 要么就是默认的 ISO-8859-1 ,要使用 ContentType 中定义的编码就要设 connector 的 <Connector URIEncoding=”UTF-8” useBodyEncodingForURI=”true”/> 中的 useBodyEncodingForURI 设置为 true 。这个配置项的名字有点让人产生混淆,它并不是对整个 URI 都采用 BodyEncoding 进行解码而仅仅是对 QueryString 使用 BodyEncoding 解码,这一点还要特别注意。
从上面的 URL 编码和解码过程来看,比较复杂,而且编码和解码并不是我们在应用程序中能完全控制的,所以在我们的应用程序中应该尽量避免在 URL 中使用非 ASCII 字符,不然很可能会碰到乱码问题,当然在我们的服务器端最好设置 <Connector/> 中的 URIEncoding 和 useBodyEncodingForURI 两个参数。
也就是说对请求资源的解码和对后面所带参数的解码采用的字符集可能是不同的, URIEncoding 的设置只会告诉服务器如何对请求资源解码,而不会告诉服务器如何对请求参数解码,配置了 useBodyEncodingForURI 则告诉服务器使用 bodyEncoding 进行解码。
根据以上分析,可以知道项目没有配置 URIEncoding 和 useBodyEncodingForURI 两个参数。则对请求资源以及请求参数都会采用默认的 ISO8859-1 ,进行解码。
不过要说一点,虽然这里如果进行了指定,但是由于不同浏览器对 URL 进行编码的方式不同,也会出现乱码,以下是分析 :
浏览器:
1
、
GET
方式提交,浏览器会对
URL
进行
URL encode
,然后发送给服务器。
(1)
对于中文
IE,
如果在高级选项中选中总以
UTF-8
发送
(
默认方式
)
,则
PathInfo
是
URL Encode
是按照
UTF-8
编码
,QueryString
是按照
GBK
编码。
http://localhost:8080/example/
中国
?name=
中国
实际上提交是:
GET /example/%E4%B8%AD%E5%9B%BD?name=%D6%D0%B9%FA
(1)
对于中文
IE,
如果在高级选项中取消总以
UTF-8
发送,则
PathInfo
和
QueryString
是
URL encode
按照
GBK
编码。
实际上提交是:
GET /example/%D6%D0%B9%FA?name=%D6%D0%B9%FA
(3)
对于中文
firefox
,则
pathInfo
和
queryString
都是
URL encode
按照
GBK
编码。
实际上提交是:
GET /example/%D6%D0%B9%FA?name=%D6%D0%B9%FA
很显然,不同的浏览器以及同一浏览器的不同设置,会影响最终 URL 中 PathInfo 的编码。对于中文的 IE 和 FIREFOX 都是采用 GBK 编码 QueryString 。
小结::
1
、
URL
中如果含有中文等非
ASCII
字符,则浏览器会对它们进行
URLEncode
。为了避免浏览器采用了我们不希望的编码,所以最好不要在
URL
中直接使用非
ASCII
字符,而采用
URL Encode
编码过的字符串
%.
比如:
URL
:
http://localhost:8080/example/
中国
?name=
中国
建议:
URL
:
http://localhost:8080/example/%D6%D0%B9%FA?name=%D6%D0%B9%FA
2 、我们建议 URL 中 PathInfo 和 QueryString 采用相同的编码,这样对服务器端处理的时候会更加简单。
由于我什么都没有设置,所以可以猜到,如果有中文路径或者是中文参数的 URL ,一定会出现中文乱码。假设我进行了设置,但是如果我在页面中没对中文路径和中文参数 URL 进行编码处理,那么由于不同浏览器的编码方式不同也会造成中文乱码问题,也就说终极是:所以最好不要在 URL 中直接使用非 ASCII 字符,而采用 URL Encode 编码过的字符串 %.
B 、 < constant name = "struts.i18n.encoding" value = "UTF-8" />
这个参数有什么作用呢?
关于这个参数有什么作用,网上也进行过讨论,参考这篇文档 http://cgl198617.iteye.com/blog/1066401 ,从这篇文档可以看出这个参数的设置对于解码和页面显示有很大的作用,对于请求阶段来说相当于执行了 HttpServletRequest 的 setCharacterEncoding 这个方法,但是对于这个方法,之前一直都没认真去理解,是不是设置了这个参数 url 中文乱码问题就解决了呢,答案是否定的, HttpServletRequest.setCharacterEncoding() 方法仅仅只适用于设置 post 提交的 request body 的编码而不是设置 get 方法提交的 queryString 的编码。该方法告诉应用服务器应该采用什么编码解析 post 传过来的内容。很多文章并没有说明这一点。看看 servlet 的 api 也可以知道这一点:
Overrides the name of the character encoding used in the body of this request 。
这个方法用来设置网页 body 的编码方式,可以在页面中设置,也可以在获取参数之前,通过 HttpSevletRequest 对象设置,现在来看 POST 提交:
POST 表单的编解码
在前面提到了 POST 表单提交的参数的解码是在第一次调用 request.getParameter 发生的, POST 表单参数传递方式与 QueryString 不同,它是通过 HTTP 的 BODY 传递到服务端的。当我们在页面上点击 submit 按钮时浏览器首先将根据 ContentType 的 Charset 编码格式对表单填的参数进行编码然后提交到服务器端,在服务器端同样也是用 ContentType 中字符集进行解码。所以通过 POST 表单提交的参数一般不会出现问题,而且这个字符集编码是我们自己设置的,可以通过 request.setCharacterEncoding(charset) 来设置。
另外针对 multipart/form-data 类型的参数,也就是上传的文件编码同样也是使用 ContentType( 这 里所说的 ContentType 是指 http 头的 ContentType ,而不是在网页中 meta 中的 ContentType ) 定义的字符集编码,值得注意的地方是上传文件是用字节流的方式传输到服务器的本地临时目录,这个过程并没有涉及到字符编码,而真正编码是在将文件内容添加 到 parameters 中,如果用这个编码不能编码时将会用默认编码 ISO-8859-1 来编码。
通过上面的分析我们可以知道, post 提交的编码解码跟服务器端的设置没有任何关系,而且也别妄想通过使用 request.setCharacterEncoding() 方法来解决 url 传递参数中文乱码的问题。
C 、 web.xml 中配置了解决中文编码问题的过滤器,我们查看源码发现这个:
if ( encoding != null && ( forceEncoding || request.getCharacterEncoding() == null ))
{
request.setCharacterEncoding( encoding );
if ( forceEncoding && responseSetCharacterEncodingAvailable )
response.setCharacterEncoding( encoding );
}
filterChain.doFilter(request, response);
看到这个方法没有:
request.setCharacterEncoding( encoding );
现在就知道为什么我配置了中文编码过滤器还是出错的原因了嘛,这个过滤器只会对 post 提交有效,这是防止你在页面中没有设置 ContentType 的 charset 而导致中文乱码。
经过上面的分析总结一下终极解决办法:
1、 在 Tomcat 中设置 URIEncoding 和 useBodyEncodingForURI 这两个参数。
2、 页面中或 js 中有中文路径或从参数,先进行编码,编码字符集和上面 tomcat 中配置的一样。