android doesn't go into the coroutine - android

Good afternoon, I ran into the following problem: I'm trying to get a list of stores, but I get null in response.
judging by the debag, it does not even go into the coroutine in DataAccessStrategy.
A similar piece of code elsewhere in the app works, I can't figure out what the problem is.
LoginViewModel
class LoginViewModel #ViewModelInject constructor(private val remoteDataSource: OrderRemoteDataSource,
private val ordersRepository: OrdersRepository) :
ViewModel() {
var shops = ordersRepository.getShops()
fun showDialog(){
var shopList: List<Shop>? = shops.value?.data
var names:List<String?> = shopList!!.map{it.customName}
MaterialAlertDialogBuilder(LOGIN_ACTIVITY)
.setTitle("Выбрать магазин")
.setItems(names.toTypedArray()) { dialog, which ->
Log.d("LOGIN VIEW MODEL", "selected")
}
.show()
}
DataAccessStrategy
fun <T, A> performGetOperation(
databaseQuery: () -> LiveData<T>,
networkCall: suspend () -> Resource<A>,
saveCallResult: suspend (A) -> Unit
): LiveData<Resource<T>> =
liveData(Dispatchers.IO) {
emit(Resource.loading())
val source = databaseQuery.invoke().map { Resource.success(it) }
emitSource(source)
val responseStatus = networkCall.invoke()
if (responseStatus.status == SUCCESS) {
saveCallResult(responseStatus.data!!)
} else if (responseStatus.status == ERROR) {
emit(Resource.error(responseStatus.message!!))
emitSource(source)
}
}
OdredsRepository
class OrdersRepository #Inject constructor(
private val remoteDataSource: OrderRemoteDataSource,
private val localDataSource: OrdersDao
) {
fun getShops() = performGetOperation(
databaseQuery = { localDataSource.getAllShops() },
networkCall = { remoteDataSource.getShops() },
saveCallResult = { localDataSource.insertShop(it.results) }
)
}
OrderRemoteDataSource
class OrderRemoteDataSource #Inject constructor(
private val orderService: OrderService
) : BaseDataSource() {
suspend fun getShops() = getResult { orderService.getAllShops() }
OrderService
interface OrderService {
#GET("shops/all")
suspend fun getAllShops(): Response<ResultShops>
companion object {
const val MAUMarket_API_URL = "MYURL.com"
}
}
E/AndroidRuntime: FATAL EXCEPTION: main
Process: ru.gkomega.navigation, PID: 4349
java.lang.NullPointerException
at ru.gkomega.maumarket.ui.login.LoginViewModel.showDialog(LoginViewModel.kt:36)
at ru.gkomega.maumarket.ui.login.LoginFragment$onViewCreated$1.onClick(LoginFragment.kt:50)
at android.view.View.performClick(View.java:7259)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7236)
at android.view.View.access$3600(View.java:801)
at android.view.View$PerformClick.run(View.java:27892)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
I will be glad of any help, thanks

private fun obser() {
mViewModel.shopsLiveData.observe(viewLifecycleOwner, Observer {
when (it.status) {
Resource.Status.SUCCESS -> {
//binding.progressBar.visibility = View.GONE
main.isRefreshing = false
if (!it.data.isNullOrEmpty()) Log.i("LEL",it.data.toString())
}
Resource.Status.ERROR ->
Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT).show()
Resource.Status.LOADING -> {
main.isRefreshing = true
//binding.progressBar.visibility = View.VISIBLE
}
}
})
}
Try it sample code. May it not better way, but its may be working

Related

Composable visibility not changing on State change

