Dagger + MVP - Problems injecting Presenter in Model - java

I'm having problems with the dagger dependency cycle, and despite looking for hours, I can not find the solution. I think it's my idea of architecture. What could be wrong? I'm using Dagger 2.11.
Following the codes
Inicio.java
public class Inicio extends BaseFragment implements InicioMvpView {
#Inject
InicioMvpPresenter inicioPresenter;
#Inject
MainMvpPresenter<MainMvpView> mainPresenter;
//...
InicioComponent.java
#Subcomponent(modules = {InicioModule.class})
public interface InicioComponent extends AndroidInjector<Inicio> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<Inicio> {
}
}
InicioModel.java
public class InicioModel implements InicioMvpModel{
#Inject
InicioMvpPresenter inicioPresenter;
#Inject
public InicioModel() {
}
#Override
public void recuperarAgendamentos() {
//...
inicioPresenter.atualizarListaAgendamentos(agendamentos);
}
}
InicioModule.java
#Module
public class InicioModule {
#Provides
InicioMvpView provideInicioView(Inicio inicioFragment){
return inicioFragment;
}
#Provides
InicioMvpPresenter provideInicioPresenter(
InicioPresenter presenter) {
return presenter;
}
#Provides
InicioMvpModel provideInicioModel(InicioModel inicioModel) {
return inicioModel;
}
}
InicioPresenter.java
public class InicioPresenter implements InicioMvpPresenter{
#Inject
InicioMvpModel inicioModel;
#Inject
InicioMvpView inicioMvpView;
#Inject
public InicioPresenter() {
}
#Override
public void recuperarAgendamentos(Bundle savedInstanceState) {
//..
}
}

Your problem is that you trying to solve a ciclic dependency with Dagger and Dagger doesn't solve this problem.
This can be corrected in your architecture. Just pass a callback to your model instead of passing the Presenter to the Model.
This this:
public class InicioModel implements InicioMvpModel{
#Inject
public InicioModel() {
}
#Override
public void recuperarAgendamentos(Presenter inicioPresenter) {
//...
inicioPresenter.atualizarListaAgendamentos(agendamentos);
}
}
That's it. Just pass the presenter as a parameter in your methods in your model. This makes the communication less coupled.
You can also take a look for RxJava, it removes the need to pass the presenter in the method. Link: https://github.com/ReactiveX/RxJava
There is also a good implementation to follow for MVP by Antonio Leiva:
https://github.com/antoniolg/androidmvp

Related

Java Guice Named Bindings with Objects provided from other modules

