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?
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