LivePagedList emitting empty list - java

I am using a paging library from Android Architecture Components.
Paging is implemented using ItemKeyedDataSource
class MyDatasource(
private val queryMap: HashMap<String, String>) : ItemKeyedDataSource<String, Order>() {
private val compositeDisposable: CompositeDisposable by lazy { CompositeDisposable() }
override fun loadInitial(params: LoadInitialParams<String>, callback: LoadInitialCallback<Order>) {
compositeDisposable.add(
MyService.getService().fetchData(queryMap)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableObserver<OrdersResponse>() {
override fun onNext(orders: OrdersResponse) {
callback.onResult(orders.data)
}
override fun onError(e: Throwable) {
e.printStackTrace()
}
override fun onComplete() {
}
})
)
}
override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<Order>) {
// do nothing
}
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Order>) {
queryMap["offsetOrderId"] = params.key
compositeDisposable.add(
MyService.getService().fetchData(queryMap)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableObserver<OrdersResponse>() {
override fun onNext(orders: OrdersResponse) {
callback.onResult(orders.data)
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
}
})
)
}
override fun getKey(item: Order): String {
return item.orderId
}
}
I build pagedlist in my viewmodel
class MyViewModel() : ViewModel() {
private var myPagingConfig: PagedList.Config? = null
var dataList: LiveData<PagedList<Order>>? = null
fun getOrders(params: HashMap<String, String>) {
if (myPagingConfig == null) {
myPagingConfig = PagedList.Config.Builder()
.setPageSize(LIMIT)
.setPrefetchDistance(10)
.setEnablePlaceholders(false)
.build()
}
dataList = LivePagedListBuilder(MyDataFactory(
MyDatasource(params)), myPagingConfig!!)
.setInitialLoadKey(null)
.setFetchExecutor(Executors.newFixedThreadPool(5))
.build()
}
}
However, when I observe the dataList in my activity, it sometimes (most of the times) returns an empty list, while in logcat I see that I had fetched data successfully. callback.onResult is invoked after it returns an empty list, but observer never gets notified again.
Can you tell me if what would cause this?

Related

Return a list of items after Observable is complete

I need to return a list of items but before that I have to check for some particular ones. I'm able to do that only async.
fun getItemList() : List<Model> {
val list = mutableListOf<Model>()
list.add(model1)
list.add(model2)
Observable.fromCallable { findParticularModel() }
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<String> {
override fun onSubscribe(d: Disposable?) {
}
override fun onNext(modelId: String) {
list.add(createModelFromId(modelId))
}
#SuppressLint("TimberExceptionLogging")
override fun onError(e: Throwable?) {
Timber.e(e?.message)
}
override fun onComplete() {
}
})
return list
}
How can I return the list only after the Observable is completed
Here is an example how to create and consume such asynchronous datasource:
fun getItemList() : Single<List<Model>> {
Single.fromCallable { findParticularModel() }
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map {
val list = mutableListOf<Model>()
list.add(model1)
list.add(model2)
list.add(createModelFromId(it))
list
}
}
getItemList().subscribe({
// here you have $it as list on the main thread
}, {
Timber.e(it?.message)
})

Android - Observation method did not work

The observer I created is not working. There are classes that I have run before, but the current one did not work. I could not understand where the problem was. The observation methods I created before are working.
SingleLiveData.kt
class SingleLiveData<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean()
#MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Timber.w("Multiple observers registered but only one will be notified of changes.")
}
super.observe(owner, Observer { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
#MainThread
override fun setValue(value: T?) {
pending.set(true)
super.setValue(value)
}
}
ViewModel.kt
class ExampleViewModel#Inject constructor(): ViewModel() {
val event = SingleLiveData<ExampleViewEvent>()
fun goToHome(userId: Long) {
event.postValue(ExampleViewEvent.GoToHome(userId))
}
}
Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
observe(viewModel.event, ::onViewEvent)
}
private fun onViewEvent(viewEvent: ExampleViewEvent) {
when (viewEvent) {
is ExampleViewEvent.GoToHome ->
findNavController().navigate(
ExampleFragmentDirections
.actionExampleFragmentToHomeFragment(viewEvent.id))
}
}
EDIT:
observe() Method:
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, observer: (T) -> Unit) {
liveData.observe(this, Observer {
it?.let { t -> observer(t) }
})
}
fun <T> LifecycleOwner.observe(liveData: MutableLiveData<T>, observer: (T) -> Unit) {
liveData.observe(this, Observer {
it?.let { t -> observer(t) }
})
}
NOTE: hasActiveObservers() return false.

How to update server response in UI using MVVM pattern