I have a isLoading state and I'm trying to show a CircularProgressIndicator when the value is true.
#Composable
fun ProductDetailScreen(
viewModel: ProductDetailViewModel = hiltViewModel()
) {
val productState = viewModel.productState.value
LazyColumn{
item {
if (productState.isLoading)
CircularProgressIndicator()
}
}
}
I'm using a Resource class for my API call results and in the repository I use this class to wrap my request result.
The problem is, although I'm returning Resource.Loading from the repository, the isLoading state is not being updated from ViewModel and the ProgressIndicator is not shown in my screen. What could be causing this behavior?
sealed class Resource<T>(
val data: T? = null,
val message: String? = null,
val errorType: ExceptionMapper.Type? = null
) {
class Success<T>(data: T?) : Resource<T>(data)
class Error<T>(message: String, errorType: ExceptionMapper.Type, data: T? = null) : Resource<T>(data, message, errorType)
class Loading<T>(isLoading: Boolean = true) : Resource<T>()
}
Repository:
override suspend fun getProductComments(productId: Int): Resource<List<Comment>> {
return try {
Resource.Loading<List<Comment>>()
delay(3000)
Resource.Success(apiService.getComments(productId))
} catch (t: Throwable) {
val mappedException = ExceptionMapper.map(t)
Resource.Error(message = t.message!!, errorType = mappedException.type)
}
}
ViewModel:
#HiltViewModel
class ProductDetailViewModel #Inject constructor(
state: SavedStateHandle,
private val productRepository: ProductRepository
) : ViewModel() {
private val passedProduct = state.get<Product>(EXTRA_KEY_DATA)
var productId = passedProduct?.id
var productState = mutableStateOf(ProductState())
private set
init {
getProductComments()
}
private fun getProductComments() {
viewModelScope.launch {
productId?.let { pId ->
when (val commentResult = productRepository.getProductComments(pId)) {
is Resource.Success -> {
commentResult.data?.let { comments ->
productState.value =
productState.value.copy(
comments = comments,
error = null,
isLoading = false
)
}
}
is Resource.Error -> {
productState.value = productState.value.copy(
isLoadFailed = true,
isLoading = false,
error = commentResult.message
)
}
is Resource.Loading -> {
productState.value = productState.value.copy(
isLoadFailed = false,
isLoading = true,
error = null
)
}
}
}
}
}
}
Your'e only checking this
is Resource.Loading -> {
...
}
when the repository returns, at this point its useless because when the call to getProductComments is done, it's already Resource.Success.
return try {
Resource.Loading<List<Comment>>() // you'll never get this value
delay(3000)
Resource.Success(apiService.getComments(productId))
So I'd suggest to update the ProductState before you call the repository
private fun getProductComments() {
productState.value = productState.value.copy(isLoading = true)
viewModelScope.launch {
...
...
or set isLoading to true as its initial state.
data class ProductState(
...
...
val isLoading : Boolean = true
...
)

How to retrieve data from Firestore with the MVVM pattern

I am creating an android application following the MVVM patron with the goal of retrieving data from a Firebase collection.
Before applying this pattern, I did proof of concept and I was able to retrieve data from the Firebase collection. But once I apply MVVM, I am not able to get the data from that collection, my screen does not show anything. I am not able to return the data from the repository to be painted on the screen.
This is my code:
Model:
data class PotatoesData(
val modifiedDate: String,
var potatoes: List<Potato>
)
data class Potato(
val type: String,
val site: String
)
State:
data class PotatoesState(
val isLoading: Boolean = false,
val potatoes: List<Potato> = emptyList(),
val error: String = ""
)
ModelView:
#HiltViewModel
class PotatoesViewModel #Inject constructor(
private val getPotatoesDataUseCase: GetPotatoesData
) : ViewModel() {
private val _state = mutableStateOf(PotatoesState())
val state: State<PotatoesState> = _state
init {
getPotatoes()
}
private fun getPotatoes() {
getPotatoesDataUseCase().onEach { result ->
when (result) {
is Resource.Success -> {
_state.value = PotatoesState(potatoes = result.data?.potatoes ?: emptyList())
}
is Resource.Error -> {
_state.value = PotatoesState(
error = result.message ?: "An unexpected error occurred"
)
}
is Resource.Loading -> {
_state.value = PotatoesState(isLoading = true)
}
}
}.launchIn(viewModelScope)
}
}
UseCase:
class GetPotatoesData #Inject constructor(
private val repository: PotatoRepository
) {
operator fun invoke(): Flow<Resource<PotatoesData>> = flow {
try {
emit(Resource.Loading())
val potatoes = repository.getPotatoesData()
emit(Resource.Success(potatoes))
} catch (e: IOException) {
emit(Resource.Error("Couldn't reach server. Check your internet connection."))
}
}
}
Repository implementation:
class PotatoRepositoryImpl : PotatoRepository {
override suspend fun getPotatoesData(): PotatoesData {
var potatoes = PotatoesData("TEST", emptyList())
FirestoreProvider.getLastPotatoes(
{ potatoesData ->
if (potatoesData != null) {
potatoes = potatoesData
}
},
{
potatoes
}
)
return potatoes
}
}
Firestore provider:
object FirestoreProvider {
private val incidentsRef = FirebaseFirestore.getInstance().collection(FirestoreCollection.POTATOES.key)
fun getLastPotatoes(
success: (potatoesData: PotatoesData?) -> Unit,
failure: () -> Unit
) {
val query: Query = orderBy(FirestoreField.CREATED_DATE, Query.Direction.DESCENDING).limit(1)
val querySnapshot: Task<QuerySnapshot> = query.get()
querySnapshot
.addOnSuccessListener {
if (!querySnapshot.result.isEmpty) {
val document = querySnapshot.result.documents[0]
val potatoesDataDB: PotatoesDataDto? = document.toObject(PotatoesDataDto::class.java)
potatoesDataDB?.let {
success(potatoesDataDB.toPotatoesData())
} ?: run {
success(null)
}
} else {
success(null)
}
}
.addOnFailureListener {
failure()
}
}
private fun orderBy(field: FirestoreField, direction: Query.Direction): Query {
return incidentsRef.orderBy(field.key, direction)
}
}
I am thankful for any kind of help! Thanks in advance!
I think the error is in the way of how you are handling Firestore callbacks. in FirestoreProvider: the callback will fire later than the function getLastPotatoes returns. Try to make that function suspend and use suspendCoroutine to wait for the callback and return it's result. It will look something like:
suspend fun getLastPotatoes() = suspendCoroutine <PotatoesData?> { continuation ->
val query: Query = orderBy(FirestoreField.CREATED_DATE, Query.Direction.DESCENDING).limit(1)
val querySnapshot: Task<QuerySnapshot> = query.get()
querySnapshot
.addOnSuccessListener {
if (!querySnapshot.result.isEmpty) {
val document = querySnapshot.result.documents[0]
val potatoesDataDB: PotatoesDataDto? = document.toObject(PotatoesDataDto::class.java)
potatoesDataDB?.let {
continuation.resume(potatoesDataDB.toPotatoesData())
} ?: run {
continuation.resume(null)
}
} else {
continuation.resume(null)
}
}
.addOnFailureListener {
continuation.resumeWithException(...)
}
}
suspendCoroutine suspends coroutine in which it executed until we decide to continue by calling appropriate methods - Continuation.resume....
In your PotatoRepositoryImpl:
override suspend fun getPotatoesData(): PotatoesData {
var potatoes = PotatoesData("TEST", emptyList())
try {
val potatoesData = FirestoreProvider.getLastPotatoes()
if (potatoesData != null) {
potatoes = potatoesData
}
} catch (e: Exception) {
// handle Exception
}
return potatoes
}

Local unit test for LiveData while using RxJava

Full source code is available at : https://github.com/AliRezaeiii/StarWarsSearch-RxPaging
I am trying to test my DetailViewModel. My expectation is Species and Films not be empty lists as I have for instance : when(service.getSpecie(anyString())).thenReturn(Single.just(specie)). Here is my test :
class DetailViewModelTest {
#get:Rule
var rule: TestRule = InstantTaskExecutorRule()
#Mock
private lateinit var service: StarWarsService
private lateinit var specie: Specie
private lateinit var planet: Planet
private lateinit var film: Film
private lateinit var viewModel: DetailViewModel
#Before
fun setUp() {
initMocks(this)
// Make the sure that all schedulers are immediate.
val schedulerProvider = ImmediateSchedulerProvider()
val detailRepository = DetailRepository(service)
val character = Character(
"Ali", "127", "1385", emptyList(), emptyList()
)
viewModel = DetailViewModel(
schedulerProvider, character, GetSpecieUseCase(detailRepository),
GetPlanetUseCase(detailRepository), GetFilmUseCase(detailRepository)
)
specie = Specie("Ali", "Persian", "Iran")
planet = Planet("")
film = Film("")
}
#Test
fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
`when`(service.getSpecie(anyString())).thenReturn(Single.just(specie))
`when`(service.getPlanet(anyString())).thenReturn(Single.just(planet))
`when`(service.getFilm(anyString())).thenReturn(Single.just(film))
viewModel.liveData.value.let {
assertThat(it, `is`(notNullValue()))
if (it is Resource.Success) {
it.data?.let { data ->
assertTrue(data.films.isEmpty())
assertTrue(data.species.isEmpty())
}
}
}
}
#Test
fun givenServerResponseError_whenFetch_specie_shouldReturnError() {
`when`(service.getSpecie(anyString())).thenReturn(Single.error(Exception("error")))
`when`(service.getPlanet(anyString())).thenReturn(Single.just(planet))
`when`(service.getFilm(anyString())).thenReturn(Single.just(film))
viewModel.liveData.value.let {
assertThat(it, `is`(notNullValue()))
if (it is Resource.Error) {
assertThat(it.message, `is`(notNullValue()))
assertThat(it.message, `is`("error"))
}
}
}
}
Here is my ViewModel :
class DetailViewModel #Inject constructor(
schedulerProvider: BaseSchedulerProvider,
character: Character,
getSpecieUseCase: GetSpecieUseCase,
getPlanetUseCase: GetPlanetUseCase,
getFilmUseCase: GetFilmUseCase,
) : BaseViewModel<DetailWrapper>(schedulerProvider,
Single.zip(Flowable.fromIterable(character.specieUrls)
.flatMapSingle { specieUrl -> getSpecieUseCase(specieUrl) }
.flatMapSingle { specie ->
getPlanetUseCase(specie.homeWorld).map { planet ->
SpecieWrapper(specie.name, specie.language, planet.population)
}
}.toList(),
Flowable.fromIterable(character.filmUrls)
.flatMapSingle { filmUrl -> getFilmUseCase(filmUrl) }
.toList(), { species, films ->
DetailWrapper(species, films)
}))
And here is my BaseViewModel :
open class BaseViewModel<T>(
private val schedulerProvider: BaseSchedulerProvider,
private val singleRequest: Single<T>
) : ViewModel() {
private val compositeDisposable = CompositeDisposable()
private val _liveData = MutableLiveData<Resource<T>>()
val liveData: LiveData<Resource<T>>
get() = _liveData
init {
sendRequest()
}
fun sendRequest() {
_liveData.value = Resource.Loading
wrapEspressoIdlingResourceSingle { singleRequest }
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui()).subscribe({
_liveData.postValue(Resource.Success(it))
}) {
_liveData.postValue(Resource.Error(it.localizedMessage))
Timber.e(it)
}.also { compositeDisposable.add(it) }
}
override fun onCleared() {
super.onCleared()
compositeDisposable.clear()
}
}
And here is DetailWrapper class :
class DetailWrapper(
val species: List<SpecieWrapper>,
val films: List<Film>,
)
class SpecieWrapper(
val name: String,
val language: String,
val population: String,
)
Why films and species lists are empty in my local unit test?
As you see I pass two emptyLists to Character object. That is the source of problem since for instance I have following in DetailViewModel :
Flowable.fromIterable(character.filmUrls)
.flatMapSingle { filmUrl -> getFilmUseCase(filmUrl) }
.toList()
FilmUrls is one of those emptyLists. If I change Character by passing not emptyList, it is working as expected :
character = Character("Ali", "127", "1385",
listOf("url1", "url2"), listOf("url1", "url2"))
I also need to move ViewModel initialization to the method body, such as :
#Test
fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
`when`(repository.getSpecie(anyString())).thenReturn(Single.just(specie))
`when`(repository.getPlanet(anyString())).thenReturn(Single.just(planet))
`when`(repository.getFilm(anyString())).thenReturn(Single.just(film))
viewModel = DetailViewModel(schedulerProvider, character, GetSpecieUseCase(repository),
GetPlanetUseCase(repository), GetFilmUseCase(repository))
viewModel.liveData.value.let {
assertThat(it, `is`(notNullValue()))
if (it is Resource.Success) {
it.data?.let { data ->
assertTrue(data.films.isNotEmpty())
assertTrue(data.species.isNotEmpty())
}
}
}
}

