Home » Android » java – Dagger for Android: Injecting an Activity to the object graph after calling setContentView

java – Dagger for Android: Injecting an Activity to the object graph after calling setContentView

Posted by: admin June 15, 2020 Leave a comment

Questions:

I want to use Dagger on Android to inject an instance of an Activity into another class as follows:

class PresentationLayer{
    Activity mActivity;

    @Inject
    public PresentationLayer(Activity activity){
        this.mActivity = activity;
    }

    public void presentData(User user){
        ((TextView)mActivity.findViewById(R.id.username))
            .setText(user.getName());
        //...
        //...
    }
}

I am able to do the injection, but all fields of the Activity are null at the time of injection.

Here’s how I am doing the injection:

My Activity is a module in itself.

@Module(
    complete = false
)
class MainActivity extends Activity{

    @Override
    public void onCreate(Bundle bundle){
        super.onCreate(bundle);
        setContentView(R.layout.main_activity);
        ObjectGraph objectGraph = CustomApplication.getObjectGraph();
        PresentationLayer pres = objectGraph.get(PresentationLayer.class);
    }

    @Provides Activity provideActivity(){
        return this;
    }


}

This is my main module

@Module(
    entryPoints = PresentationLayer.class,
    includes = MainActivity.class
)
class DaggerModule{
    @Provides PresentationLayer providePresentation(Activity activity){
        return new PresentationLayer(activity);
    }
}

And my Application class that bootstraps the object graph.

class CustomApplication extends Application{

    private static ObjectGraph sObjectGraph;
    @Override
    public void onCreate(){
        sObjectGraph = ObjectGraph.create(new DaggerModule());
    }

    static getObjectGraph(){
        return sObjectGraph;
    }
}

I am looking for a way to explicitly perform the injection after I call setContentView.

How do I go about doing this?


EDIT 1:

I got it to work like this – however I am not sure if this is the “right way”. What I do is

  1. Pass in the Activity to the Module constructor
  2. Ensure that I build the ObjectGraph after I do setContentView() so that a proper Activity instance is passed in to the ObjectGraph.

My Activity is no longer a dagger Module.

This translates to the following in code:

@Module(
    entryPoints = PresentationLayer.class
)
class DaggerModule{

    private Activity mActivity;

    public DaggerModule (Activity activity){
        this.mActivity = activity;
    }

    @Provides PresentationLayer providePresentation(){
        return new PresentationLayer(mActivity);
    }
}

And this is how my Activity looks:

class MainActivity extends Activity{

    @Override
    public void onCreate(Bundle bundle){
        super.onCreate(bundle);
        setContentView(R.layout.main_activity);
        ObjectGraph objectGraph = CustomApplication.getObjectGraph(this);
        PresentationLayer pres = objectGraph.get(PresentationLayer.class);
        User user = //get the user here
        pres.presentData(user);
    }
}

This solution seems to address the points raised by Jesse Wilson in this answer. However, I am concerned about the performance, since the ObjectGraph is going to be built every time the Activity is created.

Any thoughts?

How to&Answers:

Unfortunately this probably isn’t going to work with Dagger, or with most other dependency injection frameworks. The problem is that the lifecycle of Activity doesn’t cleanly line up with the lifecycle of PresentationLayer. For example, when you rotate your screen, Android will destroy the activity and create another one in its place. There’s no hook for PresentationLayer to participate in that.

At Square we’ve been using Otto to communicate between activities and their backends and it has worked well. See Eric Burke’s sample code project for an example.