How to Inject a SharedPreferences Class as DataManager into app? - java

I'm getting this error while i'm trying to Build/Rebuild :
error: ir.atlaspio.roshdbookstore.DI.Components.ApplicationComponent scoped with #ir.atlaspio.roshdbookstore.DI.Scopes.ApplicationScope may not reference bindings with different scopes:
#Singleton class ir.atlaspio.roshdbookstore.Data.Prefs.AtlasPreferencesHelper
I'm trying to improve my code base on some projects and tutorials, right now i'm trying to doing dependency injection for SharedPreferences, which i've got above problem. It's not long time since i'm messing with Dagger 2 And DI, so excuse my beginner question and help me about this to find out where i'm doing this wrong. also there are some codes which they will inject SharedPrefereces but what i want is to use My own DataManager to do it right.
So here's how i created my project(Which is base on some tutorials):
Explaining Section(Which could be obvious for you guys): adding some method to set data into sharedpref.
AtlasPreferencesHelper :
#Singleton
public class AtlasPreferencesHelper implements PreferencesHelper {
private static final String PREF_KEY_USER_LOGGED_IN_MODE = "PREF_KEY_USER_LOGGED_IN_MODE";
private static final String PREF_KEY_FIRST_TIME_RUN_MODE = "PREF_KEY_FIRST_TIME_RUN_MODE";
private final SharedPreferences atlasPrefs;
#Inject
public AtlasPreferencesHelper(#ApplicationContext Context context,
#PreferenceInfo String prefFileName) {
atlasPrefs = context.getSharedPreferences(prefFileName, Context.MODE_PRIVATE);
}
#Override
public int getCurrentUserLoggedInMode() {
return atlasPrefs.getInt(PREF_KEY_USER_LOGGED_IN_MODE,
DataManager.LoggedInMode.LOGGED_IN_MODE_LOGGED_OUT.getType());
}
#Override
public void setCurrentUserLoggedInMode(DataManager.LoggedInMode mode) {
atlasPrefs.edit().putInt(PREF_KEY_USER_LOGGED_IN_MODE, mode.getType()).apply();
}
#Override
public int getCurrentFirstTimeStat() {
return atlasPrefs.getInt(PREF_KEY_FIRST_TIME_RUN_MODE,
DataManager.FirstTimeRun.FIRST_TIME_RUN_TRUE.getFrType());
}
#Override
public void setCurrentFirstTimeStat(DataManager.FirstTimeRun fMode) {
atlasPrefs.edit().putInt(PREF_KEY_FIRST_TIME_RUN_MODE, fMode.getFrType()).apply();
}
}
Explains : interface to be implemented in AtlasPreferencesHelper.
PreferenceHelper :
public interface PreferencesHelper {
int getCurrentUserLoggedInMode();
void setCurrentUserLoggedInMode(DataManager.LoggedInMode mode);
int getCurrentFirstTimeStat();
void setCurrentFirstTimeStat(DataManager.FirstTimeRun fMode);
}
Explains: Doing Job and getting access to activity to use methods.
AppDataManager :
public class AppDataManager implements DataManager {
private static final String TAG = "AppDataManager";
private final Context mContext;
private final PreferencesHelper mPrefencesHelper;
#Inject
public AppDataManager(#ApplicationContext Context context, PreferencesHelper prefencesHelper) {
mContext = context;
mPrefencesHelper = prefencesHelper;
}
#Override
public void setUserAssLoggedOut() {
setCurrentUserLoggedInMode(LoggedInMode.LOGGED_IN_MODE_LOGGED_OUT);
}
#Override
public int getCurrentUserLoggedInMode() {
return mPrefencesHelper.getCurrentUserLoggedInMode();
}
#Override
public void setCurrentUserLoggedInMode(LoggedInMode mode) {
mPrefencesHelper.setCurrentUserLoggedInMode(mode);
}
#Override
public int getCurrentFirstTimeStat() {
return mPrefencesHelper.getCurrentFirstTimeStat();
}
#Override
public void setCurrentFirstTimeStat(FirstTimeRun fMode) {
mPrefencesHelper.setCurrentFirstTimeStat(fMode);
}
}
Explains : DataManager interface implemented to AppDataManager
DataManager :
public interface DataManager extends PreferencesHelper {
void setUserAssLoggedOut();
enum LoggedInMode {
LOGGED_IN_MODE_LOGGED_OUT(0),
LOGGED_IN_MODE_SERVER(1);
private final int mType;
LoggedInMode(int type)
{
mType = type;
}
public int getType()
{
return mType;
}
}
enum FirstTimeRun {
FIRST_TIME_RUN_FALSE(0),
FIRST_TIME_RUN_TRUE(1);
private final int frType;
FirstTimeRun(int rType){
frType = rType;
}
public int getFrType()
{
return frType;
}
}
}
Explains : Components which, Context provide context, Retrofit Provide retrofit and SharedPref will provide Datamanager and SharedPref
ApplicationComponent :
#ApplicationScope
#Component(modules = {ContextModule.class,
RetrofitModule.class,
SharedPrefModule.class})
public interface ApplicationComponent {
MyAtlasAPI getApiReference();
DataManager getDataManager();
#ApplicationContext
Context getContext();
void injectApplication(AtlasApplication atlasApplication);
}
Explains : Module that will be implement in component for datamanager and sharedpref
SharedPrefModule :
#Module
public class SharedPrefModule {
#Provides
#PreferenceInfo
String providePreferenceName() {
return AppConstants.PREF_NAME;
}
#Provides
#Singleton
DataManager provideDataManager(AppDataManager appDataManager)
{
return appDataManager;
}
#Provides
#Singleton
PreferencesHelper providePreferencesHelper(AtlasPreferencesHelper atlasPreferencesHelper)
{
return atlasPreferencesHelper;
}
}

