Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger 2 - Injecting interface in implementation class - "Attempt to invoke interface method on a null object reference"

I am always getting null pointer exception when I try to inject interface in implementation class. here is my code:

Getting:

Nullpointer error in LoginManagerImpl class at line:
@Override
public String getLoginResponse(String request) {
return networkManager.getLoginResponse(request);
}

here networkManager is always null, can you please look into the code.

Below is my source code:

public class LoginActivity extends Activity {

    @Inject
    NetworkManager networkManager;

    @Inject
    LoginManager loginManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getApplicationComponent().inject(this);

        loginManager.getLoginResponse("valid Login Request");
    }
}

public interface NetworkManager
{
    String getLoginResponse(String request);
    String getUserDetailResponse(String request);
    String getABCResponse(String request);
}

public interface LoginManager
{
    String getLoginResponse(String request);
}

public class LoginManagerImpl implements LoginManager {
    @Inject
    NetworkManager networkManager;

    public LoginManagerImpl(Context context)
    {

    }

    @Override
    public String getLoginResponse(String request) {
        return networkManager.getLoginResponse(request);
    }
}

public class NetworkManagerImpl implements NetworkManager {

    public NetworkManagerImpl(Context context)
    {

    }
    @Override
    public String getLoginResponse(String request) {
        return "valid login response fetched from server";
    }

    @Override
    public String getUserDetailResponse(String request) {
        return "valid user deails";
    }

    @Override
    public String getABCResponse(String request) {
        return "valid ABC request response";
    }
}

@Module
public class AppModule {

    DaggerApplication application;

    public AppModule(DaggerApplication application) {
        this.application = application;
    }

    @Provides
    @Singleton
    DaggerApplication provideDaggerApplication() {
        return application;
    }

    @Provides
    @Singleton
    public Context provideApplicationContext()
    {
        return application.getApplicationContext();
    }

    @Provides
    @Singleton
    public LoginManager providesLoginManager(Context context)
    {
        return new LoginManagerImpl();
    }

    @Provides
    @Singleton
    public LoyaltyCardManager providesLoyaltyCardManager(Context context)
    {
        return new LoyaltyCardManagerImpl(context);
    }

    @Provides
    @Singleton
    public NetworkManager providesNetworkManager(Context context)
    {
        return new NetworkManagerImpl(context);
    }
}

@Singleton
@Component(
        modules = {
                com.dagger.component.AppModule.class
        })
public interface ApplicationComponent {
    void inject (LoginActivity activity);

    NetworkManager getNetworkManager();
    LoginManager getLoginManager();
}
like image 717
Piyush Avatar asked Oct 12 '15 11:10

Piyush


People also ask

How do you get an interface in Dagger 2?

We can simply use @Inject at constructor! TL DR; If you have provide methods which just call constructor of implementation classes to inject interfaces, use @Binds annotation instead to get rid of boilerplate code in your dagger module.

What is dependency injection dagger?

The term dependency injection context is typically used to describe the set of objects which can be injected. In Dagger 2, classes annotated with @Module are responsible for providing objects which can be injected. Such classes can define methods annotated with @Provides .


2 Answers

There are two ways for you to let Dagger inject dependencies like the networkManager field into LoginManagerImpl.

  1. Annotate its constructor with @Inject and change providesLoginManager(Context) to

    @Provides @Singleton
    LoginManager providesLoginManager(LoginManagerImpl manager) {
      return manager;
    }
    

    That will mean Dagger will provide the LoginManagerImpl, and your @Provides method binds LoginManager to that.

  2. If you want to instantiate LoginManagerImpl yourself in providesLoginManager(Context), then also request a MembersInjector<LoginManagerImpl> and use it to inject fields like networkManager:

    @Provides @Singleton
    LoginManager providesLoginManager(Context context, MembersInjector<LoginManagerImpl> membersInjector) {
      LoginManagerImpl manager = new LoginManagerImpl(context);
      membersInjector.injectMembers(manager);
      return manager;
    }
    

For your example, I'd recommend option #1. Using MembersInjector is really useful for classes that you cannot let Dagger instantiate, like Android Activities.

like image 142
netdpb Avatar answered Sep 29 '22 12:09

netdpb


Field injection is not automatic. You need to provide those dependencies through the constructor, or you need to invoke member injection on the class via the component.

public class LoginActivity extends Activity {    
    @Inject
    NetworkManager networkManager;

    @Inject
    LoginManager loginManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getApplicationComponent().inject(this);

        loginManager.getLoginResponse("valid Login Request");
    }
}

public interface NetworkManager {
    String getLoginResponse(String request);
    String getUserDetailResponse(String request);
    String getABCResponse(String request);
}

public interface LoginManager {
    String getLoginResponse(String request);
}

@Singleton
public class LoginManagerImpl implements LoginManager {
    private final NetworkManager networkManager;

    @Inject
    public LoginManagerImpl(NetworkManager networkManager) {    
        this.networkManager = networkManager;
    }

    @Override
    public String getLoginResponse(String request) {
        return networkManager.getLoginResponse(request);
    }
}

@Singleton
public class NetworkManagerImpl implements NetworkManager {
    @Inject
    NetworkManagerImpl(){
    }

    @Override
    public String getLoginResponse(String request) {
        return "valid login response fetched from server";
    }

    @Override
    public String getUserDetailResponse(String request) {
        return "valid user deails";
    }

    @Override
    public String getABCResponse(String request) {
        return "valid ABC request response";
    }
}

@Module
public class AppModule {
    DaggerApplication application;

    public AppModule(DaggerApplication application) {
        this.application = application;
    }

    @Provides
    DaggerApplication provideDaggerApplication() {
        return application;
    }

    @Provides
    Context provideApplicationContext() {
        return application.getApplicationContext();
    }
}

@Module
public abstract class ManagerModule {    
    @Binds
    abstract LoginManager loginManager(LoginManagerImpl impl);

    @Binds
    abstract LoyaltyCardManager loyaltyCardManager(LoyaltyCardManagerImpl impl);

    @Binds
    abstract NetworkManager networkManager(NetworkManagerImpl impl); 
}

@Singleton
@Component(
        modules = {
                com.acme.di.component.AppModule.class,
                com.acme.di.component.ManagerModule.class
        })
public interface ApplicationComponent {
    void inject (LoginActivity activity);

    NetworkManager getNetworkManager();
    LoginManager getLoginManager();
}
like image 26
EpicPandaForce Avatar answered Sep 29 '22 12:09

EpicPandaForce