Heres my current setup
Class file
public class ToyAdapter {
private final ToyClient toyClient;
private final Retryer retryer;
#Inject
public APIAdapter(final ToyClient toyClient,
#Named("toyRetryer") final Retryer retryer) {
this.toyClient = toyClient;
this.retryer = retryer;
}
Guice file
I have several guice modules, but this one pertains to the above class
public class ToyModule extends AbstractModule {
#Override
protected void configure() {
bind(ToyAdapter.class).in(Singleton.class);
bind(Retryer.class).annotatedWith(Names.named("toyRetryer")).toInstance(getToyRetryer());
}
#Provides
#Singleton
public ToyClient getToyClient(...){
...
}
private Retryer getToyRetryer() {#Takes no arguments
return RetryerBuilder...build();
}
}
So far this works great! However, now my retryer requires a LogPublisher object provided in another module.
I'm trying
public class ToyModule extends AbstractModule {
LogPublisher logPublisher;
#Override
protected void configure() {
requestInjection(logPublisher);
bind(ToyAdapter.class).in(Singleton.class);
bind(Retryer.class).annotatedWith(Names.named("toyRetryer")).toInstance(getToyRetryer());
}
private Retryer getToyRetryer() {
return RetryerBuilder.withLogPublisher(logPublisher).build();
}
}
LogPublisher is provided in another guice module which has alot of other objects that depend on LogPublisher so I'd rather not just merge everything into one giant guice module.
#Provides
#Singleton
public LogPublisher getLogPublisher() {...}
Is this the proper way to do this? I'm getting Java findBugs errors saying unwritten field so I'm thinking I'm doing it wrong.
Declare your Retryer with help of #Provides/#Named annotations.
#Provides
#Singleton
#Named("toyRetryer")
public Retryer getToyRetryer(LogPublisher logPublisher) {
return RetryerBuilder.withLogPublisher(logPublisher).build();
}

Dagger 2: inject an interface in a constructor

I'm trying to learn dagger 2 but I'm confused in injecting of constructor with interface. This is my below code :
MainActivity.java
public class MainActivity extends AppCompatActivity implements MainView {
// this keyword of request dependency . At compiling process, dagger will look at all of these annotations
//to create the exact dependency
#Inject MainPresenter mainPresenter ;
TextView textView ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textview) ;
DaggerPresenterComponent.create().inject(this);
textView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
mainPresenter.doThings(8555) ;
}
});
}
/**********************************/
#Override
public void invokeRandomViewMethod(String msg) {
textView.setText(msg);
}
}
MainPresenter.java
public class MainPresenter {
private MainView mainView ;
#Inject
public MainPresenter(MainView mainView) {
this.mainView = mainView;
}
public void doThings(int value){
Random random = new Random();
int rand= random.nextInt(value) ;
if(mainView != null){
mainView.invokeRandomViewMethod("You random number is "+rand);
}
}
public interface MainView {
void invokeRandomViewMethod(String msg) ;
}
}
This is the Module :
#Module
public class PresenterModule {
#Provides
// this is the method that will provide the dependancy
MainPresenter provideMainPresenter(MainView mainView){
return new MainPresenter(mainView);
}
}
And this is the Component
#Component (modules = PresenterModule.class)
public interface PresenterComponent {
void inject(MainActivity activity) ;
}
When I run the code it shows me this error :
Error:(15, 10) error: com.imennmn.hellodagger2example.MainView cannot
be provided without an #Provides-annotated method.
com.imennmn.hellodagger2example.MainView is injected at
com.imennmn.hellodagger2example.presenterInjection.PresenterModule.provideMainPresenter(mainView)
com.imennmn.hellodagger2example.MainPresenter is injected at
com.imennmn.hellodagger2example.MainActivity.mainPresenter
com.imennmn.hellodagger2example.MainActivity is injected at
com.imennmn.hellodagger2example.simpleInjection.DataComponent.inject(activity)
My Question is how I can provide the interface MainView by inject it with dagger and bind the MainPresenter and MainActivity ?
Any help would be appreciated !
By following code:
MainPresenter provideMainPresenter(MainView mainView) {
return new MainPresenter(mainView);
}
You are telling dagger: "hey, whenever I ask you to inject MainPresenter, construct it using MainView".
But dagger complaints, because you haven't specified how exactly he should build/acquire MainView.
So, in your PresenterModule do this:
#Module
public class PresenterModule {
MainView mainView;
public PresenterModule(MainView mainView) {
this.mainView = mainView;
}
#Provides
MainPresenter provideMainPresenter() {
return new MainPresenter(mainView);
}
}
Then when building the component:
DaggerPresenterComponent.builder()
.presenterModule(new PresenterModule(this))
.build();
Your provideMainPresenter implicitly depends on a MainView. Dagger has no way to get it. You need to add a method to provide it:
#Module
public class PresenterModule {
#Provides
MainView provideMainView(){
// Provide MainView here somehow so Dagger can use this to create a MainPresenter
}
#Provides
// this is the method that will provide the dependancy
MainPresenter provideMainPresenter(MainView mainView){
return new MainPresenter(mainView);
}
}
Add abstract module with #Binds annotation, look at my impl of : AbstractTestSettingsFragmentModule.java
TestFragment.java
public class TestFragment extends Fragment{
#Inject TestFragmentContract.Presenter mPresenter;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidSupportInjection.inject(this);
}
}
TestFragmentPresenterImpl.java
public class TestFragmentPresenterImpl implements TestFragmentContract.Presenter {
#Inject
public TestFragmentPresenterImpl(){
}
}
AbstractTestSettingsFragmentModule.java
#Module
public abstract class AbstractTestSettingsFragmentModule {
#Binds
#NonNull
public abstract TestSettingsFragmentContract.Presenter testSettingsFragmentPresenterImpl(TestSettingsFragmentImpl presenter);
}
ContributesModule.java
#Module
public abstract class ContributesModule {
#ContributesAndroidInjector(modules = {AbstractTestSettingsFragmentModule.class})
abstract TestSettingsFragment testSettingsFragment();
}
AppComponent.java
#Singleton
#Component(
modules = {
AndroidSupportInjectionModule.class,
ContributesModule.class,
AppModule.class,
})
public interface AppComponent extends AndroidInjector<DaggerApplication> {
void inject(TheApplication theApplication);
#Override
void inject(DaggerApplication instance);
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
You can use Assisted Injection with Dagger: https://dagger.dev/dev-guide/assisted-injection.html
Assisted injection is a dependency injection (DI) pattern that is used to construct an object where some parameters may be provided by the DI framework and others must be passed in at creation time (a.k.a “assisted”) by the user.

Dagger 2 - injecting non Android classes

I'm implimenting Dagger 2 in my Android app. I have it setup in the following way:
AppComponent.java
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AndroidSupportInjectionModule.class,
ActivityBuilder.class,
AppModule.class,
DataBaseDaoModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application aApplication);
AppComponent build();
}
Application application();
void inject(MyApplication aApplication);
}
AppInjector.java
ublic class AppInjector {
public static void init(MyApplication aApplication) {
//Initialize dagger and inject the aApplication
DaggerAppComponent.builder().application(aApplication).build().inject(aApplication);
aApplication.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity aActivity, Bundle aBundle) {
handleActivity(aActivity);
}
#Override
public void onActivityStarted(Activity aActivity) {
}
#Override
public void onActivityResumed(Activity aActivity) {
}
#Override
public void onActivityPaused(Activity aActivity) {
}
#Override
public void onActivityStopped(Activity aActivity) {
}
#Override
public void onActivitySaveInstanceState(Activity aActivity, Bundle aBundle) {
}
#Override
public void onActivityDestroyed(Activity aActivity) {
}
});
}
private static void handleActivity(Activity aActivity) {
if (aActivity instanceof HasActivityInjector) {
AndroidInjection.inject(aActivity);
Timber.d("injected Activity");
}
if (aActivity instanceof FragmentActivity) {
((FragmentActivity) aActivity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
#Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
Timber.d("injected Fragment");
AndroidSupportInjection.inject(f);
}
}
}, true);
}
}
}
AppModule.java
Module(includes = ViewModelModule.class)
class AppModule {
#Singleton
#Provides
ApiService providesApiService(OkHttpClient aClient, MyInterceptor aInterceptor) {
//Build a Retrofit object here
}
#Singleton
#Provides
OkHttpClient providesOkHTTPClient(MyInterceptor aInterceptor) {
//Setup OKHTTP here
}
}
And finally in MyApplication.Java in the onCreate method I just call the AppInjector like so: AppInjector.init(this);
All of this works and anything I put in my AppComponent's moduels, I can inject into Activities, Fragments and ViewModels.
However, I have cases where I would need a utility class, that depends on Application, for contex - and I use the utility class in various places. Or I will have a Manager class, that depends on Application, or needs something from AppModule. However, since I use these classes outside of Activities, Fragments and ViewModels I cannot just inject. How would I provide my utility classes with their dependencies and any other type of class - like a manager class?
My first thought was to create a UtilityComponent and a ManagerCompoent of sorts, however I have no idea how I would get them to work with anything in AppModuel or through my AppComponent.
Please don't just use component.inject(myObject) for everything. Always prefer constructor injection or provide it from a module where you can do additional setup steps. .inject(myObject) is intended for Framework components where you don't have access to the constructor.
My first thought was to create a UtilityComponent and a ManagerCompoent of sorts, however I have no idea how I would get them to work with anything in AppModuel or through my AppComponent.
You don't need a separate component for that. See below.
However, since I use these classes outside of Activities, Fragments and ViewModels I cannot just inject.
That has nothing to do with injection. You're talking about scopes, and it sound like your utilities are a #Singleton. Your AppComponent is a #Singleton scoped component, hence it can be used to provide your utils, too.
However, I have cases where I would need a utility class, that depends on Application, for context
If they are part of the #Singleton component, which has access to your Application, they can also be provided anywhere else. No need for more components or anything. Just declare your dependencies and don't overthink it.
Just declare your util, annotate it with #Singleton and mark the constructor with #Inject for constructor injection. #Singleton ensures that it will be provided by your AppComponent and can access the Application on which it depends.
#Singleton public class MyUtil {
private Application application;
#Inject public MyUtil(Application application) {
this.application = application;
}
}
And then you can just inject it in your Activities, Fragments, or even into other Utilities....
#Singleton public class MyUtilWrapper {
private MyUtil myUtil;
#Inject public MyUtilWrapper(MyUtil myUtil) {
this.myUtil = myUtil;
}
}
And you can inject either or both into your activity or fragment...
#Inject MyUtil myUtil;
#Inject MyUtilWrapper myUtilWrapper;
void onCreate(..) {
AndroidInjection.inject(this);
}
You do not need any modules, provides methods, or components to provide simple classes. Just make sure to add the right scope!

