Dagger automatically persisting data - android

I have a class that's being injected using DI via dagger. However when the activity is destroyed and recreated, the model class seems to contain the data automatically without me insert/repopulating the data.
public class MyApplication extends Application {
private ExpressionFactoryComponent mExpressionFactoryComponent;
#Override
public void onCreate() {
super.onCreate();
// Building dagger DI component
mExpressionFactoryComponent = DaggerExpressionFactoryComponent.builder().
expressionFactoryModule(new ExpressionFactoryModule(new ExpressionFactory(this))).build();
}
}
Module:
#Module
public class ExpressionFactoryModule {
private ExpressionFactory mExpressionFactory;
public ExpressionFactoryModule(ExpressionFactory expressionFactory) {
this.mExpressionFactory = expressionFactory;
}
#Provides
ExpressionFactory provideStringExpressionFactory() {
return mExpressionFactory;
}
}

The one reason for this is that ExpressionFactory is instantiated in MyApplication class and then passed into the constructor of ExpressionFactoryModule while creating ExpressionFactoryComponent. You should pass an Application instance in your module constructor and then create an instance of your class withing a metod with #Provide annotation passing that Application instance in your class's constructor.
This how things should be done with dagger. But to solve your problem you need to create another component class and build the component in an activity if you need to have an instance of your class living withing an activity only.
Here is the solution (ExpressionFactoryComponent is renamed to AppComponent here):
public class MyApplication extends Application {
private static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
// Building dagger DI component
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this)).build();
}
public static AppComponent getAppComponent() {
return appComponent;
}
}
#Component(modules = {AppModule.class})
public interface AppComponent {
Application getApplication();
void inject(App application);
}
#Module
public class AppModule {
private Application application;
public AppModule(Application application) {
this.application = application;
}
#Provides
Application provideApplication() {
return application;
}
}
#Component(dependencies = AppComponent.class, modules = {
ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity activity);
}
#Module
public class ActivityModule {
private Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
#Provides
Activity provideActivity() {
return activity;
}
#Provides
ExpressionFactory provideStringExpressionFactory(Application application) {
return new ExpressionFactory(application);
}
}
public class MainActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityComponent activityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(activity))
.appComponent(App.getAppComponent())
.build();
activityComponent.inject(this);
}
}

Related

RequestQueue cannot be provided without an inject constructor

I am trying to learn Dagger2 from this medium article and pass RequestQueue as an activity level dependency:
https://proandroiddev.com/dagger-2-annotations-binds-contributesandroidinjector-a09e6a57758f
I can create application components just fine but I am facing a lot of trouble with ContributesAndroidInjector.
Application Class:
public class PokemonApplication extends Application {
private static AppComponent appComponent;
public static AppComponent getAppComponent(){
return appComponent;
}
#Override
public void onCreate() {
super.onCreate();
appComponent=buildMyComponent();
}
private AppComponent buildMyComponent() {
return DaggerAppComponent.builder().appmod(this).build();
}
}
AppModule:
#Module
public abstract class AppModule {
#ContributesAndroidInjector(modules = VolleyModule.class)
abstract MainActivity mainActivity();
#Provides
#Singleton
static SharedPreferences providePreferences(Application application) {
return application.getSharedPreferences("data", Context.MODE_PRIVATE);
}
#Provides
#Singleton
static Context getContext(Application application){
return application.getApplicationContext();
}
}
AppComponent:
#Singleton
#Component(modules = {RetrofitModule.class,AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity); //Error here.
#Component.Builder
interface Builder
{
AppComponent build();
#BindsInstance Builder appmod(Application application);
}
}
VolleyModule:
#Module
public abstract class VolleyModule {
#Provides
static RequestQueue getRequestQueue(Context context) {
return Volley.newRequestQueue(context);
}
}
MainActivity:
#Inject
RequestQueue requestQueue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PokemonApplication.getAppComponent().inject(this);
VolleyModule has to be also declared in the list of modules in your Component
#Singleton
#Component(modules = {RetrofitModule.class,AppModule.class, VolleyModule.class})

dagger2 field is not injected

I have an application (module + component) where
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(App app);
Serializer getSerializer();
ListOfCallingLists getListOfCallingLists();
Context getContext();
App getApp();
}
And
#Module
public class AppModule {
private final App app;
public AppModule(App app) {
this.app = app;
}
#Provides
Serializer provideSerializer() {
return new BinarySerializer();
}
#Provides
Context provideContext() {
return app;
}
#Provides
App provideApp() {
return app;
}
}
And
#Singleton
public class ListOfCallingLists implements Serializable {
...
#Inject
public ListOfCallingLists(Context context,
Serializer serializer) {
this.serializer = serializer;
...
}
}
And App is the application, I registered it in manifest:
public class App extends Application {
private AppComponent appComponent;
public static App get(Context context) {
return (App) context.getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
if (appComponent == null)
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
appComponent.inject(this);
}
public AppComponent getComponent() {
return appComponent;
}
}
And finally the activity:
public class CallListsActivity extends AppCompatActivity {
#Inject
ListOfCallingLists list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
#Override
public void onPostCreate(#Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// list here is null why?
}
}
In your AppComponent you need to add:
void inject(CallListsActivity callListActivity);
And in your CallListsActivity's onCreate() you need to tell how is your CallListsActivity injected.
For example, build your AppComponent and inject the activity, or you can use the new android injector: https://google.github.io/dagger/android.html

