Skip to content

Instantly share code, notes, and snippets.

@DanielNovak
Last active April 23, 2024 18:12
Show Gist options
  • Save DanielNovak/06bc27fa4ecea63207c424bef88199df to your computer and use it in GitHub Desktop.
Save DanielNovak/06bc27fa4ecea63207c424bef88199df to your computer and use it in GitHub Desktop.
Android Service Locator implementation
package eu.inloop.servicelocator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings({"unused", "WeakerAccess"})
public class SL {
/**
* All Services provided by the Service Locator have to implement this interface.
* This is just a "marker interface pattern", it can be replaced by an annotation later.
*/
public interface IService {
}
private static final Map<String, Object> sServicesInstances = new HashMap<>();
private static final Map<String, Class> sServicesImplementationsMapping = new HashMap<>();
@SuppressLint("StaticFieldLeak")
private static Context mContext;
private static final Object sServicesInstancesLock = new Object();
public static void init(@NonNull Context context) {
mContext = context.getApplicationContext();
}
/**
* Return instance of desired class or object that implement desired interface.
*/
@SuppressWarnings({"unchecked"})
public static <T> T get(@NonNull Class<T> clazz) {
@SuppressWarnings("ResourceType") T instance = (T) getService(clazz.getName(), mContext);
return instance;
}
/**
* This method allows to bind a custom service implementation to an interface.
*
* @param interfaceClass interface
* @param implementationClass class which implement interface specified in first param
*/
public static void bindCustomServiceImplementation(@NonNull Class interfaceClass, @NonNull Class implementationClass) {
synchronized (sServicesInstancesLock) {
sServicesImplementationsMapping.put(interfaceClass.getName(), implementationClass);
}
}
@NonNull
private static Object getService(@NonNull String name, @Nullable Context applicationContext) {
synchronized (sServicesInstancesLock) {
Object o = sServicesInstances.get(name);
if (o != null) {
return o;
} else {
try {
Object serviceInstance;
final Class<?> clazz;
if (sServicesImplementationsMapping.containsKey(name)) {
clazz = sServicesImplementationsMapping.get(name);
} else {
clazz = Class.forName(name);
}
try {
Constructor e1 = clazz.getConstructor(Context.class);
serviceInstance = e1.newInstance(applicationContext);
} catch (NoSuchMethodException var6) {
Constructor constructor = clazz.getConstructor();
serviceInstance = constructor.newInstance();
}
if (!(serviceInstance instanceof SL.IService)) {
throw new IllegalArgumentException("Requested service must implement IService interface");
}
sServicesInstances.put(name, serviceInstance);
return serviceInstance;
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Requested service class was not found: " + name, e);
} catch (Exception e) {
throw new IllegalArgumentException("Cannot initialize requested service: " + name, e);
}
}
}
}
public interface Creator<T> {
T newInstance(@NonNull Context context);
}
}
@moon-sky
Copy link

bindCustomServiceImplementation (@NonNull Class interfaceClass, @NonNull Class implementationClass)
if many classes implement IService,sServicesImplementationsMapping only store the last services ,is that right?

@amazingvoice
Copy link

public interface Creator<T> { T newInstance(@NonNull Context context); }

May I know what is this?

https://medium.com/inloopx/service-locator-pattern-in-android-af3830924c69
As in the article, I guess this simply declares that only classes with empty or single Context parameter constructors are allowed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment