I am trying to create dependencies using dagger 2 with kotlin. I am getting this error on runtime
Caused by: java.lang.IllegalStateException: pk.telenorbank.easypaisa.di.modules.RetrofitApiModule must be set
at pk.telenorbank.easypaisa.di.DaggerAppComponent$Builder.build(DaggerAppComponent.java:54)
at pk.telenorbank.easypaisa.EasypaisaApp.onCreate(EasypaisaApp.kt:22)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1015)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4834)
at android.app.ActivityThread.access$1600(ActivityThread.java:168)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1440)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5659)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:822)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:712)
Here is dependency graph.
#Module(includes = arrayOf(NetworkModule::class))
class RetrofitApiModule(val retrofitMvpApi: RetrofitMvpApi) {
#Provides
#Singleton
fun provideMvpApi(): RetrofitMvpApi {
return retrofitMvpApi
}
}
Here is RetorfitMvpApi
#Singleton
class RetrofitMvpApi #Inject constructor(retrofit: Retrofit) : MvpApi {
var retrofitService: RetrofitService
init {
retrofitService = retrofit.create(RetrofitService::class.java)
}
override fun login(source: String) =
retrofitService.getPosts(source, Constants.API_KEY)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map { rs -> rs }
.doOnError { t -> t.printStackTrace() }
interface RetrofitService {
#POST
fun getPosts(#Query("sources") source: String,
#Query("apiKey") key: String): Observable<WSResponse>
}
}
Here is the component.
#Singleton
#Component(modules = arrayOf(AppModule::class, RetrofitApiModule::class))
interface AppComponent {
fun loginComponent(loginModule: LoginModule) : LoginComponent
}
What am i doing wrong here? I am using dagger 2.15
Dagger will automatically create Module for dependency graph if that Module has default constructor. If you use custom constructor then you have to provide the Module when you are building the graph.
For java code:
#Module
public class ContextModule {
private final Context context;
public ContextModule(Context context) {
this.context = context;
}
#Provides
#GithubApplicationScope
#ApplicationContext
public Context provideContext(){
return context.getApplicationContext();
}
}
Building graph:
githubApplicationComponent = DaggerGithubApplicationComponent.builder()
.contextModule(new ContextModule(this))
// not needed as Dagger automatically generate module class with no arguments
//.networkModule(new NetworkModule())
.build();
Your provideMvpApi method should take an instance of retrofitMvpApi and return its interface:
#Module(includes = arrayOf(NetworkModule::class))
class RetrofitApiModule() {
#Provides
#Singleton
fun provideMvpApi(val retrofitMvpApi: RetrofitMvpApi): MvpApi {
return retrofitMvpApi
}
}
Related
I want to know how to inject Presenter in activity using code Following are details
Following is error message:
Error:(12, 46) error: cannot find symbol class DaggerCategoryPresenterComponent
Error:(9, 46) error: cannot find symbol class DaggerNetComponent
Error:(18, 10) error: [Dagger/MissingBinding] com.headytest.android.category_listing.CategoryContract.CategoryPresenter cannot be provided without an #Provides-annotated method.
com.headytest.android.category_listing.CategoryContract.CategoryPresenter is injected at
com.headytest.android.MainActivity.categoryPresenter
com.headytest.android.MainActivity is injected at
com.headytest.android.dagger_component.NetComponent.inject(com.headytest.android.MainActivity)
Following are modules
#Module
public class NetworkModule {
String baseURL;
public NetworkModule(String baseURL) {
this.baseURL = baseURL;
}
#Provides
#Singleton
Cache provideHttpCache(Application application) {
int cacheSize = 10 * 1024 * 1024;
Cache cache = new Cache(application.getCacheDir(), cacheSize);
return cache;
}
#Provides
#Singleton
Gson provideGson() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return gsonBuilder.create();
}
#Provides
#Singleton
OkHttpClient provideOkhttpClient(Cache cache) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.cache(cache);
client.addInterceptor(interceptor);
return client.build();
}
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
try {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(baseURL)
.client(okHttpClient)
.build();
} catch (Exception e) {
e.printStackTrace();
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(Constants.BASE_URL)
.client(okHttpClient)
.build();
}
}
}
ApplicationModule
#Module
public class ApplicationModule {
Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application providesApplication() {
return application;
}
}
CategoryContractModule
#Module
public class CategoryContractModule {
public CategoryContractModule() {
}
#Provides
#AScope
CategoryContract.CategoryPresenter providesCategoryPresenter(CategoryPresenterImpl categoryPresenter) {
return (CategoryContract.CategoryPresenter)categoryPresenter;
}
}
Following are components:
#Singleton
#Component(modules = {ApplicationModule.class, NetworkModule.class})
public interface NetComponent {
void inject(MainActivity activity);
}
CategoryPresenterComponent:
#AScope
#Component(dependencies = NetComponent.class, modules =
{CategoryContractModule.class})
public interface CategoryPresenterComponent {
void inject(MainActivity activity);
}
AScope
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface AScope {
}
Following is the code in MainActivity:
#Inject
CategoryPresenter categoryPresenter;
#Inject
Retrofit retrofit;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerCategoryPresenterComponent.builder()
.categoryContractModule(new CategoryContractModule())
.netComponent(((App) getApplicationContext()).getNetComponent())
.build()
.inject(this);
}
CategoryPresenterImpl
public class CategoryPresenterImpl implements CategoryContract.CategoryPresenter {
#Inject
public CategoryPresenterImpl() {
}
#Override
public void onStart() {
}
#Override
public void onStop() {
}
#Override
public void getCategoryLiast() {
}
}
I assume current error will be easy to fix, because it very much looks like this issue.
You've instructed Dagger how to provide #AScope CategoryContract.CategoryPresenter but in activity you request Dagger to inject CategoryContract.CategoryPresenter. Dagger is confused at this point, because those two things do not match.
What you have to do, is simply add #AScope to categoryPresenter:
#Inject
#AScope
CategoryPresenter categoryPresenter;
I've checked out your project. The problem was in NetComponent.java:
#Singleton
#Component(modules = {ApplicationModule.class, NetworkModule.class})
public interface NetComponent {
void inject(MainActivity activity);
}
The issue is in line void inject(MainActivity activity). Why is this an issue? Because by the time you construct NetComponent in App, dagger analyzes MainActivity for #Inject annotated field and sees CategoryPresenter. This means, that at this point Dagger should find appropriate provider/binder method in order to be able to inject MainActivity as declared inside this interface.
But it cannot find such a provider/binder method, because it is declared in an another component - CategoryPresenterComponent and this component (NetComponent) is not connected with CategoryPresenterComponent anyhow (subcomponents or component dependencies), thus bindings are not exposed.
Simply removing that line will make your build successful:
#Singleton
#Component(modules = {ApplicationModule.class, NetworkModule.class})
public interface NetComponent {
}
For resolving "Error:cannot access Nullable" refer to this thread, which suggest applying compile 'com.google.code.findbugs:jsr305:3.0.2' to your gradle file.
After doing this you'll overcome that error message and will stumble on retrofit2.Retrofit cannot be provided without an #Inject constructor or an #Provides-annotated method, which has the same symptoms as CategoryPresenter had.
To overcome this issue you have to add a Retrofit provider method inside NetComponent (or to remove #Inject Retrofit retrofit from activity):
#Singleton
#Component(modules = {ApplicationModule.class, NetworkModule.class})
public interface NetComponent {
Retrofit providesRetrofit();
}
After doing this you'll be able to run the app, but you'll end up in a NPE in MainActivity, because of this line:
.categoryContractModule(new CategoryContractModule(retrofit.create(HeadyAPI.class)))
You are referring to retrofit object, which is not yet initialized.
Long story short, your initial question has mutated a couple of times and in fact you had a few problems in your code. Still you have a problem there, because you are trying to use retrofit in order to construct a component, that was supposed to inject the retrofit for you.
When trying to inject field variables using dagger I'm getting null. Here are the files. Some are in Java and some in Kotlin
App.java
public class App extends DaggerApplication{
#Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().application(this).build();
}
}
AppComponent.kt
#Singleton
#Component(modules = arrayOf(
NetworkModule::class,
ApplicationModule::class,
AndroidSupportInjectionModule::class
))
interface AppComponent : AndroidInjector<TBApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): AppComponent.Builder
fun build(): AppComponent
}
}
NetworkModule.kt
#Module
class NetworkModule {
#Provides
#Singleton
fun provideOkHttpClient(): OkHttpClient {
val builder = OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
builder.addInterceptor(interceptor).build()
}
return builder.build()
}
#Singleton
#Provides
fun provideRetrofit(client: OkHttpClient): Retrofit {
val retrofit = Retrofit.Builder()
.baseUrl(BaseApi.SITE_ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
return retrofit
}
}
// Repository where injection should be done
class Repository {
private var examsService: BlogExamsService
#Inject
var retrofit: Retrofit? = null
init {
// retrofit is null here
examsService = retrofit?.create(BlogExamsService::class.java)!!
}
}
Field injection won't work as you do not run inject() method.
To make it work with your approach you should call in you Repository class:
App.self.getComponent().inject(this)
Where:
self is static instance of your application
getComponent() public getter for ApplicationComponent
Though I would not recommend it in your case, it is a misuse of DI framework.
You should create RepositoryModule and #Provide instance of Repository the same as you have done with NetworkModule.
Change your Repository to:
class Repository {
private var examsService: BlogExamsService
#Inject
constructor(retrofit: Retrofit) {
examsService = retrofit.create(BlogExamsService::class.java)!!
}
}
I have a simple situation here which I am not able to get around (since today is my second day with dagger).
I have a RepositoryManager class the intent of which is to house all repositories like(StudentRepo , TeacherRepo etc etc)
This is my RepositoryManager
public class RepositoryManager {
private static RepositoryManager INSTANCE = null;
#Inject
StudentRepo studentRepo;
#Inject
TeacherRepo teacherRepo;
private final List<Repository> repositories;
private Context context;
#Inject
public RepositoryManager(Context context) {
this.repositories = new ArrayList<>();
this.context = context;
addRepositories();
}
private void addRepositories() {
addRepository(studentRepo);
addRepository(teacherRepo);
}
I understand why my studentRepo and teacherRepo are null here. It is because I have not asked Dagger to fetch them for me . I believe I am missing some very important aspect of Dagger here in which we can explicitly fetch instances of our desired objects.
The StudentRepo btw has its own module and I can easily pull it out in an activity.
My question is just how to fetch instances in a non activity class.
my AppComponent
#Singleton
#Component(modules =
{
AndroidInjectionModule.class,
ApplicationModule.class,
ActivityBindingModule.class,
RepositoryModule.class})
public interface AppComponent extends AndroidInjector<ChallengerHuntApplication> {
RepositoryManager exposeRepositoryManager();
#Component.Builder
interface Builder {
#BindsInstance
AppComponent.Builder application(Application application);
AppComponent build();
}
}
RepositoryModule
#Module(includes = SystemRepositoryModule.class)
public class RepositoryModule {
}
Student Repository Module
#Module
public class StudentRepositoryModule {
#Singleton
#Provides
#Local
public StudentDataSource provideStudentLocalDataSource(Context context) {
return new StudentLocalDataSource(context);
}
#Singleton
#Provides
#Remote
public StudentDataSource provideStudentRemoteDataSource(Context context) {
return new StudentRemoteDataSource();
}
}
I'm having a problem with Dagger 2.
I want a general NetworkModule where I can share my retrofit, etc.. and then later on I want subcomponents so that per flow I have different Retrofit Interfaces for example login, ...
My setup right now is:
#Module
public class NetworkModule {
#Provides
#Singleton
#Named("Default")
Retrofit provideRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("myUrl")
.build();
return retrofit;
}
}
I also have a ApplicationModule ( I don't know if this is the correct way to have a ApplicationModule ).
#Module
public class ApplicationModule {
private Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application providesApplication() {
return this.application;
}
}
And last but not least my Component that binds the two together:
#Singleton
#Component(modules = {ApplicationModule.class, NetworkModule.class})
public interface NetworkComponent {
Retrofit provideRetrofit();
void inject(MainActivity activity);
}
Now I don't see the problem but when I do:
((MyApplication) getApplication()).getNetworkComponent().inject(this);
Where this is created as:
DaggerNetworkComponent.builder().applicationModule(new ApplicationModule(this)).build();
I can't compile and I get the error:
retrofit2.Retrofit cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
Add #Named("Default") to your Retrofit provideRetrofit(); in NetworkComponent
You can refer to the following classes for the usage of dagger 2 and retrofit :
Injector.java
public class Injector {
// Singleton instance
private static Injector instance;
// components
private AppComponent appComponent;
// Private constructor to make it singleton
private Injector() {
}
/**
* Get the singleton Injector instance
*
* #return Injector
*/
private static Injector instance() {
if (instance == null) {
instance = new Injector();
}
return instance;
}
/**
* Creates application component which used of injection later.
*
* #param application
*/
public static void createApplicationComponent(Application application) {
if (instance().appComponent == null) {
instance().appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}
}
/**
* Returns the component for injection.
*
* #return
*/
public static AppComponent component() {
return instance().appComponent;
}
}
AppComponent.java
#Singleton
#Component(modules = {
AppModule.class,
ApiModule.class,
})
public interface AppComponent {
// Other utility classes for injection
}
ApiModule.java which similar to your network module class
#Module
public class ApiModule {
#Provides
#Singleton
public Retrofit provideApi(
OkHttpClient client,
Converter.Factory converterFactory,
CallAdapter.Factory callAdapterFactory) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Config.API_BASE_URL)
.client(client)
.addConverterFactory(converterFactory)
.addCallAdapterFactory(callAdapterFactory)
.build();
return retrofit;
}
}
AppModule.java
#Module
public class AppModule {
private Application application;
public AppModule(Application application) {
this.application = application;
}
#Provides
#Singleton
public Application provideApplication() {
return application;
}
#Provides
#Singleton
public Context provideContext() {
return application;
}
}
later in your activity class you can use Injector.component().inject(this); to inject dagger 2 dependencies
For more clarification you can refer this github repo
I want to return type with generic to be exposed by sub-graphs, the problem is in auto-generated java-classes, I tried to do something, but the one way to solve it is to remove generic type from AppComponent and return simple object. Is there more "right" approach?
Here is the AppComponent
#Singleton
#Component(modules = arrayOf(ApplicationModule::class))
interface ApplicationComponent {
fun inject(activity: BaseActivity<MvpView, MvpPresenter<MvpView>>)
//...
fun dataBase(): Database<Realm>
}
here is function in the ApplicationModule
#Provides #Singleton fun provideDatabase(#AppContext context: App): Database<Realm> {
Realm.init(context)
val config = RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.name("db")
.build()
Realm.setDefaultConfiguration(config)
return RealmDatabase(Realm.getDefaultInstance())
}
Then I want to receive my Database
#Provides #ActivityScope fun provideDich(database: Database<Realm>) = Someobject(database)
And then I see log that says:
**Error:com.test.data.storage.Database<? extends io.realm.Realm> cannot be provided without an #Provides-annotated method.**
Because dagger2 generates factories like this and there is java masks
public final class Logout_Factory implements Factory<Logout> {
private final MembersInjector<Logout> logoutMembersInjector;
private final Provider<SessionStorage.CloudStorage> arg0Provider;
private final Provider<Database<? extends Realm>> arg1Provider;
public Logout_Factory(
MembersInjector<Logout> logoutMembersInjector,
Provider<SessionStorage.CloudStorage> arg0Provider,
Provider<Database<? extends Realm>> arg1Provider) {
assert logoutMembersInjector != null;
this.logoutMembersInjector = logoutMembersInjector;
assert arg0Provider != null;
this.arg0Provider = arg0Provider;
assert arg1Provider != null;
this.arg1Provider = arg1Provider;
}
#Override
public Logout get() {
return MembersInjectors.injectMembers(
logoutMembersInjector, new Logout(arg0Provider.get(), arg1Provider.get()));
}
public static Factory<Logout> create(
MembersInjector<Logout> logoutMembersInjector,
Provider<SessionStorage.CloudStorage> arg0Provider,
Provider<Database<? extends Realm>> arg1Provider) {
return new Logout_Factory(logoutMembersInjector, arg0Provider, arg1Provider);
}
}
I had the same problem and I found out the solution.
You need to declare the #JvmWildcard in your #Provide method return type.
#Provides
#Singleton
fun provideDatabase(#AppContext context: App): Database<#JvmWildcard Realm> {
Realm.init(context)
val config = RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.name("db")
.build()
Realm.setDefaultConfiguration(config)
return RealmDatabase(Realm.getDefaultInstance())
}