I am using MVVM pattern in my app I have separate repository class for network operations. In repository class I am getting response from the server. How can I show Toast message send from the server in my main activity.
Below is my code:
Repository.java
public class MyRepository {
MutableLiveData<List<Facts>> mutableLiveData = new MutableLiveData<>();
Application application;
public MyRepository(Application application) {
this.application = application;
}
public MutableLiveData<List<Facts>> getMutableLiveData(){
Retrofit retrofit = RetrofitClient.getInstance();
ApiService apiService = retrofit.create(ApiService.class);
apiService.getFacts().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Facts>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<Facts> facts) {
if(facts.size() > 0 && facts != null){
mutableLiveData.setValue(facts);
}
}
#Override
public void onError(Throwable e) {
TastyToast.makeText(application,e.getMessage(),TastyToast.LENGTH_SHORT,
TastyToast.ERROR).show();
}
#Override
public void onComplete() {
}
});
return mutableLiveData;
}
}
FactsViewModel.java
public class FactsViewModel extends AndroidViewModel {
MyRepository repo;
public FactsViewModel(#NonNull Application application) {
super(application);
repo = new MyRepository(application);
}
public LiveData<List<Facts>> getAllFacts(){
return repo.getMutableLiveData();
}
}
MainActivity.java
private void myFacts(){
FactsViewModel viewModel = new ViewModelProvider(this).get(FactsViewModel.class);
viewModel.getAllFacts().observe(this, new Observer<List<Facts>>() {
#Override
public void onChanged(List<Facts> facts) {
adapter = new FactsAdapter(facts,getActivity());
recycle.setAdapter(adapter);
}
});
}
How can I show error toast messages in MainActivity?
To implement that you firstly need to create a class which has the status of the response ,
Loading which is before the fetching of the data and there you can set progress bar to visible then on success you would set the data to your adapter and right after your hide your progress bar and in the on failure one , you show the toast message error
This is the generic class
class AuthResource<T>(
var authStatus : AuthStatus? = null,
var data : T,
var msg : String? = null
)
fun <T> success(#Nullable data: T): AuthResource<T> {
return AuthResource(
AuthStatus.Success,
data,
null
)
}
fun <T> Error(#NonNull msg: String?, #Nullable data: T) : AuthResource<T>? {
return AuthResource(
AuthStatus.ERROR,
data,
msg
)
}
fun <T> loading(#Nullable data: T): AuthResource<T>? {
return AuthResource(
AuthStatus.LOADING,
data,
null
)
}
enum class AuthStatus {
Success, ERROR, LOADING
}
This is my view model where i implement the authResource with the api response
class MainViewModel #Inject constructor( private var webAuth: WebAuth,
private var favFoodDao: FavFoodDao,
private var application: Application) : ViewModel() {
/// you have to create MediatorLiveData with authresource which contains your modelclass
private var mediatorLiveData = MediatorLiveData<AuthResource<WrapLatestMeals>>()
///Here you return a livedata object
fun ObserverCountries(): LiveData<AuthResource<WrapCountries>> {
var liveData = LiveDataReactiveStreams.fromPublisher(
webAuth.getCountries()
///onerrorreturn , rxjava operator which returns error in case
///of response failure
.onErrorReturn(object : Function<Throwable, WrapCountries> {
override fun apply(t: Throwable): WrapCountries {
var country = WrapCountries()
return country
}
})
.map(object : Function<WrapCountries,
AuthResource<WrapCountries>> {
override fun apply(t: WrapCountries):
AuthResource<WrapCountries> {
if(t.meals.isNullOrEmpty())
{
return Error(
"Error",
t
)!!
}
return success(t)
}
})
.subscribeOn(Schedulers.io())
)
//add that data to mediatorLivedata
mediatorLiveDataCountries.addSource(liveData, Observer {
mediatorLiveDataCountries.postValue(it)
mediatorLiveDataCountries.removeSource(liveData)
})
return mediatorLiveDataCountries
}
This is how you handle the status in your MainActivity
mainViewModel = ViewModelProvider(this,provider)[MainViewModel::class.java]
mainViewModel.ObserverCountries().observe(viewLifecycleOwner, Observer {
when(it.authStatus) {
AuthStatus.LOADING -> /// here you show progressbar in response pre-fetch
{
countriesFragmentBinding.countryprogress.show()
}
AuthStatus.Success -> { // here you update your ui
countriesAdapter = CountriesAdapter(it.data.meals!!,
requireContext())
countriesFragmentBinding.recyclercountries.adapter = countriesAdapter
countriesAdapter!!.deleteCategory(23)
countriesFragmentBinding.countryprogress.hide()
}
AuthStatus.ERROR -> // here you hide your progressbar and show your toast
{
countriesFragmentBinding.countryprogress.hide()
ToastyError(requireContext(),getString(R.string.errorretreivingdata))
}
}
})
return countriesFragmentBinding.root
}
}

Android get LiveData from Retrofit call

