How to get Activity Instance in Module class using AndroidInjector?

1.7k Views Asked by At

I have sample project in which I am using dagger-android AndroidInjector to Inject dependencies. In this sample app I have a class Navigator which does all the Activity to Activity navigation and it requires the instance of current Activity in it's Constructor.

I have a AppModule class which provides the Instances to the other component, which is also responsible for providing the instance of Navigator to my presenter, but the Navigator constructor takes Activity as parameter from the AppModule. How can I access the activity instance to the Navigator constructor. I have tried Injecting the instance of Activity directly in Navigator by annotating the Activity member variable with @Inject annotation but that throws error while building the project.

error: [dagger.android.AndroidInjector.inject(T)] android.app.Activity cannot be provided without an @Inject constructor or from an @Provides-annotated method. android.app.Activity is injected at

I have referred other SA post but that doesn't help with my query or may be I am failed to understand the solution given there. Below are the post I followed

How to inject current activity in a collaborator using Dagger 2.11 Android injector

How to get MainActivity inside a module using AndroidInjector

Below is the code which I have implemented:

Application.Java

public class MyApplication extends Application implements HasActivityInjector {

    @Inject
    DispatchingAndroidInjector<Activity> androidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent
                .builder()
                .application(this)
                .build()
                .inject(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return androidInjector;
    }
}

MainActivity.java

public class MainActivity extends BaseActivity implements BaseView {

    @Inject
    Context application;

    @Inject
    MainPresenter mainPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }
}

MyPresenter.Java

public class MyPresenter extends BasePresenter<IActivityView> {

    private final NavigationRouter mNavigationRouter;

    @Inject
    public MyPresenter(Navigator navigatr) {
        mNavigationRouter = navigationRouter;
    }

}

Navigator.Java

//This is how my Navigator is currently.
public class Navigator{

    Activity mActivity;

    @Inject
    public Navigator(Activity activity){
    mActivity = activity;
    }

}

ActivityBuilder.java

@Module
public abstract class ActivityBuilder {

    @ContributesAndroidInjector
    abstract MainActivity bindMainActivity();

    @ContributesAndroidInjector
    abstract LoginActivity bindLoginActivity();
}

AppModule.Java

@Module
public class AppModule {
//How can i have the instance of current activity in this module class.
    @Provides
    @Singleton
    NavigationRouter providesNavigationRouter(Activity activity) {
        //Here I want the instance of current activity
        return new NavigationRouter(activity);
    }
}

AppComponent.Java

@Component(modules = {
        AndroidInjectionModule.class,
        AppModule.class,
        ActivityBuilder.class})
@Singleton
public interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(Application application);

        AppComponent build();
    }

    void inject(MyApplication application);
}

Any help on this will be much appreciated.

I have update the code snippet for more clarity

1

There are 1 best solutions below

6
PPartisan On

In this sample app I have a class Navigator which does all the Activity to Activity navigation and it requires the instance of current Activity in it's Constructor

I'm a little confused on how this is expected to work. Does this mean that each Activity creates its own Navigator? If the Activity is supplied via the constructor then I can't think of any other way this would be feasible.

Anyway, assuming you do want to create a Navigator for each of your Activities, then your Dagger setup would look like this:

MainActivityModule.java

@Module
public abstract class MainActivityModule {
    @Binds @Named(MainActivity.class.getSimpleName()) abstract Navigator navigator(MainActivity ma);
}

ActivityBuilder.java

@Module
public abstract class ActivityBuilder {
    @ContributesAndroidInjector(modules = MainActivityModule.class) 
    abstract MainActivity bindMainActivity();
}

MainActivity.java

public class MainActivity extends BaseActivity implements BaseView {

    @Inject Navigator navigator; //This will hold a reference to MainActivity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this); //Or AndroidSupportInjection...
    }
}

Edit: I made a couple of points the comments that show a "Navigator" class that takes a single Activity in via its constructor isn't feasible. You could instead try this:

@Singleton
public final class Navigator {

    private WeakReference<BaseActivity> ref = null;

    @Inject
    Navigator(){}

    void setForegroundActivity(BaseActivity base) {
        this.ref = new WeakReference<>(base);
    }

    void transitionTo(Class<? extends BaseActivity> next) {
        if(ref == null) return;
        final BaseActivity current = ref.get();
        if(current != null) {
            current.transitionTo(next);
        }
    }
}
public class MyApp extends Application implements Application.ActivityLifecycleCallbacks {

    @Inject Navigator navigate;

//...
   void onActivityResumed(Activity activity) {
        if(activity instanceof BaseActivity) {
            navigate.setForegroundActivity((BaseActivity) activitiy);
        }
    }

}
public class AnExampleClass {

    private final Navigator navigate;

    @Inject
    AnExampleClass(Navigator navigate) {
        this.navigate = navigate;
    }

    //...
    void someMethodRequiringATransition() {
        navigator.transitionTo(AnotherActivity.class);
    }

}
public class AnExampleActivity extends BaseActivity {

    @Override
    public void transitionTo(Class<? extends BaseActivity> next) {
        if(AnotherExampleActivity.class.equals(next)) {
            //Do stuff specific to transitioning to "Another Example Activity"
        }
    }    

}

I use WeakReferences because storing an Activity in a singleton is dangerous, and effectively begs memory leaks. However, you could use the ActivityLifecycleCallbacks to remove Activities as they move into "onPause" as well.