1

I am following coding with mitch's dagger 2 course from youtube.I am confused about something.Here is my classes: AppComponent.class

@Singleton
@Component(
        modules = {
                AndroidSupportInjectionModule.class,
                ActivityBuildersModule.class,
                AppModule.class,
                ViewModelFactoryModule.class,
        }
)
public interface AppComponent extends AndroidInjector<BaseApplication> {
    
    SessionManager sessionManager();

    @Component.Builder
    interface Builder{

        @BindsInstance
        Builder application(Application application);

        AppComponent build();
    }

}

ActivityBuildersModule

@Module
public abstract class ActivityBuildersModule {

    @ContributesAndroidInjector(
            modules = {AuthViewModelsModule.class,
                    AuthModule.class
            }
    )
    abstract AuthActivity contributeAuthActivity();

    @ContributesAndroidInjector
    abstract MainActivity contributeMainActivity();

}

BaseActivity

public abstract class BaseActivity extends DaggerAppCompatActivity {

    private static final String TAG = "BaseActivity";
    @Inject
    public SessionManager sessionManager;//confused here

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        subscribeObservers();
    }

    private void subscribeObservers() {
        sessionManager.getAuthUser().observe(this, userAuthResource -> {
            if (userAuthResource != null) {
                switch (userAuthResource.status) {
                    case LOADING: {
                        break;
                    }
                    case AUTHENTICATED: {
                        Log.d(TAG, "subsrcibeObservers: LOGIN SUCCESS:" + userAuthResource.data.getEmail());
                        break;
                    }
                    case ERROR: {
                        Toast.makeText(this, userAuthResource.message, Toast.LENGTH_SHORT).show();
                        break;
                    }
                    case NOT_AUTHENTICATED: {
                        navLoginScreen();
                        break;
                    }
                }
            }
        });
    }
    private void navLoginScreen(){
        Intent intent = new Intent(this, AuthActivity.class);
        startActivity(intent);
        finish();
    }

}

MainActivity

public class MainActivity extends BaseActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

SessionManager

@Singleton
public class SessionManager {
    private static final String TAG = "SessionManager";

    private MediatorLiveData<AuthResource<User>> cachedUser = new MediatorLiveData<>();

    @Inject
    public SessionManager() {

    }
    public void authenticaWithId(final LiveData<AuthResource<User>> source) {
        if (cachedUser != null) {
            cachedUser.setValue(AuthResource.loading(null));
            cachedUser.addSource(source, userAuthResource -> {
                cachedUser.setValue(userAuthResource);
                cachedUser.removeSource(source);
            });
        }
    }

    public void logOut(){
        Log.d(TAG, "logOut: logging out...");
        cachedUser.setValue(AuthResource.logout());
    }
    public LiveData<AuthResource<User>> getAuthUser(){
        return cachedUser;
    }
}

BaseApplication

public class BaseApplication extends DaggerApplication {
    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        return DaggerAppComponent.builder().application(this).build();
    }
}

My problem is @Inject SessionManager sessionManager that statement in the BaseActivity.In the ActivityBuildersModule class, we only annotated MainActivity as a subcomponent, not the BaseActivity.Since the MainActivity is a subcomponent it can access dependencies of the AppComponent.So how we get accessed that SessionManager in the Base Activity or we get accessed that object because MainActivity is derived from BaseActivity?

Jarnojr
  • 543
  • 1
  • 7
  • 18
  • It works because `AppComponent` is `@Singleton`, and `SessionManager` is also `@Singleton`, therefore Dagger adds a `@Singleton`-scoped provider for `SessionManager` into `AppComponent`, from which all `DaggerInjector` generated child components inherit from. But I'm not sure why AndroidInjector can inject the base field automatically. – EpicPandaForce Jan 19 '21 at 04:41
  • Thanks for comment.I think I got the point thanks to David. – Jarnojr Jan 19 '21 at 15:18

1 Answers1

2

Dagger will inject any annotated fields and methods of the declared parameter type and its supertypes. You can also read about it on the JavaDoc:

Members-injection methods

Members-injection methods have a single parameter and inject dependencies into each of the Inject-annotated fields and methods of the passed instance.

[...]

A note about covariance

While a members-injection method for a type will accept instances of its subtypes, only Inject-annotated members of the parameter type and its supertypes will be injected;

So if you have a SubActivity < BaseActivity < AppCompatActivity then declaring the method as inject(activity: BaseActivity) would only inject fields of BaseActivity and any supertypes, wheras inject(activity: SubActivity) will inject SubActivity as well as any parent/supertypes (BaseActivity in this example, your observed behavior).

David Medenjak
  • 33,993
  • 14
  • 106
  • 134
  • Thanks for answer.So It's injected because MainActivitySubComponent is a subcomponent of the AppComponent which means It can provide dependencies from the AppComponent, and since MainActivity is the SubActivity of the BaseActivity we injected MainActivity with all its supertypes and inject fields(in this case `SessionManager` ) which you already said. – Jarnojr Jan 19 '21 at 15:03
  • Thank you so much. – Jarnojr Jan 19 '21 at 16:55