I'm trying to fetch some data with Retrofit on my Android project update this on the ViewModel and my activity with LiveData.
Here is my service Class:
class PaymentService {
private var paymentMethodList = ArrayList<PaymentMethodModel>()
private val paymentMethodListLiveData = MutableLiveData<List<PaymentMethodModel>>()
init {
paymentMethodListLiveData.value = paymentMethodList
}
fun fetchPaymentMethods() {
val retrofit = Retrofit.Builder()
.baseUrl(SERVICE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(PaymentClient::class.java)
val jsonCall = service.getListOfPaymentMethods()
jsonCall.enqueue(object : Callback<List<PaymentMethodModel>> {
override fun onResponse(call: Call<List<PaymentMethodModel>>, response: Response<List<PaymentMethodModel>>) {
paymentMethodList = (response.body() as ArrayList<PaymentMethodModel>?)!!
}
override fun onFailure(call: Call<List<PaymentMethodModel>>, t: Throwable) {
//TODO
}
})
}
And here is where I'm trying to listen to the changes on the list:
goToNextButton.setOnClickListener {
paymentMethods = PaymentMethodSelectionViewModel().getAllPaymentMethods()
paymentMethods!!.observe(viewLifecycleOwner, Observer {
Log.e("", "")
})
}
The problem is that so far I'm getting the list only the first time with 0 elements and this observer method is not getting called after the rest call is made and the list updated.
Edit
class PaymentRepository {
private val paymentService = PaymentService()
fun getPaymentMethods(): LiveData<List<PaymentMethodModel>> {
paymentService.fetchPaymentMethods()
return paymentService.getPaymentMethods()
}
}
class PaymentMethodSelectionViewModel: ViewModel() {
private val paymentRepository = PaymentRepository()
private val paymentMethods = paymentRepository.getPaymentMethods()
fun getAllPaymentMethods(): LiveData<List<PaymentMethodModel>> {
paymentRepository.getPaymentMethods()
return paymentMethods
}
}
Change your request into viewmodel
class PaymentMethodSelectionViewModel: ViewModel() {
//Data
var paymentMethodList = MutableLiveData<List<PaymentMethodModel>>()
fun getAllPayments(){
val retrofit = Retrofit.Builder()
.baseUrl(SERVICE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(PaymentClient::class.java)
val jsonCall = service.getListOfPaymentMethods()
jsonCall.enqueue(object : Callback<List<PaymentMethodModel>> {
override fun onResponse(call: Call<List<PaymentMethodModel>>, response: Response<List<PaymentMethodModel>>) {
var data: List<PaymentMethodModel> = (response.body() as ArrayList<PaymentMethodModel>?)!!
paymentMethodList.value=data
}
override fun onFailure(call: Call<List<PaymentMethodModel>>, t: Throwable) {
//TODO
}
})
}
}
in your view (activity) use
//load
paymentMethodSelectionViewModel.getAllPayments();
//Observers
paymentMethodSelectionViewModel.paymentMethodList.observe(this,
Observer { list ->
// your code
})
I recommend you use retrofit 2 with corutines or RXJAVA2,
check this tutorial
https://medium.com/#amtechnovation/android-architecture-component-mvvm-part-1-a2e7cff07a76
https://medium.com/#saquib3705/consuming-rest-api-using-retrofit-library-with-the-help-of-mvvm-dagger-livedata-and-rxjava2-in-67aebefe031d
As #tyczj says in the comment, every time you use a LiveData, you have to decide when all the observers receive an update notification.
You can do this notification by calling post function of your paymentMethodListLiveData object. This is the correct way to use LiveData in Java.
In Kotlin I think you have to add something like this on your onResponse method:
paymentMethodListLiveData.value = paymentMethodList;
to implicitly call the post method and trigger methods in your observe function.
Hope this help or give you some hints.
Cheers

Strange behavior of compose and ObservableTransformer in RxJava when the passed generic extends something

While trying to play with compose in RxJava2 I found something I can't really explain.
Suppose we have the following two static methods:
object RxUtils {
fun <T> handleResponse(): ObservableTransformer<T, T> {
return ObservableTransformer { observable ->
observable.flatMap { t ->
ObservableFromCallable.fromCallable(object: Callable<T>{
override fun call(): T {
return t
}
})
}
}
}
fun <T: GenericResponse> handleResponse2(): ObservableTransformer<T, T> {
return ObservableTransformer { observable ->
observable.flatMap { t ->
ObservableFromCallable.fromCallable(object: Callable<T>{
override fun call(): T {
return t
}
})
}
}
}}
handleResponse and handleResponse2 are the same, the difference is that in handleResponse2 T extends GenericResponse.
The problem is that when i use handleResponse2 in compose, the object which comes from compose is not an Observable and I can't use any Observable operators.
Example:
fun <T: GenericResponse> loadApi2(observable: Observable<T>): Observable<T> {
return observable
.compose(RxUtils.handleResponse2()) // here is blocked, can't use any other operator
}
fun <T: GenericResponse> loadApi(observable: Observable<T>): Observable<T> {
return observable
.compose(RxUtils.handleResponse()) //works fine, I can use other operators such as compose, subscribeOn etc
}

Categories