Java 在同一Dalvik/JVM中使用不同类加载器实例化的类不能;见;彼此

Java 在同一Dalvik/JVM中使用不同类加载器实例化的类不能;见;彼此,java,android,jar,classloader,dalvik,Java,Android,Jar,Classloader,Dalvik,我正在开发一个带有pluggable.jar模块的Android应用程序 我的问题是,当我加载两个不同的.jar文件时,第一个.jar文件中的类无法从第二个.jar文件中“看到”(Class.forName())类,反之亦然。我使用DexClassLoader从主应用程序加载外部.jars ==下面是一个示例情况=== 我们有两个模块: First.jar(包含类FirstA和FirstB,打包在classes.dex中) Second.jar(使用类SecondA,同样在classes.de

我正在开发一个带有pluggable.jar模块的Android应用程序

我的问题是,当我加载两个不同的.jar文件时,第一个.jar文件中的类无法从第二个.jar文件中“看到”(Class.forName())类,反之亦然。我使用DexClassLoader从主应用程序加载外部.jars

==下面是一个示例情况===

我们有两个模块:

  • First.jar(包含类FirstA和FirstB,打包在classes.dex中)
  • Second.jar(使用类SecondA,同样在classes.dex中)
通过使用DexClassLoader,我首先加载First.jar。一切都好。 然后再次使用DexClassLoader加载Second.jar,SecondA无法通过Class.forName()看到FirstA或FirstB。我得到java.lang.ClassNotFoundException。当我使用Class.forName()从SecondA检查FirstA时,我没有设置classloader参数

==示例情况结束===

我在想,当一个类加载到dalvik/jvm中时,它将从同一个dalvik/jvm中的所有其他类可见/可访问。不是这样,还是我完全错了? 为了让SecondA从First.jar(Class.forName())中查看类,我必须做什么

欢迎任何我可以学习的建议或资源! 我被卡住了,伙计们

编辑:

  • 所有模块都在运行时加载
  • 每个模块都由不同的DexClassLoader实例加载,因为我需要提供.jar文件的路径,并且我不能将所有模块打包在一个jar中

    • 这是标准的Java行为;为了使类A对类B可见,类A必须由类B的装入器或其父类之一装入。事实上,不同的类加载器可以具有名称相同的不同JVM类

      除非您有特定的理由不这样做,否则您通常应该对所有类/JAR使用相同的类加载器。如果确实有充分的理由,则需要确保加载程序之间具有适当的父/子关系


      Java类加载的语义模型位于中。Dalvik通常遵循语义,尽管我不太了解这些差异。

      这是标准的Java行为;为了使类A对类B可见,类A必须由类B的装入器或其父类之一装入。事实上,不同的类加载器可以具有名称相同的不同JVM类

      除非您有特定的理由不这样做,否则您通常应该对所有类/JAR使用相同的类加载器。如果确实有充分的理由,则需要确保加载程序之间具有适当的父/子关系


      Java类加载的语义模型位于中。Dalvik通常遵循语义,但我对这些差异并不了解。

      警告:非常难看的“解决方案”

      假设这两个模块加载了不同的DexClassLoader(在不同的场合,通过不同的线程等):

      • 模块1(模块1.jar,类为ClassModule1)
      • 模块2(带有ClassModule2类的模块2.jar)
      要使ClassModule1“查看”ClassModule2而不是执行此操作,请执行以下操作:

      Class class = Class.forName("com.example.app.ClassModule2", false, ClassModule1.class.getClassLoader());
      
      我们应该这样做:

      ClassLoader dexClassLoader = new DexClassLoader(
          manager.getPluginsFolder() + "Module-2.jar",
          this.context.getApplicationContext().getFilesDir().getAbsolutePath(), 
          null, 
          ClassModule1.class.getClassLoader()
      );
      
      Class class = Class.forName("com.example.app.ClassModule2", true, dexClassLoader);
      
      这很难看,因为有几件事:

      • 由于forName()上的第二个参数“true”,我们正在再次确定加载模块
      • 我们必须知道Module-2.jar的路径,这是不好的,因为模块是由ModuleManager管理的,而不是由模块本身管理的(职责分离)
      至少Dalvik(DexClassLoader)足够聪明,不会在加载之前优化.jar,而是使用由以前的DexClassLoader实例生成的缓存版本

      我想问题在于:“在运行时,类或接口不是由其名称决定的,而是由一对名称决定的:它的二进制名称(§4.2.1)及其定义类加载器。每个此类类或接口都属于一个运行时包。类或接口的运行时包由类或接口的包名和定义类加载器确定。”(参考:JVM规范第5章)


      感谢您的帮助。

      警告:非常难看的“解决方案”

      假设这两个模块加载了不同的DexClassLoader(在不同的场合,通过不同的线程等):

      • 模块1(模块1.jar,类为ClassModule1)
      • 模块2(带有ClassModule2类的模块2.jar)
      要使ClassModule1“查看”ClassModule2而不是执行此操作,请执行以下操作:

      Class class = Class.forName("com.example.app.ClassModule2", false, ClassModule1.class.getClassLoader());
      
      我们应该这样做:

      ClassLoader dexClassLoader = new DexClassLoader(
          manager.getPluginsFolder() + "Module-2.jar",
          this.context.getApplicationContext().getFilesDir().getAbsolutePath(), 
          null, 
          ClassModule1.class.getClassLoader()
      );
      
      Class class = Class.forName("com.example.app.ClassModule2", true, dexClassLoader);
      
      这很难看,因为有几件事:

      • 由于forName()上的第二个参数“true”,我们正在再次确定加载模块
      • 我们必须知道Module-2.jar的路径,这是不好的,因为模块是由ModuleManager管理的,而不是由模块本身管理的(职责分离)
      至少Dalvik(DexClassLoader)足够聪明,不会在加载之前优化.jar,而是使用由以前的DexClassLoader实例生成的缓存版本

      我想问题在于:“在运行时,类或接口不是由其名称决定的,而是由一对名称决定的:它的二进制名称(§4.2.1)及其定义类加载器。每个此类类或接口都属于一个运行时包。类或接口的运行时包由类或接口的包名和定义类加载器确定。”(参考:JVM规范第5章)

      感谢您的帮助。

      另一个想法

      创建一个数据结构,保存用于加载每个插件的类加载器(可能只是
      ArrayList
      )。不要调用
      Class.forName(“ClassIWant”)
      ,而是编写一个方法,迭代每个类加载器并调用
      ClassLoader\loadClass(“ClassIWant”)

      我假设您的代码负责加载每个插件,这意味着您有一个对