ServiceLoader in Android Couldn't instantiate class
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
Hyperion-Android: ServiceLoader can’t load custom Plugin
it looks like the problem is related to the processing of @AutoService
annotation. Make sure that you have added an annotation processor to the project.
You should add the following annotation processor to the build.gradle
file.
for Kotlin project:
kapt 'com.google.auto.service:auto-service:1.0-rc6'
for Java project:
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
Using ServiceLoader to implement a JRE service, but without modules
The way ServiceLoader
loads classes on the classpath hasn't changed1. It still locates providers by searching for appropriate provider-configuration files under META-INF/services
.
The documentation of LoggerFinder
says it searches for providers visible to the system class loader. As you mention in a comment the provider is included via -cp
this should not be an issue.
The provider-configuration file's name must be the fully qualified binary name of the SPI. From the ServiceLoader
documentation:
Deploying service providers on the class path
A service provider that is packaged as a JAR file for the class path is identified by placing a provider-configuration file in the resource directory
META-INF/services
. The name of the provider-configuration file is the fully qualified binary name of the service. The provider-configuration file contains a list of fully qualified binary names of service providers, one per line.
The binary name of LoggerFinder
, as returned by Class.getName
, is java.lang.System$LoggerFinder
. Based on that, the provider-configuration file's name should be:
META-INF/services/java.lang.System$LoggerFinder
I'm not at all familiar with Ant, but I'd guess you should change the value of type
to use the fully qualified binary name.
<service type="java.lang.System$LoggerFinder">
<provider classname="com.example.for.stackoverflow.LoggerFinder"/>
</service>
Note: When testing this on my own, I initially couldn't get the system to use my LoggerFinder
implementation either. When I changed the provider-configuration file name to java.lang.System$LoggerFinder
(once finally reading the docs) then it worked as expected. Unfortunately, I don't have Ant available to test a solution with the <jar>
task.
1. Unless you use ServiceLoader.load(ModuleLayer,Class)
as it will ignore the unnamed modules (i.e. classpath).
ServiceLoader to find implementations of an interface
ServiceLoader
cannot do it.
In order to expose class as a service that can be discovered by ServiceLoader
you need to put its name into provider configuration file, as described in Creating Extensible Applications With the Java Platform
.
There are no built-in ways find all classes that implement a particular interface. Frameworks that can do something similar use their own classpath scanning solutions (and even with custom classpath scanning it's not easy because .class
files only store information about interfaces implemented directly, not transitively).
Keep 'META-INF/services'-files in apk
ANT
I'm using the following solution now:
I created a custom_rules.xml with the followin targets to copy files in META-INF/services into the unaligned and unsigned apk.
<target name="-post-package" depends="-custom-copy" />
<target name="-copy-custom">
<zip destfile="${out.packaged.file}"
update="true"
basedir="${source.absolute.dir}"
includes="${custom.copy}" />
</target>
And in ant.properties I added the line
custom.copy=META-INF/services/**
Now I just have to copy relevant files from libraries to the META-INF/services-folder of my own project to include them in the apk. This gives me full control over which classes to be loaded by ServiceLoader.
Remark:
Currently I only load implementations that are included in external Java-SE-jars this way. Obfuscation may have to be configured if the implementations are in an android-project.
ECLIPSE-PLUGIN
Using the eclipse-plugin, there's no workaround like in ANT. The problem is the ExportHelper (line 405) which just igores everything in META-INF
folders.
Android-Studio/GRADLE
According to this Bug you may define a META-INF
-directory in your main project and this will be packaged into your apk. META-INF
-folders of libraries are excluded, so that you are forced to specify the implementations you want to use in your own project (I think this is the intention of it).
Related Topics
Can't Run Junit 4 Test Case in Eclipse Android Project
How to Include the Spongy Castle Jar in Android
Wrong Version of Keystore on Android Call
Difference Between Google() and Maven { Url 'Https://Maven.Google.Com' }
How to Persist Permission in Android API 19 (Kitkat)
Runtime.Exec():Reboot in Android
Open Gallery App from Android Intent
How to Get String Response from Retrofit2
Android: How to Use Download Manager Class
Transformexception Duplicate Entry for Common.Annotations.Beta
Polygon Touch Detection Google Map API V2
Android Resource Not Found Exception
How to Resume Android Activity Programmatically from Background
Unhandled Exception Type Error
:App:Dexdebug Execexception Finished with Non-Zero Exit Value 2
Google Sign in Signed APK Not Working