Observe StateFlow as LiveData in Unit test

Source code can be found at : https://github.com/AliRezaeiii/MVI-Architecture-Android-Beginners
I have following Unit test which is working fine :
#ExperimentalCoroutinesApi
#RunWith(MockitoJUnitRunner::class)
class MainViewModelTest {
#get:Rule
val rule: TestRule = InstantTaskExecutorRule()
#get:Rule
val coroutineScope = MainCoroutineScopeRule()
#Mock
lateinit var apiService: ApiService
#Mock
private lateinit var observer: Observer<MainState>
#Test
fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
runBlockingTest {
`when`(apiService.getUsers()).thenReturn(emptyList())
}
val apiHelper = ApiHelperImpl(apiService)
val repository = MainRepository(apiHelper)
val viewModel = MainViewModel(repository, TestContextProvider())
viewModel.state.asLiveData().observeForever(observer)
verify(observer).onChanged(MainState.Users(emptyList()))
}
#Test
fun givenServerResponseError_whenFetch_shouldReturnError() {
runBlockingTest {
`when`(apiService.getUsers()).thenThrow(RuntimeException())
}
val apiHelper = ApiHelperImpl(apiService)
val repository = MainRepository(apiHelper)
val viewModel = MainViewModel(repository, TestContextProvider())
viewModel.state.asLiveData().observeForever(observer)
verify(observer).onChanged(MainState.Error(null))
}
}
The idea of unit test for stateFlow is taken from alternative solution in this question : Unit test the new Kotlin coroutine StateFlow
This is my ViewModel class :
#ExperimentalCoroutinesApi
class MainViewModel(
private val repository: MainRepository,
private val contextProvider: ContextProvider
) : ViewModel() {
val userIntent = Channel<MainIntent>(Channel.UNLIMITED)
private val _state = MutableStateFlow<MainState>(MainState.Idle)
val state: StateFlow<MainState>
get() = _state
init {
handleIntent()
}
private fun handleIntent() {
viewModelScope.launch(contextProvider.io) {
userIntent.send(MainIntent.FetchUser)
userIntent.consumeAsFlow().collect {
when (it) {
is MainIntent.FetchUser -> fetchUser()
}
}
}
}
private fun fetchUser() {
viewModelScope.launch(contextProvider.io) {
_state.value = MainState.Loading
_state.value = try {
MainState.Users(repository.getUsers())
} catch (e: Exception) {
MainState.Error(e.localizedMessage)
}
}
}
}
As you see when fetchUser() is called, _state.value = MainState.Loading will be executed at start. As a result in unit test I expect following as well in advance :
verify(observer).onChanged(MainState.Loading)
Why unit test is passing without Loading state?
Here is my sealed class :
sealed class MainState {
object Idle : MainState()
object Loading : MainState()
data class Users(val user: List<User>) : MainState()
data class Error(val error: String?) : MainState()
}
And here is how I observe it in MainActivity :
private fun observeViewModel() {
lifecycleScope.launch {
mainViewModel.state.collect {
when (it) {
is MainState.Idle -> {
}
is MainState.Loading -> {
buttonFetchUser.visibility = View.GONE
progressBar.visibility = View.VISIBLE
}
is MainState.Users -> {
progressBar.visibility = View.GONE
buttonFetchUser.visibility = View.GONE
renderList(it.user)
}
is MainState.Error -> {
progressBar.visibility = View.GONE
buttonFetchUser.visibility = View.VISIBLE
Toast.makeText(this#MainActivity, it.error, Toast.LENGTH_LONG).show()
}
}
}
}
}
Addendda: If I call userIntent.send(MainIntent.FetchUser) method after viewModel.state.asLiveData().observeForever(observer) instead of init block of ViewModel, Idle and Loading states will be verified as expected by Mockito.

Wrong UninitializedPropertyAccessException in Kotlin after property initialization

I have Dagger2 injected property in my presenter.
#Inject lateinit var dataInteractor: DataInteractor
It is accessed in couple of methods. In one of them loadAppointments() everything works fine but in another refund() UninitializedPropertyAccessException is thrown. The code has been working well for a while and this issue has raised the only couple of days ago.
No Kotlin updates were installed before.
import android.util.Log
import co.example.Application
import co.example.domain.model.entity.AppointmentsEntity
import co.example.domain.model.entity.ProvidersEntity
import co.example.interactor.data.DataInteractor
import co.example.domain.di.base.RxDisposablePresenter
import co.example.view.operation.OperationView
import io.reactivex.android.schedulers.AndroidSchedulers
import java.util.*
import javax.inject.Inject
class AppointmentsPresenter : RxDisposablePresenter<AppointmentsPresenter.View>() {
#Inject
lateinit var dataInteractor: DataInteractor
private lateinit var operationView: OperationView<*>
private val results: ArrayList<AppointmentsEntity.AppointmentEntity> = ArrayList()
private val resultsHistory: ArrayList<AppointmentsEntity.AppointmentEntity> = ArrayList()
override fun onTakeView(view: View?) {
super.onTakeView(view)
operationView = view?.operationView()!!
}
fun loadAppointments() {
val userID = Application.appComponent.userInternalStorage().userID()
if (userID != null) {
add(dataInteractor
.getAppointments(userID, LIMIT, OFFSET)
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { operationView.showProgress() }
.doFinally {
operationView.hideProgress()
view?.onAppointmentsDataFilled(results, resultsHistory)
}
.subscribe(
{
divideUpcomingAndCompleteAppointments(it)
view?.unblockAppointments()
},
{
val message = it.message
if (message!!.contains("blocked")) {
view?.blockAppointments()
operationView.showError("Error")
return#subscribe
}
operationView.showError(message)
}
))
}
}
fun refund(position: Int?) {
add(dataInteractor
.refund(results[position!!].id!!)
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { operationView.showProgress() }
.doFinally {
operationView.hideProgress()
}
.subscribe(
{
view?.onRefundCompleted()
},
{
val message = it.message
operationView.showError(message!!)
}
))
}
private fun divideUpcomingAndCompleteAppointments(appointmentsEntity: AppointmentsEntity) {
results.clear()
resultsHistory.clear()
for (appointment in appointmentsEntity.results!!) {
if (appointment.status.equals(STATUS_COMPLETE, true)) {
resultsHistory.add(appointment)
} else {
results.add(appointment)
}
}
}
fun loadProviderById(doctorId: Long?, onDoctorRetrivedListener: (ProvidersEntity.ProviderEntity) -> Unit) {
add(
dataInteractor
.getProviders(0, 100, 0, 0)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ providersEntity ->
val doctor = providersEntity
.results
?.filter {
Log.d("ATAT", "loadProviderById: ${it.user_id}")
it.user_id == doctorId
}
doctor?.let {
if (it.isNotEmpty()) {
onDoctorRetrivedListener.invoke(doctor[0])
}
}
},
{
val message = it.message
operationView.showError(message!!)
}))
}
interface View {
fun operationView(): OperationView<*>
fun onAppointmentsDataFilled(appointments: ArrayList<AppointmentsEntity.AppointmentEntity>?,
appointmentsHistory: ArrayList<AppointmentsEntity.AppointmentEntity>?)
fun onRefundCompleted()
fun blockAppointments()
fun unblockAppointments()
}
companion object {
const val STATUS_COMPLETE = "Complete"
const val LIMIT: Long = 1000
const val OFFSET: Long = 0
}
}
Here is the stack trace:
2018-12-13 11:59:29.359 4808-4808/co.example E/AndroidRuntime: FATAL EXCEPTION: main
Process: co.example, PID: 4808
kotlin.UninitializedPropertyAccessException: lateinit property dataInteractor has not been initialized
at co.example.presenter.appointements.AppointmentsPresenter.refund(AppointmentsPresenter.kt:59)
at co.example.view.appointements.AppointmentsFragment.refundRequest(AppointmentsFragment.kt:123)
at co.example.activity.MainActivity.onRefundRequested(MainActivity.kt:295)
at co.example.view.dialog.RefundDialogFragment$onCreateDialog$1.onClick(RefundDialogFragment.kt:16)
at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:172)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
UPD:
Doesn't reproduce if fragment that calls these methods is created as singleton.

Categories

Resources