Dagger component & subcomponents

I am trying to figure out how to use Dagger components/subcomponents. I know it is the older style of things but I have a legacy app that is using this Dagger structure and I'm using this sample project to better understand it.
This sample app has just a AppComponent, AppModule, ActivityComponent and ActivityModule. I'm trying to inject MainActivityDependency into my MainActivity but the compiler says it can provide it without an explicit #Provides. But I am already explicitly providing it. I know I can just annotate the class with #Inject but for the sake of exercise I need to use the #Provides method.
It seems like I have the dependency structure correct but can't figure it out. I've pasted sample code below but here is the github link as well:
https://github.com/fkruege/daggerSubComponentPractice/tree/master/app/src/main/java/com/franctan/daggerpractice
Thank you
AppComponent.java
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
ActivityComponent.Builder activityComponentBuilder();
Application application();
void inject(MyApp myApp);
}
AppModule
#Module(subcomponents = {ActivityComponent.class})
public class AppModule {
private Application app;
public AppModule(Application app) {
this.app = app;
}
#Provides
#Singleton
public Application provideApplication() {
return app;
}
#Provides
#Singleton
public AppDependency provideAppDependency() {
return new AppDependency();
}
}
ActivityComponent
#ActivityScope
#Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
#Subcomponent.Builder
interface Builder {
Builder activityModule(ActivityModule module);
ActivityComponent build();
}
void inject(MainActivity mainActivity);
}
ActivityModule
#Module
public class ActivityModule {
private Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
#ActivityScope
#Provides
public Activity provideActivity() {
return activity;
}
#ActivityScope
#Provides
public MainActivityDependency provideMainActivityDependency() {
return new MainActivityDependency();
}
}
MyApp
public class MyApp extends Application {
public AppComponent appComponent;
#Inject
AppDependency appDependency;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent
.builder()
.appModule(new AppModule(this))
.build();
appComponent.inject(this);
}
}
MainActivity
#Inject
MainActivityDependency mainActivityDependency;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApp myApp = (MyApp) getApplication();
myApp.appComponent
.activityComponentBuilder()
.activityModule(new ActivityModule(this))
.build()
.inject(this);
So figured it out. It turns out the ActivityScope interface was defined incorrectly:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Scope
public #interface ActivityScope {
}
The #Qualifier annotation was messing things up. Dumb mistake.

I don't know why the object injected by dagger2 is null in presenter

