Search code examples
javaandroidserviceloader

ServiceLoader in Android "Couldn't instantiate class"


I have a problem with integrate the ServiceLoader to my Android app, to integrate a plugin system.

public class StrategieClassLoader extends URLClassLoader {

  private ClassLoader m_SystemClassLoader;

  private static Set<String> m_SaveClasses = new HashSet<String>();
  {
      m_SaveClasses.add( ESpielsteinFarbe.class.getName() );
      m_SaveClasses.add( ISpielstein.class.getName() );
      m_SaveClasses.add( SpielsteinPosition.class.getName() );

      m_SaveClasses.add( ISpielzug.class.getName() );
      m_SaveClasses.add( IStrategie.class.getName() );
      m_SaveClasses.add( IStrategieFactory.class.getName() );
      m_SaveClasses.add( StrategieException.class.getName() );
  }

  public StrategieClassLoader(URL[] p_Classpath, ClassLoader p_Parent) {
    super(p_Classpath, p_Parent);
    m_SystemClassLoader = getSystemClassLoader();
  }

  @Override
  protected synchronized Class<?> loadClass(String p_ClassName, boolean p_Resolve)
      throws ClassNotFoundException {

    Class<?> l_Class = findLoadedClass(p_ClassName);
    if (l_Class == null) {
      if (isSystemClass(p_ClassName)) {
        l_Class = super.loadClass(p_ClassName, p_Resolve);
      } else {
        try {
          l_Class = findClass(p_ClassName);
        } catch (ClassNotFoundException e) {
          l_Class = super.loadClass(p_ClassName, p_Resolve);
        }
      }
    }
    if (p_Resolve) {
      resolveClass(l_Class);
    }
    return l_Class;
  }

  private boolean isSystemClass(String p_ClassName) {
    return ( m_SaveClasses.contains( p_ClassName ) || p_ClassName.startsWith("java.") || p_ClassName.startsWith("javax.") ||
           p_ClassName.startsWith("com.sun."));
  }

  @Override
  public URL getResource(String p_ResourceName) {
    URL l_ResourceUrl = findResource(p_ResourceName);
    if (l_ResourceUrl == null) {
      l_ResourceUrl = super.getResource(p_ResourceName);
    }
    return l_ResourceUrl;
  }

  @Override
  public Enumeration<URL> getResources(String p_ResourceName) throws IOException {
    Enumeration<URL> l_LocalUrls = findResources(p_ResourceName);
    Enumeration<URL> l_ParentUrls = null;
    if (getParent() != null) {
      l_ParentUrls = getParent().getResources(p_ResourceName);
    }
    final List<URL> l_Urls = new ArrayList<URL>();
    if (l_LocalUrls != null) {
      while (l_LocalUrls.hasMoreElements()) {
        l_Urls.add(l_LocalUrls.nextElement());
      }
    }
    if (l_ParentUrls != null) {
      while (l_ParentUrls.hasMoreElements()) {
        l_Urls.add(l_ParentUrls.nextElement());
      }
    }
    return new Enumeration<URL>() {
      Iterator<URL> l_UrlIterator = l_Urls.iterator();

      public boolean hasMoreElements() {
        return l_UrlIterator.hasNext();
      }

      public URL nextElement() {
        return l_UrlIterator.next();
      }
    };
  }

  @Override
  public InputStream getResourceAsStream(String p_ResourceName) {
    URL l_ResourceUrl = getResource(p_ResourceName);
    try {
      return l_ResourceUrl != null ? l_ResourceUrl.openStream() : null;
    } catch (IOException e) {
    }
    return null;
  }
}