The error you're getting is telling it all. You're trying to reference to a module with a different scope than your component.
Looking at you ApplicationComponent:
#ApplicationScope
#Component(modules = {ContextModule.class,
RetrofitModule.class,
SharedPrefModule.class})
public interface ApplicationComponent { }
It's using the ApplicationScope
Your ApplicationComponent is then referring to SharedPrefModule.
If we take a look at that class:
#Module
public class SharedPrefModule {
#Provides
#Singleton
PreferencesHelper providePreferencesHelper(AtlasPreferencesHelper atlasPreferencesHelper) {
return atlasPreferencesHelper;
}
}
This module is using the Singleton scope.
Finally your AtlasPreferencesHelper is also scoped with Singleton:
#Singleton
public class AtlasPreferencesHelper {}
What you wanna do is align these scopes.
So either annotate the ApplicationComponent with #Singleton or use the #Application for all the modules within your ApplicationComponent

Related

How to get a singleton injection in a class with Dagger in android

There is the DataAccessInterface class that is in charge of managing the database:
public class DataAccessInterface {
private DaoSession daoSession;
public DataAccessInterface () {
}
...
public void saveCar (Car car) {
daoSession.getCarDao (). insert (car);
}
}
DataAccessInterface injection is used in several Fragments with success. Example:
public class LoginFragment extends BaseFragment {
#Inject
DataAccessInterface dataAccessInterface;
...
public boolean initDatabase () throws SyncDataBaseException {
try {
dataAccessInterface.openSession (currentUser.getUsername ());
} catch (Exception e) {
throw new SyncDataBaseException ();
}
return true;
}
...
}
There is a BackendImp class (No Fragment or Activity) that in the background queries a rest service and saves the response in the database. The injection does not work, it is always null:
public class BackendImp {
#Inject
DataAccessInterface dataAccessInterface;
public void save () {
Car car = unloadCar ()
dataAccessInterface.saveCar (car);
}
The AbstractActivityComponent looks like this
#PerActivity
#Component (dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface AbstractActivityComponent {
Activity activity ();
final class Initializer {
public static AbstractActivityComponent init (Activity activity) {
return DaggerAbstractActivityComponent.builder ()
.applicationComponent (DaggerManager.getInstance (). appComponent ())
.activityModule (new ActivityModule (activity))
.build ();
}
}
void inject (LoginFragment inject);
void inject (BackendImp inject);
}
ApplicationModule:
#Module
public class ApplicationModule {
private final Application application;
private final User currentUser;
public ApplicationModule (Application application) {
this.application = application;
this.currentUser = getUser ();
}
#Provides
#Singleton
DataAccessInterface dataAccessInterface () {
return new DataAccessInterface (userProfile ());
}
}
And ApplicationComponent
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(Application application);
final class Initializer {
public static ApplicationComponent init(Application app) {
return DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(app))
.build();
}
}
Application application();
Context context();
DataAccessInterface dataAccessInterface();
}
Error:
W/System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.service.DataAccessInterface.saveCar(Car)' on a null object reference
Edit:
Based on Nitrodon's question in the comments:
The BackendImp functions are called from a Worker, since they will be done every hour. I wanted a single instance so I did the following which is probably wrong:
public class MainApp extends Application {
public static BackendService backendService;
#Override
public void onCreate () {
super.onCreate ();
backendService = new BackendImp ();
}
public static void callWorker () {
...
workManager.enqueue (updateWorkRequest);
}
And the Worker:
public class updateWorker extends Worker {
...
#Override
public Result doWork () {
Result result = Result.retry ();
try {
backend = MainApp.backendService;
backend.save ();
result = Result.success ();
} catch (Exception e) {
result = Result.retry ();
}
return result;
}
Dagger doesn't hook into constructor calls. You have to tell Dagger to inject dependencies into BackendImp.
You have an inject(BackendImp inject) method in your activity component. This would work, but it's in the wrong place, so your application class can't access it. Putting this method in the application component would work:
#Override
public void onCreate () {
super.onCreate ();
backendService = new BackendImp();
// I assume you created the application component somewhere in here.
component.inject(backendService);
}
However, members injection methods are generally discouraged where they can be avoided. There's no other choice in Activity subclasses, since they are instantiated by the framework, but something like BackendImp is entirely under your control, so you can and should let Dagger create it for you.
To do this, place BackendImp itself in the application component by giving it the #Singleton scope and an #Inject constructor:
#Singleton
public class BackendImp {
final DataAccessInterface dataAccessInterface;
#Inject
BackendImp(DataAccessInterface dataAccessInterface) {
this.dataAccessInterface = dataAccessInterface;
}
// ...
}
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
// ...
// Use this in your application instead of new BackendImp()
BackendImp backendImp();
// Many tutorials instead suggest making an #Inject field in
// your application and calling inject(application).
}

Dagger2: how to inject retrofit module in presenter class

Dagger2 learning is still difficult and am trying to learn. Have setup a project using new dagger android to avoid injecting inside activity class. So far it's working but need to use retrofit injected in presenter class. Added retrofit module in AppComponent but apiService class method getting null when call from presenter class. Need to know how to inject properly.
AppComponent.java
#Singleton
#Component(modules = {
/* Use AndroidInjectionModule.class if you're not using support library */
AndroidSupportInjectionModule.class,
AppModule.class,
BuildersModule.class,
RetrofitModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(BaseApplication application);
Builder retrofitModule(RetrofitModule retrofitModule);
AppComponent build();
}
void inject(BaseApplication app);
}
AppModule.java
#Module
class AppModule {
#Provides
Context provideContext(BaseApplication application) {
return application.getApplicationContext();
}
}
BuildersModule.java
#Module
public abstract class BuildersModule {
#ContributesAndroidInjector(modules = {SplashViewModule.class, SplashModule.class})
abstract SplashActivity bindSplashActivity();
// Add bindings for other sub-components here
}
BaseApplication.java
public class BaseApplication extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
//configure timber for logging
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
DaggerAppComponent
.builder()
.application(this)
.retrofitModule(new RetrofitModule())
.build()
.inject(this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
RetrofitModule.java
#Module
public class RetrofitModule {
//get retrofit instance
#Singleton
#Provides
public UserAuthService getRestService() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ApiConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(getOkHttpClient())
.build();
return retrofit.create(UserAuthService.class);
}
}
UserAuthService.java
public interface UserAuthService {
#GET("v2/5be345062f00006b00ca22c4")
Observable<Example> getExampleResponse();
}
SplashModule.java
#Module
public class SplashModule {
#Provides
SplashPresenter provideSplashPresenter(SplashView splashView) {
return new SplashPresenter(splashView);
}
}
SplashViewModule.java
#Module
public abstract class SplashViewModule {
#Binds
abstract SplashView provideSplashView(SplashActivity splashActivity);
}
SplashPresenter.java
class SplashPresenter extends BasePresenter<SplashView> {
#Inject
#Named(ValueConstants.MAIN_THREAD)
Scheduler mMainThread;
#Inject
#Named(ValueConstants.NEW_THREAD)
Scheduler mNewThread;
#Inject
UserAuthService userAuthService;
SplashPresenter(SplashView view) {
super(view);
}
//test purpose
public void sayHello() {
userAuthService.getExampleResponse()
.observeOn(mMainThread)
.subscribeOn(mNewThread)
.subscribe(new Observer<Example>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Example example) {
Timber.d("example %s", example.toString());
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
});
}
}
here "userAuthService.getExampleResponse()" getting null. I think presenter need to know about RetrofitModule injection. So I need to fix this and how?
I would add those dependencies to the constructor of SplashPresenter and add the #Inject annotation to it. Dagger2 supports constructor injection and all the dependencies you need are resolvable (and you can also get rid of SplashModule)
class SplashPresenter extends BasePresenter<SplashView> {
private Scheduler mMainThread;
private Scheduler mNewThread;
private UserAuthService userAuthService;
#Inject
SplashPresenter(SplashView view, UserAuthService userAuthService, #Named(ValueConstants.NEW_THREAD) Scheduler newThread, #Named(ValueConstants.MAIN_THREAD) Scheduler mainThread) {
super(view);
this.userAuthService = userAuthService;
mNewThread = newThread;
mMainThread = mainThread;
}

Is there a difference between the way Dagger2 treats #Singleton and custom sopes?

The question is in the subject, yet I'll repeat it again:
Is there a difference between the way Dagger2 treats #Singleton and custom sopes?
Also, if a class is annotated with some scope, is there a convenient way to expose it as a different scope (or unscoped), or do I need to write a provider method?
There is no difference between the way Dagger2 treats #Singleton and custom sopes.
Lets say we are using #User
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface User {
}
#Module
public class TwitterModule {
private final String user;
public TwitterModule(String user) {
this.user = user;
}
#Provides
#User
Tweeter provideTweeter(TwitterApi twitterApi) {
return new Tweeter(twitterApi, user);
}
#Provides
#User
Timeline provideTimeline(TwitterApi twitterApi) {
return new Timeline(twitterApi, user);
}
}
#Module
public class NetworkModule {
#Provides
#Singleton
OkHttpClient provideOkHttpClient() {
return new OkHttpClient();
}
#Provides
#Singleton
TwitterApi provideTwitterApi(OkHttpClient okHttpClient) {
return new TwitterApi(okHttpClient);
}
}
#Singleton
#Component(modules = {NetworkModule.class})
public interface ApiComponent {
TwitterApi api();
TwitterComponent twitterComponent(TwitterModule twitterModule);
}
#User
#Subcomponent(modules = {TwitterModule.class})
public interface TwitterComponent {
TwitterApplication app();
}
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
TwitterComponent twitterComponentForUserOne,twitterComponentForUserTwo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ApiComponent apiComponent = DaggerApiComponent.create();
twitterComponentForUserOne = apiComponent.twitterComponent(new TwitterModule("Amit Shekhar"));
twitterComponentForUserTwo = apiComponent.twitterComponent(new TwitterModule("Sumit Shekhar"));
// use twitterComponentOne and twitterComponentTwo for two users independently
}
#Override
protected void onDestroy() {
super.onDestroy();
twitterComponentForUserOne = null;
twitterComponentForUserTwo = null;
}
}
Here just we have to make sure that when we do not need the twitterComponent for that user. We have to assign null so that it gets garbage collected as I am doing here in onDestroy();
Finally, everything depends on component,if you have an instance of component in Application class it is not going to be garbage collected for whole application life-cycle.

