WHY NOT HELP ME ANY PEOPLE???
In my application i used Kotin and Kodein for Dependency injection.
I write below codes, but when run application show me Force close error!
In my application i used Kotin and Kodein for Dependency injection.
I write below codes, but when run application show me Force close error!
Application class codes:
class QuoteDiApp : Application(), KodeinAware {
override val kodein = Kodein.lazy {
bind<Database>() with singleton { DatabaseFakeImpl() }
bind<QuoteDao>() with singleton { instance<Database>().quoteDao }
bind<QuoteRepository>() with singleton { QuoteRepositoryImpl(instance()) }
bind() from provider { QuotesViewModelFactory(instance()) }
}
}
Activity class codes:
class QuoteDiActivity : AppCompatActivity(), KodeinAware {
override val kodein by closestKodein()
private val viewModelFactory: QuoteViewModelFactory by instance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_quote_di)
initializeUi()
}
private fun initializeUi() {
// Use ViewModelProviders class to create / get already created QuotesViewModel
// for this view (activity)
val viewModel = ViewModelProviders.of(this, viewModelFactory).get(QuoteViewModel::class.java)
// Observing LiveData from the QuotesViewModel which in turn observes
// LiveData from the repository, which observes LiveData from the DAO ☺
viewModel.getQuotes().observe(this, Observer { quotes ->
val stringBuilder = StringBuilder()
quotes.forEach { quote ->
stringBuilder.append("\n$quote")
}
textView_quotes.text = stringBuilder.toString()
})
// When button is clicked, instantiate a Quote and add it to DB through the ViewModel
button_add_quote.setOnClickListener {
val quote = Quote(editText_quote.text.toString(), editText_author.text.toString())
viewModel.addQuote(quote)
editText_quote.setText("")
editText_author.setText("")
}
}
}
I initialize application class into Manifest
Error message :
Caused by: org.kodein.di.Kodein$NotFoundException: No binding found for bind<QuoteViewModelFactory>() with ? { ? }
Registered in this Kodein container:
bind<QuoteDao>() with singleton { QuoteDao }
bind<QuotesViewModelFactory>() with provider { QuotesViewModelFactory }
bind<Database>() with singleton { DatabaseFakeImpl }
bind<QuoteRepository>() with singleton { QuoteRepositoryImpl }
at org.kodein.di.internal.KodeinContainerImpl.factory(KodeinContainerImpl.kt:174)
at org.kodein.di.KodeinContainer$DefaultImpls.factory$default(KodeinContainer.kt:33)
at org.kodein.di.KodeinContainer$DefaultImpls.provider(KodeinContainer.kt:80)
at org.kodein.di.internal.KodeinContainerImpl.provider(KodeinContainerImpl.kt:7)
at org.kodein.di.KodeinContainer$DefaultImpls.provider$default(KodeinContainer.kt:79)
at org.kodein.di.KodeinAwareKt$Instance$1.invoke(KodeinAware.kt:152)
at org.kodein.di.KodeinProperty$provideDelegate$1.invoke(properties.kt:39)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at nouri.mohammad.mvvm_kodeindi.with_di.ui.quotes.QuoteDiActivity.getViewModelFactory(QuoteDiActivity.kt)
at nouri.mohammad.mvvm_kodeindi.with_di.ui.quotes.QuoteDiActivity.initializeUi(QuoteDiActivity.kt:30)
at nouri.mohammad.mvvm_kodeindi.with_di.ui.quotes.QuoteDiActivity.onCreate(QuoteDiActivity.kt:24)
at android.app.Activity.performCreate(Activity.java:5231)
In your application class you use QuotesViewModelFactory class, but into you activity class you use QuoteViewModelFactory class.
This two classes is not match!
Please check your classes and match this two classes !
Related
I have an app which fetches data from an API that should be available globally within the application. This data should be fetched eagerly directly at the app start. I manage dependencies using Koin.
To achieve the desired behaviour, I created a ViewModel class that is tied to the Activity lifecycle using a AndroidViewModel instance:
class AppViewModel(
application: Application,
private val apiService: ApiService
) : AndroidViewModel(application) {
init {
getData();
Log.d("INIT", "Hooray, initialization worked!");
}
var data: List<DataObject> by mutableStateOf(listOf())
fun getData() {
viewModelScope.launch(Dispatchers.IO) {
//... consulting API service
}
}
}
I initialize this as a Koin module at the App class:
class App : Application() {
override fun onCreate() {
super.onCreate()
val appViewModel = module(createdAtStart = true) {
viewModel { AppViewModel(androidApplication(), get()) }
}
startKoin {
androidContext(this#App)
modules(/*...., */ appViewModel /*,.... */)
}
}
}
But whenever I start the application, I can see that the data request is not performed, the Log output does not appear, and the data is not present. What do I need to change so that Koin triggers the init block of the ViewModel? If this is not possible, what are alternative ways to achieve this?
Is there a way to mock ViewModel that's built is inside of a fragment? I'm trying to run some tests on a fragment, one of the fragment functions interacts with the ViewModel, I would like to run the test function and provided a mocked result for the ViewModel. Is this even possilbe?
MyFragment
class MyFragment : Fragment() {
#Inject
lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
(requireActivity().application as MyApplication).appComponent.inject(this)
super.onCreate(savedInstanceState)
}
}
Test
#RunWith(RoboeltricTestRunner::) {
#Before
fun setup() {
FragmentScenario.Companion.launchIncontainer(MyFragment::class.java)
}
}
Yeah, just mark your ViewModel open and then you can create a mock implementation on top of it.
open class MyViewModel: ViewModel() {
fun myMethodINeedToMock() {
}
}
class MockMyViewModel: MyViewModel() {
override fun myMethodINeedToMock() {
// don't call super.myMethodINeedToMock()
}
}
So, register your MockMyViewModel to the DI framework when testing.
I thought I would post this for anyone else struggling to find a solution. You'll want to use a Fragment Factory, that has a dependency on the ViewModel. Injecting the ViewModel into the fragments constructor allows the ViewModel to easliy be mocked. There are a few steps that need to be completed for a FragmentFactory but it's not that complicated once you do a couple of them.
Fragment Add the ViewModel into the constructor.
class MyFragment(private val viewModel: ViewModel) : Fragment {
...
}
FragmentFactory, allows fragments to have dependencies in the constructor.
class MyFragmentFactory(private val viewModel: MyViewModel) : FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
return when(className) {
MyFirstFragment::class.java.name -> {
MyFragment(viewModel)
}
// You could use this factory for multiple Fragments.
MySecondFragment::class.java.name -> {
MySecondFragment(viewModel)
}
// You also don't have to pass the dependency
MyThirdFragment::class.java.name -> {
MyThirdFragment()
}
else -> super.instantiate(classLoader, className)
}
}
}
Main Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Create your ViewModel
val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
// create the FragmentFactory and the viewModel dependency.
supportFragmentManager.fragmentFactory = MainFragmentFactory(viewModel)
// FragmentFactory needs to be created before super in an activity.
super.onCreate(savedInstanceState)
}
}
Test
#RunWith(RobolectricTestRunner::class)
class MyFragmentUnitTest {
#Before
fun setup() {
val viewModel: MainViewModel = mock(MyViewModel::class.java)
...
}
}
Recently I have started to learn kodein dependency injection here I'm trying to load data into ArrayList and then inject that ArrayList into desired service or activity however I'm unable to do this and facing run time crashes any guidance will be appreciated.!
My Service Class:
class NotificationAccessibilityService() : AccessibilityService(),KodeinAware {
override val kodein: Kodein = Kodein.lazy{
import(AppModules().appModule(applicationContext))
}
val logger by instance<Logger>()
fun insertdata(){
//trying to insert data into arraylist
logger.list = ArrayList()
logger.list.add(mNodeInfo)
}
}
AppModules Class:
class AppModules() {
fun appModule(context:Context) = Kodein.Module{
bind<Logger>() with singleton { AndroidLogger() }
}
}
Logger interface:
interface Logger {
var list:ArrayList<AccessibilityNodeInfo>
}
Logger Class:
class AndroidLogger() : Logger {
override var list: ArrayList<AccessibilityNodeInfo>
get() = TODO("not implemented")
set(value) {}
}
In my Activity I'm extending kodeinaware and then trying to access that ArrayList from kodein however app is crashing:
class NormalCopy : AppCompatActivity(),KodeinAware {
override val kodein: Kodein by closestKodein()
val logger by kodein.instance<Logger>()
var accesslist = logger.list
}
Sorry, maybe I misunderstood you but it looks like you have to just remove TODO from your Logger implementation
class AndroidLogger() : Logger {
override var list: ArrayList<AccessibilityNodeInfo> = arrayListOf()
}
I make project with mvvm pattern with koin for DI, but i always have No definition found repository
I alredy define repository in module app before viewmodel, but i get some error
Gradle app
// Koin for Android
implementation "org.koin:koin-android:$rootProject.koin_version"
// Koin Android Scope features
implementation "org.koin:koin-androidx-scope:$rootProject.koin_version"
// Koin Android ViewModel features
implementation "org.koin:koin-androidx-viewmodel:$rootProject.koin_version"
module
val dataModule = module {
//remoteData
single { AppRemoteData() }
//repository
single{ AppRepository(get()) as AppDataSource}
// viewmodel
viewModel{ ListHomeViewModel(get()) }
viewModel { LoginViewModel(get()) }
define module
val myAppModule = listOf(appModule, dataModule)
in app
startKoin {
androidLogger(Level.DEBUG)
androidContext(this#MainApp)
modules(myAppModule)
}
Repository class
class AppRepository(val appRemoteDataSource: AppRemoteData) : AppDataSource {
override fun loginAccount(email: String, password: String) : LiveData<String> {
val data = MutableLiveData<String>()
appRemoteDataSource.loginAccount(email,password,object : AppDataSource.LoginCallback{
override fun onSucces(id: String?) {
//berhasil
data.postValue(id)
}
override fun onFailed(message: String?) {
data.postValue(message)
d(message)
}
override fun onFailure(message: String?) {
data.postValue(message)
d(message)
}
})
return data
}
AppRemoteData
class AppRemoteData {
val ref = FirebaseDatabase.getInstance().getReference("user")
var auth = FirebaseAuth.getInstance()
fun loginAccount(email: String, password: String, callback: AppDataSource.LoginCallback) {
auth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener {
task ->
if(task.isComplete){
callback.onSucces(task.result?.user?.providerId)
}else{
callback.onFailed(task.exception?.localizedMessage)
}
}
}}
here error message
if in your ViewModel has an interface as parameter.
class MyViewModel(val interfaceName: InterfaceName) : ViewModel()
In your module definition use singleBy<> Instead of Single().
singleBy<InterfaceName,InterfaceNameImplementation>()
Finally in for your ViewModel
viewModel { MyViewModel(get()) }
This worked for me in Koin 2.0
Hope it helps :)
The error message is telling you that Koin couldn't create a LoginViewModel instance for you, because it would've had to provide an instance of AppRepository during its creation, but you didn't tell it how to do that.
My guess is that you've accidentally used the AppRepository type in the LoginViewModel constructor directly, instead of using your AppDataSource that you've bound the repository instance to in your module.
So if you have something like this, that would require an AppRepository specifically:
class LoginViewModel(val dataSource: AppRepository) : ViewModel()
You should replace it with this, where you're only asking Koin for an AppDataSource, which you did configure it to be able to provide:
class LoginViewModel(val dataSource: AppDataSource) : ViewModel()
I'm new at Koin. I have set all the stuff and is working. But I'm getting some problems when I'm trying to inject interactor and presenter at the same time. That not sure it is possible.
This is my Module
val applicationModule = module(override = true) {
factory{VoucherImpl(get())}
factory<VoucherContract.Presenter> { (view: VoucherContract.View) -> VoucherPresenter(view, get()) }
}
This is my Activity where inject the presenter
private val presenter: VoucherContract.Presenter by inject { parametersOf(this)}
This is my Presenter
class VoucherPresenter (private var view: VoucherContract.View?, private var mCodeRechargeInteract : VoucherImpl) : VoucherContract.Presenter, VoucherContract.Callback, KoinComponent {
override fun create() {
view?.initView()
view?.showProgress()
mCodeRechargeInteract.run()
}
.
.
.
Interactor class
class VoucherImpl(private var mCallback: VoucherContract.Callback?) : AbstractInteractor() {
.
.
.
contract
interface VoucherContract {
interface Presenter {
fun create()
fun destroy()
fun checkIfShoppingCartHaveItems()
fun addVoucherToShoppingCart(voucherProduct: Product)
fun onItemClick(product: Product)
}
interface Callback {
fun onResponseVouchers(vouchers: List<Product>?)
fun onError()
}
}
With this code I get
No definition found for 'xxx.voucher.VoucherContract$Callback' has been found. Check your module definitions.
Then, I try to put it in the module and I can't do it because I get: a Type mismatch. Required VoucherContract.Callback Found VoucherImpl
factory<VoucherContract.Callback> { (callBack: VoucherContract.Callback) -> VoucherImpl(callBack) }
You have a circular dependency that's why this doesn't work.
VoucherImpl(VoucherContract.Callback) and VoucherPresenter(View, VoucherImpl):VoucherContract.Callback
There are multiple ways out of this predicament.
I would recommend the following changes:
The VoucherImpl should not have the constructor parameter VoucherContract.Callback. This callback should be the parameter of a method something like this:
class VoucherImpl : AbstractInteractor(){
fun listen(VoucherContract.Callback){...}
}
This way the dependency becomes one way and you can inject them.