public static List<IStrategieFactory> load(String p_path)
{
    List<IStrategieFactory> l_Facs = new ArrayList<IStrategieFactory>();

    File l_PluginDir = new File(p_path);

    for( File dir: l_PluginDir.listFiles() )
    {
        if( dir.isDirectory() )
        {
            File[] l_Jars = dir.listFiles( new FilenameFilter() {

                @Override
                public boolean accept(File file, String name) {
                    // TODO Auto-generated method stub
                    return name.endsWith( ".jar" );
                }
            });

            List<URL> l_Urls = new ArrayList<URL>();
            for( File jar: l_Jars )
            {
                try {
                    l_Urls.add( jar.toURI().toURL() );
                } catch (MalformedURLException ex) {
                    ex.printStackTrace();
                }
            }

            if( !l_Urls.isEmpty() )
            {
                StrategieClassLoader l_ClassLoader = new StrategieClassLoader(l_Urls.toArray( new URL[ l_Urls.size() ]), Thread.currentThread().getContextClassLoader() );

                ServiceLoader<IStrategieFactory> loader = ServiceLoader.load(IStrategieFactory.class, l_ClassLoader);
                for( IStrategieFactory fac : loader )
                {
                    l_Facs.add( fac );
                }
            }
        }
    }   

    m_Factories = l_Facs;

    return m_Factories; 
    }   
}

The .jar Files copied from the assets folder on the externalStorage, the Class can access to all files but it's crashes after run

ServiceLoader<IStrategieFactory> loader = ServiceLoader.load(IStrategieFactory.class, l_ClassLoader);

13967-13967/dievierlustigen5.dhbw.de.a4wins E/AndroidRuntime: FATAL EXCEPTION: main
 Process: dievierlustigen5.dhbw.de.a4wins, PID: 13967
 java.util.ServiceConfigurationError: Couldn't instantiate class de.dhbw.fourwins.strategy.EasyStratFactory
     at java.util.ServiceLoader$ServiceIterator.next(ServiceLoader.java:216)
     at dievierlustigen5.dhbw.de.a4wins.strategy.StrategyLoader.load(StrategyLoader.java:78)
     at dievierlustigen5.dhbw.de.a4wins.DifficultyActivity.onCreate(DifficultyActivity.java:43)
     at android.app.Activity.performCreate(Activity.java:6877)
     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1136)
     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3208)
     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3351)
     at android.app.ActivityThread.access$1100(ActivityThread.java:222)
     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1796)
     at android.os.Handler.dispatchMessage(Handler.java:102)
     at android.os.Looper.loop(Looper.java:158)
     at android.app.ActivityThread.main(ActivityThread.java:7230)
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
  Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
     at java.net.URLClassLoader.getPermissions(URLClassLoader.java:614)
     at java.security.SecureClassLoader.getPD(SecureClassLoader.java:140)
     at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:93)
     at java.net.URLClassLoader.access$600(URLClassLoader.java:55)
     at java.net.URLClassLoader$URLJarHandler.createClass(URLClassLoader.java:364)
     at java.net.URLClassLoader$URLJarHandler.findClass(URLClassLoader.java:303)
     at java.net.URLClassLoader.findClass(URLClassLoader.java:748)
     at dievierlustigen5.dhbw.de.a4wins.strategy.StrategieClassLoader.loadClass(StrategieClassLoader.java:56)
     at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
     at java.util.ServiceLoader$ServiceIterator.next(ServiceLoader.java:214)
     at dievierlustigen5.dhbw.de.a4wins.strategy.StrategyLoader.load(StrategyLoader.java:78) 
     at dievierlustigen5.dhbw.de.a4wins.DifficultyActivity.onCreate(DifficultyActivity.java:43) 
     at android.app.Activity.performCreate(Activity.java:6877) 
     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1136) 
     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3208) 
     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3351) 
     at android.app.ActivityThread.access$1100(ActivityThread.java:222) 
     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1796) 
     at android.os.Handler.dispatchMessage(Handler.java:102) 
     at android.os.Looper.loop(Looper.java:158) 
     at android.app.ActivityThread.main(ActivityThread.java:7230) 
     at java.lang.reflect.Method.invoke(Native Method) 
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) 
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) 

Maybe anyone know how to solve the Error?


Solution

  • It's no possible to load normal .class files during runtime with Android, because Android use an other VM, which can only read .dex files.

    So I have to use the DexClassLoader