方案一:在android下搭建linux运行环境,然后在linux下运行java web服务器。

方案二:将tomcat或jetty等java web服务器的源码改造,并将class文件转译成android能直接运行的dex格式文件(运行在Dalvik VM上)。

 

    注意,方案二,自己去改造服务器源码,对于一般人显然不现实,但是jetty官方提供了一个叫“i-jetty”的项目,可以直接在android下运行jetty服务器,然后安装dex转码过的war包。

 

本文目前主要对方案二进行讲解。

 

    方案二,有一个很大的缺点,虽然class文件可以转换成dex格式文件运行,而且java反射也可以使用

(因为android下为了使dex能运行,它重写了System ClassLoader,名字叫dalvik.system.DexClassLoader和PathClassLoader),

    但是,java classpath失效了,因为Dalvik VM根本不使用class。而且class打包成dex文件时,不会把classpath下面的资源文件也打包到dex文件中。

    所以,整个System ClassLoader无法获取任何class文件和资源文件。

    如果你的程序或者程序依赖的类库jar包中的代码,有用到classpath及资源文件加载的地方,都会失效,必须得重写这些代码的实现方式。

 

    举个例子,你把spring的application.properties文件放在classpath下面,肯定是无法使用的,甚至spring框架所依赖的Bean初始化,它会去classpath下面寻找配置的package下面的所有class文件,然后加载class文件中并读取里面的注解等,由于没有class了,所有它找不到class文件,而读取Android的dex文件需要特殊的实现,Spring等几乎所有的传统java框架或库,都没有这个功能。

    附:Spring扫描package下class文件的源码如下:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
try {
String packageSearchPath = "classpath*:" + 
resolveBasePackage(basePackage) + "/**/*.class";
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
for (Resource resource : resources) {
logger.trace("Scanning " + resource);
...
}
// 代码来源:
// org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider

    附:AndroidClassLoader源码解析,描述了从dex文件中查找资源的过程,涉及核心源码包括BaseDexClassLoader,DexPathList,DexFile

参见:

 

    值得一提的是,dex文件,是一个纯二进制文件,通常只包含class转换过的内容。你可以把dex文件当做一个map,可以通过标准的class name(比如com.zollty.test.Hello)来读取其二进制内容(类似于字节码),但是你不能通过package去寻找某个package下面有哪些类(它和传统的压缩包不同,传统的压缩包,比如zip,可以通过斜杆(/)分割的路径寻找里面的子文件,但是dex文件貌似不行)。我想从 dex文件查询  com.zollty 包下面的所有类,但是找不到方法。

 

    另外,注意,i-jetty 这个项目,官方没有怎么维护,实际使用起来问题很多,我也是花了整整2天时间,把i-jetty的源码,以及Android各个版本DexClassloader相关的源码,都分析阅读了之后,才把项目弄好。

    我重写了i-jetty的AndroidClassLoder,让它可以读取jar包中的资源文件,同时配合 maven  ant plugin自定义打包,将必要的java class和资源文件打包到 i-jetty的lib jar包中,同时配合我专用的zollty-mvc框架(能替代SpringMVC的绝大多数常用功能),以及android下专用的jdbc驱动,才把一个完整的项目跑起来,这里面的每个环节都不能少。

 

    源码已传到GitHub上:https://github.com/zollty/i-jetty

    如果不想直接写原生Servlet,强烈推荐配合“超轻量级MVC框架-ZolltyMVC”使用:https://github.com/zollty-org/zollty-mvc

    ZolltyMVC的代码量连SpringMVC的1%不到,但是却具备SpringMVC的80%常用功能。

 

附:i-jetty使用说明

1、将项目webapp文件夹复制到如下的webapps文件夹下:

/storage/emulated/0/jetty/webapps

    项目webapp文件夹包含如下内容:

1)静态文件;

2)WEB-INF文件夹,下面有一个web.xml和lib\classes.zip

其中classes.zip是classpath下面所有文件的(class文件转译成android的dex文件后)打包而成。

classes.zip是在maven工程中,直接用maven插件打包生成。

    该Maven插件配置代码如下:

<build>
<plugins>
 
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>unpack-dependencies</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
<excludeArtifactIds>servlet-api,android,i-jetty-server,jetty-util</excludeArtifactIds>
<excludeTransitive>true</excludeTransitive>
<outputDirectory>${project.build.directory}/generated-classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
 
 
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>copydex</id>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<mkdir
dir="${project.build.directory}/${project.artifactId}/WEB-INF/lib" />
<mkdir
dir="${project.build.directory}/dex-classes" />
<copy includeEmptyDirs="false" todir="${project.build.directory}/dex-classes">
<fileset dir="${project.build.directory}/generated-classes"
includes="**/*.class" />
<fileset dir="${project.build.directory}/classes"
includes="**/*.class" />
</copy>
 
<java jar="${basedir}/dx.jar" fork="true" failonerror="true">
<arg value="--dex" />
<arg value="--verbose" />
<arg value="--core-library" />
<arg value="--output=${project.build.directory}/classes/classes.dex" />
<arg value="--positions=lines" />
<arg value="${project.build.directory}/dex-classes" />
</java>
 
<copy
file="${basedir}/src/main/webapp/WEB-INF/web.xml"
todir="${project.build.directory}/${project.artifactId}/WEB-INF" />
 
<move includeEmptyDirs="false" todir="${project.build.directory}/classes">
<fileset dir="${project.build.directory}/generated-classes"
excludes="**/*.class" />
<fileset dir="${project.build.directory}/generated-classes"
includes="com/zollty/oa/**/*.class" />
</move>
 
<jar
basedir="${project.build.directory}/classes"
update="true"
strict="warn"
excludes="META-INF/maven/**,META-INF/*.SF,META-INF/*.DSA,META-INF/*.RSA"
destfile="D:/__SYNC/00-MOBILE0/classes.zip" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
 
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<versionRange>[2.3,)</versionRange>
<goals>
<goal>unpack-dependencies</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<versionRange>[1.3,)</versionRange>
<goals>
<goal>run</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

 

2、ijetty还支持native lib,在如下目录

/storage/emulated/0/jetty/lib

    放置native的so库文件即可,例如我放置了 libsqlitejdbc.so 文件。