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.
Related
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)
})
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
}
}
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?
I had the following Java code:
public class DatabaseManager {
public interface Predicate<T> {
boolean test(T t);
}
private interface Consumer<T> {
void apply(T t);
}
private void updateLiveLists() {
updateLiveLists(liveList -> true);
}
private void updateLiveLists(Predicate<MutableLiveList<?>> predicate) {
forEachLiveList(liveList -> {
if (predicate.test(liveList)) {
refresh(liveList);
}
});
}
private void forEachLiveList(Consumer<MutableLiveList<?>> consumer) {
...
}
Then I used Java -> Kotlin conversion in Android Studio:
class DatabaseManager {
interface Predicate<T> {
fun test(t: T): Boolean
}
private interface Consumer<T> {
fun apply(t: T)
}
private fun updateLiveLists(predicate: Predicate<MutableLiveList<*>> = { liveList -> true }) {
forEachLiveList({ liveList ->
if (predicate.test(liveList)) {
refresh(liveList)
}
})
}
private fun forEachLiveList(consumer: Consumer<MutableLiveList<*>>) {
...
}
Fails with the following error:
Type mismatch
Required: DatabaseManager.Consumer<MutableLiveList<*>>
Found: (???) -> Unit
Now I had to change code to following:
private fun updateLiveLists(predicate: Predicate<MutableLiveList<*>> = object : Predicate<MutableLiveList<*>> {
override fun test(t: MutableLiveList<*>): Boolean {
return true;
}
}) {
forEachLiveList(object : DatabaseManager.Consumer<MutableLiveList<*>> { // <--- !!
override fun apply(t: MutableLiveList<*>) {
if (predicate.test(t)) {
refresh(t)
}
}
})
}
Okay, so I had to explicitly declare this anonymous interface as an explicit subclass of object, as for whatever reason Kotlin couldn't figure out the lambda.
If it helps, I have the same problem occurring in a function below it:
fun refresh(vararg tables: Table) {
updateLiveLists({ liveList ->
for (table in tables) {
if (liveList.getTable() === table) {
return#updateLiveLists true
}
}
false
})
}
Which says:
Type Mismatch:
Required: DatabaseManager.Predicate<MutableLiveList<*>>
Found: ??? -> Boolean
And I have to do this instead
fun refresh(vararg tables: Table) {
updateLiveLists(object: DatabaseManager.Predicate<MutableLiveList<*>> { // <--
override fun test(t: MutableLiveList<*>): Boolean {
for (table in tables) {
if (t.getTable() === table) {
return true
}
}
return false
}
})
}
Why, and how can I avoid this? How can I use my own Predicate/Consumer without Kotlin getting confused about the lambda type?
Thanks to /u/lupajz I now know that the problem is that interfaces defined in Kotlin are not converted by the SAM conversion because of https://discuss.kotlinlang.org/t/kotlin-and-sam-interface-with-two-parameters/293/5
Basically it boils down to
"why would you do it this way when you can use Kotlin's functional interfaces and type-aliases instead; if you need this then define the interfaces in Java".
There are a few workarounds:
1.) inline objects (which is what I showed above as part of the question)
2.) type aliases + exposing overloaded methods
private typealias KotlinPredicate<T> = (T) -> Boolean;
private typealias KotlinConsumer<T> = (T) -> Unit;
class DatabaseManager {
private interface Consumer<T> {
fun apply(t : T) : Unit;
}
private fun forEachLiveList(consumer: Consumer<MutableLiveList<*>>) {
forEachLiveList({
consumer.apply(it)
})
}
private fun forEachLiveList(consumer: KotlinConsumer<MutableLiveList<*>>) {
...
}
and
interface Predicate<T> {
fun test(t : T) : Boolean;
}
fun updateLiveLists(predicate: Predicate<MutableLiveList<*>>) {
updateLiveLists({
predicate.test(it)
})
}
fun updateLiveLists(predicate: KotlinPredicate<MutableLiveList<*>> = { liveList -> true }) {
forEachLiveList({ liveList ->
if (predicate.invoke(liveList)) {
refresh(liveList)
}
})
}
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
}