方案一:在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
参见:https://blog.csdn.net/qq_15274383/article/details/73306332
值得一提的是,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 文件。