Dagger 2 Null field injection

Hi I've read through other posts but I am not being able to fix it. Basically my issue is that I call .inject and when I want to use the field it's still null.
I have this class:
public class Application extends Game implements IApplication {
#Inject IApplication app;
#Inject IRouter router;
public Application(IPlatformCode platformCode) {
}
#Override
public void create() {
initDagger();
System.out.println(app); //NULL
System.out.println(router); //NULL
router.showPage(Page.MenuPage); //NULL EXCEPTION
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 0.5f, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
super.render();
}
#Override
public void setPage(IPage page) {
setScreen(page);
}
protected void initDagger() {
ApplicationComponent.Initializer.init(this).inject(this);
RouterComponent.Initializer.init().inject(this);
}
}
I won't show the router because I'm doing the same in app.
My Application component looks like this:
#Singleton
#Component ( modules = {ApplicationModule.class })
public interface ApplicationComponent {
void inject(IApplication application);
IApplication getApplication();
static final class Initializer {
private Initializer(){}
public static ApplicationComponent init(IApplication app) {
return DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(app))
.build();
}
}
}
And this is the module:
#Module
public class ApplicationModule {
private IApplication app;
public ApplicationModule(IApplication app) {
this.app = app;
}
#Provides
#Singleton
public IApplication providesApplication(){
return app;
}
}
As far as I read after calling inject(IApplication) the #Inject IApplication should be injected and have a value but right now it's null.
The generated code looks like this:
public final class DaggerApplicationComponent implements ApplicationComponent {
private Provider<IApplication> providesApplicationProvider;
private DaggerApplicationComponent(DaggerApplicationComponent.Builder builder) {
assert builder != null;
this.initialize(builder);
}
public static DaggerApplicationComponent.Builder builder() {
return new DaggerApplicationComponent.Builder();
}
private void initialize(DaggerApplicationComponent.Builder builder) {
this.providesApplicationProvider = DoubleCheck.provider(ApplicationModule_ProvidesApplicationFactory.create(builder.applicationModule));
}
public void inject(IApplication application) {
MembersInjectors.noOp().injectMembers(application);
}
public IApplication getApplication() {
return (IApplication)this.providesApplicationProvider.get();
}
public static final class Builder {
private ApplicationModule applicationModule;
private Builder() {
}
public ApplicationComponent build() {
if(this.applicationModule == null) {
throw new IllegalStateException(ApplicationModule.class.getCanonicalName() + " must be set");
} else {
return new DaggerApplicationComponent(this);
}
}
public DaggerApplicationComponent.Builder applicationModule(ApplicationModule applicationModule) {
this.applicationModule = (ApplicationModule)Preconditions.checkNotNull(applicationModule);
return this;
}
}
}
Thanks in advance.
Your inject method
void inject(IApplication application);
needs to change to
void inject(Application application);
Note the change from IApplication to just Application. You can't use interfaces for inject methods, you need to use a class.
Typically, Dagger 2 component creation is done in a class extending one of Android's Application classes. This is primarily done to ensure that these components (and the dependencies they house) are only instantiated once. (see Dagger docs for more details: https://google.github.io/dagger/users-guide)
While I haven't seen anything that says you can't wire it up differently, I haven't found an example of it, either. I'm wondering if trying to wire the components on-the-fly with the Initializer.init() calls you're making is somehow bypassing Dagger's ability to setup Components for injection correctly. Would it be possible to refactor the instantiation of the Component classes into an Application implementation instead?
The code above looks like it should work (outside of getting a new DaggerApplicationComponent instance with every init() call instead of a Singleton), I can't really explain why it doesn't.

