Android下运行Tomcat、Jetty等Java Web服务器


方案一:在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 文件。


© 2009-2020 Zollty.com 版权所有。渝ICP备20008982号