Another approach can also be found in the Android Architecture libraries:
As the Shared Preferences depends on a context, it solely should know about it. To have things in one place, I choose a Singleton to manage this. It consists of two classes: the Manager (i.e. the SharePreferenceManager or ServiceManager or whatever), and an initializer which injects the Context.
class ServiceManager {
private static final ServiceManager instance = new ServiceManager();
// Avoid mem leak when referencing context within singletons
private WeakReference<Context> context
private ServiceManager() {}
public static ServiceManager getInstance() { return instance; }
static void attach(Context context) { instance.context = new WeakReference(context); }
... your code...
}
The initializer is basically an empty Provider
(https://developer.android.com/guide/topics/providers/content-providers.html), which is registered in the AndroidManifest.xml
and loaded when the app starts:
public class ServiceManagerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
ServiceManager.init(getContext());
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
All function are default implementations except the onCreate, which injects the required context into our manager.
Last step to get this working is to register the provider in the manifest:
<provider
android:authorities="com.example.service-trojan"
android:name=".interactor.impl.ServiceManagerInitializer"
android:exported="false" />
This way, your service manager is decoupled from any external context initialization. It now can be completely replaced with another implementation which is context-independent.