Modern Akka DI with Guice

Java 8, Guice 4.0 and Akka 2.3.9 here. I am trying to figure out how to annotate my actor classes with JSR330-style #Inject annotations, and then wire them all up via Guice.
But literally every single article I have read (some examples below) either uses Scala code examples, a criminally-old version of Guice, or a criminally-old version of Akka:
Let It Crash
Scala-Guice
So, given the following Guice module:
public interface MyService {
void doSomething();
}
public class MyServiceImpl implements MyService {
#Override
public void doSomething() {
System.out.println("Something has been done!");
}
}
public class MyActorSystemModule extends AbstractModule {
#Override
public void configure() {
bind(MyService.class).to(MyServiceImpl.class);
}
}
And given the FizzActor that gets injected with a MyService:
public class FizzActor extends UntypedActor {
private final MyService myService;
#Inject
public FizzActor(MyService myService) {
super();
this.myService = myService;
}
#Override
public void onReceive(Object message) {
// .. Do fizz stuff inside here.
}
}
Then I ask: How do I rig up MyActorSystemModule to create instances of FizzActor and properly inject them with Java (not Scala!)?
Please note: FizzActor is not the only actor in my actor system!
Use Creator to create ActorRefs in provider methods of your guice module. To distinguish between the different ActorRefs, which are untyped, use annotations on your provider methods and injection points as you would any guice system. For example,
In your guice module:
#Override
protected void configure() {
bind(ActorSystem.class).toInstance(ActorSystem.apply());
bind(FizzService.class).toInstance(new FizzServiceImpl());
}
#Provides #Singleton #Named("fizzActor")
ActorRef serviceActorRef(final ActorSystem system, final FizzService fizzService) {
return system.actorOf(Props.create(new Creator<Actor>() {
#Override
public Actor create() throws Exception {
return new FizzActor(fizzService);
}
}));
}
Then to use the actor service, inject a specific ActorRef:
class ClientOfFizzActor {
#Inject
ClientOfFizzActor(#Named("fizzActor") ActorRef fizzActorRef) {..}
}
It looks cleaner if the Props.create(..) clause is a static factory method in your actor class.
Unless you are trying to bind UntypedActor to FizzActor, then you can just inject it into other classes as is:
class SomeOtherClass {
#Inject
public SomeOtherClass(FizzActor fizzActor) {
//do stuff
}
}
If you're trying to bind it to the interface, you'll need to specifically do that in the module:
public class MyActorSystemModule extends AbstractModule {
#Override
public void configure() {
bind(MyService.class).to(MyServiceImpl.class);
bind(UntypedActor.class).to(FizzActor.class);
}
}
Edit:
What about using #Named to distinguish the UntypedActor, e.g.:
class SomeOtherClass {
#Inject
public SomeOtherClass(#Named("fizzActor")UntypedActor fizzActor, #Named("fooActor") UntypedActor fooActor) {
//do stuff
}
}
Then in your module you could do the akka lookups:
public class MyActorSystemModule extends AbstractModule {
ActorSystem system = ActorSystem.create("MySystem");
#Override
public void configure() {
bind(MyService.class).to(MyServiceImpl.class);
}
#Provides
#Named("fizzActor")
public UntypedActor getFizzActor() {
return system.actorOf(Props.create(FizzActor.class), "fizzActor");
}
#Provides
#Named("fooActor")
public UntypedActor getFooActor() {
return system.actorOf(Props.create(FooActor.class), "fooActor");
}
}
Use an akka Creator:
public class GuiceCreator<T> implements Creator<T> {
Class<T> clz;
Module module;
/*Constructor*/
public T create() {
Injector injector = Guice.createInjector(this.module);
return injector.getInstance(this.clz);
}
}
Then use Props.create with your shiny new guice-based creator.
Disclaimer: I don't actually know Akka, the mentioned information comes from browsing the documentation and JavaDoc.
In case anyone found this question, you need to use IndirectActorProducer, I referred to the Spring example and changed it to use Guice instead.
/**
* An actor producer that lets Guice create the Actor instances.
*/
public class GuiceActorProducer implements IndirectActorProducer {
final String actorBeanName;
final Injector injector;
final Class<? extends Actor> actorClass;
public GuiceActorProducer(Injector injector, String actorBeanName, Class<? extends Actor> actorClass) {
this.actorBeanName = actorBeanName;
this.injector = injector;
this.actorClass = actorClass;
}
#Override
public Actor produce() {
return injector.getInstance(Key.get(Actor.class, Names.named(actorBeanName)));
}
#Override
public Class<? extends Actor> actorClass() {
return actorClass;
}
}
In the module
public class BookingModule extends AbstractModule {
#Override
protected void configure() {
// Raw actor class, meant to be used by GuiceActorProducer.
// Do not use this directly
bind(Actor.class).annotatedWith(
Names.named(BookingActor.ACTOR_BEAN_NAME)).to(
BookingActor.class);
}
#Singleton
#Provides
#Named(BookingActor.ACTOR_ROUTER_BEAN_NAME)
ActorRef systemActorRouter(Injector injector, ActorSystem actorSystem) {
Props props = Props.create(GuiceActorProducer.class, injector, BookingActor.ACTOR_BEAN_NAME, actorClass);
actorSystem.actorOf(props.withRouter(new RoundRobinPool(DEFAULT_ROUTER_SIZE)), BookingActor.ACTOR_ROUTER_BEAN_NAME);
}
}
So I have been playing around with Akka and Guice recently alot and I feel that those two don't play too well together.
What I suggest is you take a similar approach what Play is doing.
Kutschkem's answer comes closest to that.
use the ActorCreator interface
make sure you have an argumentless Creator. Don't try to do #AssisstedInject in your Creator as this will imply that you will need a new creator for every Actor that you want to create. Personally I believe that initializing this in the actor is better done through messaging.
let the ActorCreator consume an injector such that you can easily create the Actor Object within the Creator.
Here is a code example using current Akka 2.5. This is the preferred setup we chose for our Akka 2.5 deployment. For brevity I did not provide the Module, but it should be clear from the way the Members are injected, what you want to provide.
Code:
class ActorCreator implements Creator<MyActor>
#Inject
Injector injector;
public MyActor create() {
return injector.getInstance(MyActor.class);
}
}
class MyActor extends AbstractActor {
#Inject
SomeController object;
#Nullable
MyDataObject data;
public ReceiveBuilder createReceiveBuilder() {
return receiveBuilder()
.match(MyDataObject.class, m -> { /* doInitialize() */ })
.build();
}
}
class MyParentActor extends AbstractActor {
#Inject
ActorCreator creator;
void createChild() {
getContext().actorOf(new Props(creator));
}
void initializeChild(ActorRef child, MyDataObject obj) {
child.tell(obj);
}
}
Generic Akka Guice integration without dependency on Play,
keeping in mind, not the only actor should be created in the actor system.
import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import com.google.inject.AbstractModule;
import com.google.inject.Provider;
import com.google.inject.name.Names;
public abstract class AkkaGuiceModule extends AbstractModule {
protected <T extends Actor> void bindActor(Class<T> actorClass, String name) {
bind(actorClass);
Provider<ActorSystem> actorSystemProvider = getProvider(ActorSystem.class);
Provider<T> actorProvider = getProvider(actorClass);
bind(ActorRef.class)
.annotatedWith(Names.named(name))
.toProvider(ActorRefProvider.of(actorSystemProvider, actorProvider, name))
.asEagerSingleton();
}
}
Generic ActorRefProvider to create ActorRef for each Actor
import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.japi.Creator;
import com.google.inject.Provider;
import lombok.Value;
#Value(staticConstructor = "of")
public class ActorRefProvider<T extends Actor> implements Provider<ActorRef> {
private Provider<ActorSystem> actorSystemProvider;
private Provider<T> actorProvider;
private String name;
public final class ActorCreator implements Creator<Actor> {
#Override
public Actor create() {
return actorProvider.get();
}
}
#Override
public ActorRef get() {
return actorSystemProvider.get().actorOf(Props.create(new ActorCreator()), name);
}
}
Usage example
import akka.actor.ActorSystem;
import com.google.inject.Provides;
import com.typesafe.config.Config; // optional
public class MyAkkaModule extends AkkaGuiceModule {
#Provides
#Singleton
ActorSystem actorSystem(Config config) {
return ActorSystem.create("actor-system-name", config);
}
#Override
protected void configure() {
bindActor(SomeActor1.class, "actorName1");
bindActor(SomeActor2.class, "actorName2");
}
}

Categories

Resources