Inject in Dagger returns null

Been blocked on this for a number of days.
For some reason my the 'application' member field in AndroidModule.java is null after injection.
AndroidModule.java
#Module(
library = true
)
public class AndroidModule {
#Inject MittoApplication application;
#Provides #Singleton
SharedPreferences provideSharedPreferences() {
return PreferenceManager.getDefaultSharedPreferences( application );
}
}
ApplicationModule.java
#Module(
includes = { ApiModule.class, AndroidModule.class },
library = true
)
public class ApplicationModule {
private Application application;
public ApplicationModule( Application application ) {
this.application = application;
}
#Provides #Singleton
Application providesApplication() {
return application;
}
}
BaseActivity.java
public class BaseActivity extends AppCompatActivity {
private ObjectGraph objectGraph;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
objectGraph = MittoApplication.getApplication(this).buildScopedObjectGraph(getModules());
objectGraph.inject(this);
}
#Override
protected void onDestroy() {
objectGraph = null;
super.onDestroy();
}
protected List<Object> getModules( ) {
return Arrays.<Object>asList(
);
}
}
MittoApplication.java
public class MittoApplication extends Application {
private static Context context;
private ObjectGraph objectGraph;
public void onCreate() {
super.onCreate();
MittoApplication.context = getApplicationContext();
initializeObjectGraph();
}
public static MittoApplication getApplication( Context context ) {
MittoApplication mittoApplication = (MittoApplication) context.getApplicationContext();
return mittoApplication;
}
public ObjectGraph getObjectGraph() {
return this.objectGraph;
}
public ObjectGraph buildObjectGraph( List<Object> modules ) {
return ObjectGraph.create(modules.toArray() );
}
public ObjectGraph buildScopedObjectGraph( List<Object> modules ) {
return objectGraph.plus(modules.toArray() );
}
private ObjectGraph buildInitialObjectGraph( List<Object> modules ) {
return ObjectGraph.create( modules.toArray() );
}
private void initializeObjectGraph() {
objectGraph = buildInitialObjectGraph( Arrays.<Object>asList( new ApplicationModule(this) ));
}
public static Context getContext() {
return MittoApplication.context;
}
}
I've spend ages pondering over this, I've looked at countless examples and blog sites. Would love someone smarter then I to assist.
Thanks for reading.
Field injection works only with direct invocation of the object graph. To obtain the application instance for your provider method, you need to reference it as a parameter of the provider method.
//#Inject MittoApplication application; //this won't work
#Provides #Singleton
SharedPreferences provideSharedPreferences(Application application) { //this will
return PreferenceManager.getDefaultSharedPreferences( application );
}
Also, you never actually provided a MittoApplication, only an Application.
And it's worth noting that you are using Dagger1, so I believe you will need to change your AndroidModule to be complete=false:
#Module(
library = true,
complete = false
)
public class AndroidModule {

How to provide your services via #Context in Neo4j unmanaged extension

I have Neo4j unmanaged extension. I want some services to be created as singletons and be available via #Context in my resources.
Something like this:
#Path("/example")
public class ExampleResource {
public ExampleResource(#Context CostlyService costlyService) { // <<---
// use it here
}
}
How this can be achieved?
Neo4j has PluginLifecycle interface that give us possibility to hook into Neo4j server lifecycle and provide our own services for injection blog post.
So, we have service. Let's take this one as example:
public interface CostlyService {
}
public class CostlyServiceImpl implements CostlyService {
public CostlyService() {
// a LOT of work done here
}
//...
}
Now we need to make our own PluginLifecycle implementation:
public class ExamplePluginLifecycle implements PluginLifecycle {
#Override
public Collection<Injectable<?>> start(GraphDatabaseService graphDatabaseService,
Configuration config) {
final List<Injectable<?>> injectables = new ArrayList<>();
return injectables;
}
#Override
public void stop() {
}
}
As you see, injectable list is empty for now. We will add our service there soon.
Important: you must register your PluginLifecycle implementation, so it will be available via SPI:
// file: META-INF/services/org.neo4j.server.plugins.PluginLifecycle
my.company.extension.ExamplePluginLifecycle
This will make your PluginLifecycle discoverable by Neo4j server.
Now we need to create actual injectable. Let's write implementation for Injectable interface:
public final class TypedInjectable<T> implements Injectable<T> {
private final T value;
private final Class<T> type;
private TypedInjectable(final T value, final Class<T> type) {
this.value = value;
this.type = type;
}
public static <T> TypedInjectable<T> injectable(final T value, final Class<T> type) {
return new TypedInjectable<>(value, type);
}
#Override
public T getValue() {
return value;
}
#Override
public Class<T> getType() {
return type;
}
}
This will serve as simple container for our service. Usage:
import static my.company.extension.TypedInjectable.injectable;
injectable(new CostlyServiceImpl(), CostlyService.class);
Now we can add our injectable into PluginLifecycle.
#Override
public Collection<Injectable<?>> start(GraphDatabaseService graphDatabaseService,
Configuration config) {
final List<Injectable<?>> injectables = new ArrayList<>();
injectables.add(injectable(new CostlyServiceImpl, CostlyService.class)); // <<---
return injectables;
}
After this change our CostlyService will be available for our resources via #Context:
#Path("/example")
public class ExampleResource {
public ExampleResource(#Context CostlyService costlyService) {
// use it here
}
// ...
}
Tip: keep your PluginLifecycle's in same package or in subpackage with your resources.

Categories