Reputation: 7459
I have a very simple set up. But I am unable to perform any networking within my the fragment that I am using Kodein
in.
The fragment inflates and handles all interaction but the networking layer dependency is somehow missing because I receive: NotFoundException: No binding found for bind<String>() with ?<Fragment>()
every time I hit the button that validates the username/password on this LoginFragmnet.
I don’t understand this error because my Fragment
class doesn’t have any String
in it’s args/constructor? Or even depend on a String
.
My Application class:
...
override val kodein = Kodein.lazy {
import(androidXModule(this@MyApplication))
bind() from singleton { MyLoginApi(instance(), instance()) }
bind<LoginDataSource>() with singleton { LoginService(instance()) }
bind<LoginRepository>() with singleton { LoginRepositoryImpl(instance()) }
}
...
My API
interface MyLoginApi {
@POST("Account/Login")
@FormUrlEncoded
fun login(
@Field("username") username: String,
@Field("password") password: String,
@Field("sessionType") sessionType: String
): Call<BaseApiResponse<Login>>
@GET("Account/Logout")
fun logout(
@Query("token") token: String
): Call<BaseApiResponse<Any>>
companion object {
operator fun invoke(
baseUrl: String,
cache: Cache
): MyLoginApi {
val okHttpClient = OkHttpClient.Builder()
.cache(cache)
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.build()
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl(baseUrl)
.addConverterFactory(JacksonConverterFactory.create())
.build()
.create(MyLoginApi::class.java)
}
}
}
LoginService (The Datasource)
class LoginService(private val api: MyLoginApi) : LoginDataSource {
private val bus = EventBus.getDefault()
init {
bus.register(this)
}
override fun requestLogin(username: String, password: String) {
val call = api.login(username, password, "mobile")
call.enqueue { result ->
when (result) {
is Result.Success -> {
result.response.body()?.let {
bus.post(LoginResponse(it, username))
} ?: bus.post(LoginResponse(IOException("System Error!")))
}
is Result.Failure -> {
bus.post(LoginResponse(IOException(result.error)))
}
}
}
}
An Abstract Fragment class:
abstract class InjectionFragment : Fragment(), KodeinAware {
final override val kodeinContext = kcontext<Fragment>(this)
final override val kodein: Kodein by kodein()
final override val kodeinTrigger: KodeinTrigger?
get() = if (BuildConfig.DEBUG) KodeinTrigger() else super.kodeinTrigger
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
kodeinTrigger?.trigger()
}
}
I know this scenario is very trivial, but I’m trying to do this as a test before integrating in the rest of the project. The Fragment
displays, but for some reason once I try to do a data request it crashes with an error saying:
org.kodein.di.Kodein$NotFoundException: No binding found for bind<String>() with ?<Fragment>().? { ? }
module androidModule {
bind<String>(tag = "packageResourcePath") with contexted<Context>().provider { String }
bind<String>(tag = "packageCodePath") with contexted<Context>().provider { String }
bind<String>(tag = "packageName") with contexted<Context>().provider { String }
}
Update:
When single stepping through my Fragment it the debugger says that the Repository
is "Lazy value not initialized yet.”
Upvotes: 1
Views: 3479
Reputation: 7459
I ended up going with Hilt
it's build upon Dagger
and easy to use and google is doing a good job maintaining it. I'd suggest others to use Hilt
as well.
Upvotes: -1
Reputation: 568
You should bind the String "class" with an instance of itself inside the kodein.lazy initialization before you bind classes that use instances of String
override val kodein = Kodein.lazy {
import(androidXModule(this@MyApplication))
bind() from singleton {String()} // ---> Add THIS line
bind() from singleton { MyLoginApi(instance(), instance()) }
bind<LoginDataSource>() with singleton { LoginService(instance()) }
bind<LoginRepository>() with singleton { LoginRepositoryImpl(instance()) }
}
...
You can do that because in this case the constructor of the class String is public, but do not take it as final solution since this is not possible with other data types like Integer, as its constructor is private.
The real problem may lay in this binding
bind() from singleton { MyLoginApi(instance(), instance()) }
The instances inside MyLoginApi are instances of strings, and since you do not have an implementation of MyLoginApi interface, the Kodein library directly tries to find where those instances are defined. So better create MyLoginApiImpl and then substitute:
bind() from singleton { MyLoginApi(instance(), instance()) }
with
bind<MyLoginApi>() with singleton { MyLoginApiImpl(instance())}
Upvotes: 1