I don't know why loginResponseHandler of MainRankPresenter.java injected by dagger2 is null in MainRankPresenter.
I just want to inject to field for field injection.
Should I do other way instead field injection?
please, Let me know how to resolve it.
BBBApplication.java
public class BBBApplication extends MultiDexApplication
{
...
#Override
public void onCreate() {
super.onCreate();
initAppComponent();
}
private void initAppComponent() {
this.appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
public static BBBApplication get(Context ctx) {
return (BBBApplication) ctx.getApplicationContext();
}
public AppComponent getAppComponent() {
return this.appComponent;
}
...
}
AppModule.java
#Module
public class AppModule {
private BBBApplication application;
public AppModule(BBBApplication application) {
this.application = application;
}
#Provides
#Singleton
public Application provideApplication() {
return this.application;
}
#Provides
#Singleton
public Resources provideResources() {
return this.application.getResources();
}
#Provides`enter code here`
#Singleton
public SharedPreferences provideSharedPreferences() {
return PreferenceManager.getDefaultSharedPreferences(this.application);
}
}
AppComponent.java
#Singleton
#Component(modules = {AppModule.class, ServiceModule.class})
public interface AppComponent {
RankFragmentComponent plus(RankFragmentModule module);
Application application();
Resources resources();
}
RankFragmentModule.java
#Module
public class RankFragmentModule {
private RankFragment rankFragment;
public RankFragmentModule(RankFragment rankFragment) {
this.rankFragment = rankFragment;
}
#Provides
#ActivityScope
public LoginResponseHandler provideLoginResponseHandler() {
return new LoginResponseHandler(this.rankFragment);
}
#Provides
#ActivityScope
// #Named("rankFragment")
public RankFragment provideRankFragment() {
return this.rankFragment;
}
#Provides
#ActivityScope
public MainRankPresenter provideMainRankPresenter(RankFragment rankFragment) {
return new MainRankPresenter(new MainRankViewOps(rankFragment));
}
}
RankFragmentComponent.java
#ActivityScope
#Subcomponent(modules = {RankFragmentModule.class})
public interface RankFragmentComponent {
void inject(RankFragment rankFragment);
}
RankFragment.java
public class RankFragment extends Fragment {
#Inject
MainRankPresenter presenter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BBBApplication.get(getContext())
.getAppComponent()
.plus(new RankFragmentModule(this))
.inject(this);
presenter.test();
}
MainRankPresenter.java
public class MainRankPresenter implements Presenter {
private MainRankViewOps viewOps;
#Inject
LoginResponseHandler loginResponseHandler;
#Inject
public MainRankPresenter(MainRankViewOps viewOps) {
this.viewOps = viewOps;
}
#Override
public void test() {
Log.d(Constants.TAG_DEBUG, "=== presenter test" + this.toString());
Log.d(Constants.TAG_DEBUG, "=== " + loginResponseHandler.toString());
this.viewOps.initViewOps();
}
}
MainRankViewOps.java
public class MainRankViewOps implements ViewOps {
RankFragment fragment;
#Inject
public MainRankViewOps(RankFragment fragment) {
this.fragment = fragment;
}
#Override
public void initViewOps() {
Log.d(Constants.TAG_DEBUG, "=== view ops" + this.toString());
Log.d(Constants.TAG_DEBUG, "=== " + fragment.toString());
}
}
Injection by Dagger 2 is not recursive. Therefore, when you call inject(this) in RankFragment only #Inject annotated fields of that fragment are being injected. Dagger 2 will not search for #Inject annotations in the injected objects.
In general, you should attempt to restrict usage of Dependency Injection frameworks to "top-level" components (Activities, Fragments, Services, etc.) which are being instantiated by Android framework for you. In objects that you instantiate yourself (like MainRankPresenter) you should use other DI techniques which do not involve external framework (e.g. dependency injection into constructor).
Because you #Provides the MainRankPresenter, Dagger won't inject it: you take responsibility for this. You could possibly have your provides method be passed a MembersInjector si you can inject the fields if the object before returning it, but it'd probably be better to refactor your module you remove that provides method and let Dagger handle the injection (you have all the #Inject needed already)

Android Dagger 2 POJO field Inject null

Just started using Dagger 2 today and I'm a bit confused on how exactly I need to set everything up.
I'm trying to inject a POJO, but it's always null.
First, some code:
App.java
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent
.builder()
.appModule(new AppModule(this))
.build();
}
public AppComponent component() {
return appComponent;
}
AppModule.java
#Module
public class AppModule {
private Application app;
public AppModule(Application app) {
this.app = app;
}
#Provides #Singleton
public Application application() {
return app;
}
}
AppComponent.java
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(App application);
Application application();
}
NetworkingManager.java
#Singleton
public class NetworkingManager {
private Context ctx;
#Inject
public NetworkingManager(Context context) {
this.ctx = context;
}
}
NetModule.java
#Module
public class NetModule {
#Provides #Singleton
public NetworkingManager provideNetworkingManager(Application application) {
return new NetworkingManager(application);
}
}
NetComponent.java
#Singleton
#Component(modules = {NetModule.class},
dependencies = {AppModule.class})
public interface NetComponent {
void inject(NetworkingManager networkingManager);
}
SomeClass.java
#Inject
NetworkingManager networkingManager;
public void doSomethingWithNetworkManager() {
networkManager.doStuff();
}
I've spent a good deal of time looking through lots of tutorials, SO questions, and examples, but I haven't been able to figure out what I'm doing wrong.
I'm 99% certain I have something setup wrong, but I haven't been able to figure out what.
Based on your comment, you want to make NetworkingManager available everywhere in your application.
Let's start with your definition of the Component:
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(App application);
}
This tells Dagger that this component will be injecting the App class. Now here you can also tell Dagger other classes you would like to inject. So if you want to also inject an Activity for example you would add:
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(App application);
void inject(MainActivity activity) //Where MainActivity is a class that extends Activity.
}
Please note that this is not the best way, IMO, to share Application wide dependencies; you should create a Component that inherits from AppComponent and make AppComponent expose the desired shared dependencies.
Now let's look at your module class:
#Module
public class NetModule {
#Provides #Singleton
public NetworkingManager provideNetworkingManager(Application application) {
return new NetworkingManager(application);
}
}
Here you are #Provideing a NetworkingManager, that is fine. Your NetworkingManager requires an Application (a Context really), why not provide App inside of NetworkingManager?, or even better why not provide NetworkingManager inside the AppModule since AppModule should #Provide things that are common for the whole Application:
#Module
public class AppModule {
private Application app;
public AppModule(Application app) {
this.app = app;
}
#Provides #Singleton
public Application application() {
return app;
}
#Provides #Singleton
public NetworkingManager provideNetworkingManager(Application application) {
return new NetworkingManager(application);
}
}
Now inside your App class:
public class App extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent
.builder()
.appModule(new AppModule(this))
.build();
appComponent.inject(this);
}
public AppComponent component() {
return appComponent;
}
}
And in our hypothetical MainActivity:
public class MainActivity extends Activity {
private AppComponent appComponent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appComponent = ((App)getApplicationContext()).getAppComponent();
appComponent.inject(this);
}
}
It seems that you are not using #Component(dependencies = {...}) correctly. dependencies is used when you want to expose a dependency from one Component to another using the mechanism I mentioned above.

Categories

Resources