Using Serviceloader on Android

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



Leave a reply



Submit