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

13,645

Solution 1

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.

Solution 2

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();
}
Share:
13,645
Piyush
Author by

Piyush

Updated on June 21, 2022

Comments

  • Piyush
    Piyush about 2 years

    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();
    }
    
  • Dyno Cris
    Dyno Cris almost 4 years
    Oh! Really, it helped me. Why you downvote it? I upvoted! Android Studio shows uncorrect error in this case