What do I supply to "LifeCycleOwner" parameter on observe in Kotlin? - android

I'm fairly new on the Android Room, which uses LiveData that I'm also not familiar with. I noticed that on the tutorials, the data returned is using LiveData wrapper, like this:
#Dao
interface PersonDao {
#Query("SELECT * FROM person")
fun getAll(): LiveData<List<Person>>
}
Then to read the data, I use this code:
class PersonListActivity: AppCompatActivity() {
List<Person> personList = listOf()
init {
val db = RoomDatabase.getDatabaseInstance()
db.personDao.getAll().observe(this, Observe<List<Person>> { data ->
personList = data
})
}
}
The problem is the IDE raise error "type mismatch". Required: LifeCycleOwner. Found: PersonListActivity. I don't understand how the tutorials can casually supply "this" into the observe owner parameter. I've also tried to supply context and applicationContext into the owner parameter and it doesn't work.
After I examine the LifeCycleOwner class, I tried to add the LifeCycleOwner implementation. But then the class requires getLifeCycle() function to be implemented. So I'm back at zero.
class PersonListActivity: AppCompatActivity(), LifeCycleOwner {
List<Person> personList = listOf()
init {
val db = RoomDatabase.getDatabaseInstance()
db.personDao.getAll().observe(this, Observe<List<Person>> { data ->
personList = data
})
}
override fun getLifeCycle() {
// what should I return here??????
}
}
Why all the tutorials I read about LiveData don't mention anything at all about LifeCycleOwner? Am I using the wrong observe function here?
public abstract class LiveData<T> {
...
#MainThread
public void observe(#NonNull LifecycleOwner owner, #NonNull Observer<? super T> observer) { ... }
...
}

Since the version 1.1.0 (excluding the not-stable releases) of androidx.appcompat:appcompat, AppCompatActivity implements LifecycleOwner (see ComponentActivity). So you can use this when calling:
db.personDao.getAll().observe(this, Observe<List<Person>> { data ->
personList = data
})
Without implementing anything else.
Furthermore, I would move those lines in Activity.onCreate().

Related

MVVM architecture with Interactors/UseCases

Context
So, I've been working with the MVVM architecture just for a couple of projects. I'm still trying to figure out and improve how the architecture works. I always worked with the MVP architecture, using the usual toolset, Dagger for DI, usually multi-module projects, the Presenter layer being injected with a bunch of Interactors/UseCases, and each Interactor being injected with different Repositories to perform the backend API calls.
Now that I've moved into MVVM I changed the Presenter layer by the ViewModel, the communication from the ViewModel to the UI layer is being done through LiveData instead of using a View callback interface, and so on.
Looks like this:
class ProductDetailViewModel #inject constructor(
private val getProductsUseCase: GetProductsUseCase,
private val getUserInfoUseCase: GetUserInfoUseCase,
) : ViewModel(), GetProductsUseCase.Callback, GetUserInfoUseCase.Callback {
// Sealed class used to represent the state of the ViewModel
sealed class ProductDetailViewState {
data class UserInfoFetched(
val userInfo: UserInfo
) : ProductDetailViewState(),
data class ProductListFetched(
val products: List<Product>
) : ProductDetailViewState(),
object ErrorFetchingInfo : ProductDetailViewState()
object LoadingInfo : ProductDetailViewState()
}
...
// Live data to communicate back with the UI layer
val state = MutableLiveData<ProductDetailViewState>()
...
// region Implementation of the UseCases callbacks
override fun onSuccessfullyFetchedProducts(products: List<Product>) {
state.value = ProductDetailViewState.ProductListFetched(products)
}
override fun onErrorFetchingProducts(e: Exception) {
state.value = ProductDetailViewState.ErrorFetchingInfo
}
override fun onSuccessfullyFetchedUserInfo(userInfo: UserInfo) {
state.value = ProductDetailViewState.UserInfoFetched(userInfo)
}
override fun onErrorFetchingUserInfo(e: Exception) {
state.value = ProductDetailViewState.ErrorFetchingInfo
}
// Functions to call the UseCases from the UI layer
fun fetchUserProductInfo() {
state.value = ProductDetailViewState.LoadingInfo
getProductsUseCase.execute(this)
getUserInfoUseCase.execute(this)
}
}
There's no rocket science here, sometimes I change the implementation to use more than one LiveData property to keep track of the changes. By the way, this is just an example that I wrote on the fly, so don't expect it to compile. But It's just this, the ViewModel is injected with a bunch of UseCases, it implements the UseCases callback interfaces and when I get the results from the UseCases I communicate that to the UI layer through LiveData.
My UseCases usually look like this:
// UseCase interface
interface GetProductsUseCase {
interface Callback {
fun onSuccessfullyFetchedProducts(products: List<Product>)
fun onErrorFetchingProducts(e: Exception)
}
fun execute(callback: Callback)
}
// Actual implementation
class GetProductsUseCaseImpl(
private val productRepository: ApiProductRepostory
) : GetProductsUseCase {
override fun execute(callback: Callback) {
productRepository.fetchProducts() // Fetches the products from the backend through Retrofit
.subscribe(
{
// onNext()
callback.onSuccessfullyFetchedProducts(it)
},
{
// onError()
callback.onErrorFetchingProducts(it)
}
)
}
}
My Repository classes are usually wrappers for the Retrofit instance and they take care of setting the proper Scheduler so everything runs on the proper thread and mapping the backend responses into model classes. By backend responses I mean classes mapped with Gson (for example
a list of ApiProductResponse) and they get mapped into model classes (for example a List of Product which I use across the App)
Question
My question here is that since I started working with the MVVM architecture all the articles and all the examples, people is either injecting the Repositories right into the ViewModel (duplicating code to handle errors and mapping the responses) or either using the Single Source of Truth pattern (getting the information from Room using Room's Flowables). But I haven't seen anyone use UseCases with a ViewModel layer. I mean it's pretty handy, I get to keep things separated, I do the mapping of the backend responses within the UseCases, I handle any error there. But still, feels odds that I don't see anyone doing this, is there some way to improve the UseCases to make them more friendly to the ViewModels in terms of API? Perform the communication between the UseCases and the ViewModels with something else than a callback interface?
Please let me know if you need any more info about this. Sorry for the examples, I know that these are not the best, I just came out with something simple for sake of explaining it better.
Thanks,
Edit #1
This is how my Repository classes look like:
// ApiProductRepository interface
interface ApiProductRepository {
fun fetchProducts(): Single<NetworkResponse<List<ApiProductResponse>>>
}
// Actual implementation
class ApiProductRepositoryImpl(
private val retrofitApi: ApiProducts, // This is a Retrofit API interface
private val uiScheduler: Scheduler, // AndroidSchedulers.mainThread()
private val backgroundScheduler: Scheduler, // Schedulers.io()
) : GetProductsUseCase {
override fun fetchProducts(): Single<NetworkResponse<List<ApiProductResponse>>> {
return retrofitApi.fetchProducts() // Does the API call using the Retrofit interface. I've the RxAdapter set.
.wrapOnNetworkResponse() // Extended function that converts the Retrofit's Response object into a NetworkResponse class
.observeOn(uiScheduler)
.subscribeOn(backgroundScheduler)
}
}
// The network response class is a class that just carries the Retrofit's Response class status code
Update your use case so that it returns Single<List<Product>>:
class GetProducts #Inject constructor(private val repository: ApiProductRepository) {
operator fun invoke(): Single<List<Product>> {
return repository.fetchProducts()
}
}
Then, update your ViewModel so that it subscribes to the products stream:
class ProductDetailViewModel #Inject constructor(
private val getProducts: GetProducts
): ViewModel() {
val state: LiveData<ProductDetailViewState> get() = _state
private val _state = MutableLiveData<ProductDetailViewState>()
private val compositeDisposable = CompositeDisposable()
init {
subscribeToProducts()
}
override fun onCleared() {
super.onCleared()
compositeDisposable.clear()
}
private fun subscribeToProducts() {
getProducts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.main())
.subscribe(
{
// onNext()
_state.value = ProductListFetched(products = it)
},
{
// onError()
_state.value = ErrorFetchingInfo
}
).addTo(compositeDisposable)
}
}
sealed class ProductDetailViewState {
data class ProductListFetched(
val products: List<Product>
): ProductDetailViewState()
object ErrorFetchingInfo : ProductDetailViewState()
}
One thing I'm leaving out it is the adaptation of List<ApiProductResponse>> to List<Product> but that can be handled by mapping the list with a helper function.
I have just started using MVVM for the last 2 of my projects. I can share with you my process of dealing with REST APIs in ViewModel. Hope it will help you and others.
Make a Generic Retrofit Executer Class with their callbacks. which will take a retrofit call object and gives you data.
Make a repository for Your particular package or module where you can handle all API request. in my case, I am getting one user by its id from API.
Here is User Repository.
class UserRepository {
#Inject
lateinit var mRetrofit: Retrofit
init {
MainApplication.appComponent!!.inject(this)
}
private val userApi = mRetrofit.create(UserApi::class.java)
fun getUserbyId(id: Int): Single<NetworkResponse<User>> {
return Single.create<NetworkResponse<User>>{
emitter ->
val callbyId = userApi.getUserbyId(id)
GenericReqExecutor(callbyId).executeCallRequest(object : ExecutionListener<User>{
override fun onSuccess(response: User) {
emitter.onSuccess(NetworkResponse(success = true,
response = response
))
}
override fun onApiError(error: NetworkError) {
emitter.onSuccess(NetworkResponse(success = false,
response = User(),
networkError = error
))
}
override fun onFailure(error: Throwable) {
emitter.onError(error)
}
})
}
}
}
Then Use this Repository in your ViewModel. In my case here is my LoginViewModel code.
class LoginViewModel : ViewModel() {
var userRepo = UserRepository()
fun getUserById(id :Int){
var diposable = userRepo.getUserbyId(id).subscribe({
//OnNext
},{
//onError
})
}
}
I hope this approach can help you to reduce some of your boilerplate code.
Thanks
I had the same question when I started using MVVM a while ago. I came up with the following solution, based on Kotlin suspend functions and coroutines:
Change ApiProductRepositoryImpl.fetchProducts() to run synchronously. To do this, change your retrofit interface to return Call<...> and then change the repository implementation to
// error handling omitted for brevity
override fun fetchProducts() = retrofitApi.fetchProducts().execute().body()
Make your use cases implement the following interface:
interface UseCase<InputType, OutputType> {
suspend fun execute(input: InputType): OutputType
}
so your GetProductsUseCase would look like this:
class GetProductsUseCase: UseCase<Unit, List<Product>> {
suspend fun execute(input: Unit): List<Product> = withContext(Dispatchers.IO){
// withContext causes this block to run on a background thread
return#withContext productRepository.fetchProducts()
}
Execute the use case in your ViewModel
launch {
state.value = ProductDetailViewState.ProductListFetched(getProductsUseCase.execute())
}
See https://github.com/snellen/umvvm for more info and examples.

How can I share LiveData between multiple ViewModels?

I've tried extracting the value into a base class and having the ViewModels extend it. When I do that, however, the Observer isn't sticking to the LiveData. For instance, when I have a parent class with LiveData:
class Base : ViewModel() {
private val _ data = MutableLiveData()
val data: LiveData = _data
fun someEvent(foo: Foo) { // update _data }
}
class Derived : Base()
class Derived1 : Base()
Then get one of those ViewModels and observe data:
class Frag : Fragment {
onViewCreated() {
// get Derived, ViewModelProviders.of ...etc
derived.data.observe { // Doesn't observe changes }
}
}
Calling Base.someEvent(foo) doesn't notify the LiveData in the Fragment.
I want to avoid getting a reference to both subclasses and invoking someEvent on each. One thing to note is that I'm using a single Activity approach and all ViewModels are Activity scoped.
class Derived : Base()
and
class Derived1 : Base()
have their own instance of:
private val _ data = MutableLiveData()
val data: LiveData = _data
that means you need to
derived.data.observe { // do something }
derived1.data.observer { // do something }
derived.someEvent(someFoo)
derived1.someEvent(someFoo)
You are trying to achieve something in a wrong way.

How to observe ViewModel that makes a request for a call back

I am trying to make a request to a library that gives me a call back.
Manager.getInstance().request(new CallBack())
I want to put this in a ViewModel so that I can observe it from the Activity.
class RequestViewModel : ViewModel, CallBack {
fun request() {
Manager.getInstance().request(this)
}
override fun onFinished(result : List<String>?) {
}
override fun onFailed() {
}
}
How can I make it so that I can observe when this has finished? I know I could make my Activity implement this CallBack, but I don't want to couple Activity to this.
Ideally this would be a LiveData or Observable.
If I understand the question correctly, you can submit the data acquired in onFinished method to the LiveData instance that should be observed by a view component, e.g.
class RequestViewModel : ViewModel, CallBack {
private val _liveData = MutableLiveData<SomeResult<List<String>>>
val liveData: LiveData<SomeResult<List<String>>> get() = _liveData
fun request() {
Manager.getInstance().request(this)
}
override fun onFinished(result : List<String>?) {
if (result != null) {
_liveData.postValue(SomeResult.success(result))
} else {
_liveData.postValue(SomeResult.failure())
}
}
override fun onFailed() {
_liveData.postValue(SomeResult.failure())
}
}
And somewhere in your object that corresponds to a view component:
viewModel.liveData.observe(lifecycleOwner, Observer<List<String>> {
handleResponse(it)
})
whereas lifecycleOwner typically is your AppCompatActivity or android.support.v4.Fragment inheritor.
I would advise you to decouple requesting from ViewModel and create a class called Repository to handle all the requests. In this class you could have a MutableLiveData object which can be observed and whenever new requested data is retrieved, use mutableLiveData.postValue(retrievedData) for MutableLiveData which notifies the observes about the new changes.
To read more about repository, you can follow these links:
Google's Guide to App Architecture
Codelab tutorial with Repository pattern

Android: clean architecture with Room database and LiveData in DAO

I'm trying to apply clean-architecture approach to my project (Link: guide I'm currently referencing).
I'm using Room database for local storage and I want it to be the single source of data in the application - this means that all data gathered from network calls first is saved in database and only after is passed to the presenter. Room provides return of LiveData from its DAOs and this is exactly what suits my needs.
However I also want to use repositories as a single way to access data. Here's an example of repository interface in domain layer (the most abstract one):
interface Repository<T>{
fun findByUsername(username: String) : List<T>
fun add(entity: T): Long
fun remove(entity: T)
fun update(entity: T) : Int
}
And here I'm running into the problem - I need to get a LiveData from Room's DAO in my ViewModel and I'd like to get it using Repository implementation. But in order to achieve this I need either to:
Change Repository method findByUsername to return LiveData>
Or call Room's DAO directly from ViewModel skipping repository implementation completely
Both of these options have sufficient drawbacks:
If I import android.arch.lifecycle.LiveData into my Repository interface than it would break the abstraction in Domain layer, as it is now depending on android architecture libraries.
If I call Room's DAO directly in the ViewModel as val entities: LiveData<List<Entity>> = database.entityDao.findByUsername(username) then I'm breaking the rule that all data access must be made using Reposiotry and I will need to create some boilerplate code for synchronization with remote storage etc.
How is it possible to achieve single data source approach using LiveData, Room's DAO and Clean architecure patterns?
Technically you are running into trouble because you don't want synchronous data fetching.
fun findByUsername(username: String) : List<T>
You want a subscription that returns to you a new List<T> each time there is a change.
fun findByUsernameWithChanges(username: String) : Subscription<List<T>>
So now what you might want to do is make your own subscription wrapper that can handle LiveData or Flowable. Of course, LiveData is trickier because you must also give it a LifecycleOwner.
public interface Subscription<T> {
public interface Observer<T> {
void onChange(T t);
}
void observe(Observer<T> observer);
void clear();
}
And then something like
public class LiveDataSubscription<T> implements Subscription<T> {
private LiveData<T> liveData;
private LifecycleOwner lifecycleOwner;
private List<Observer<T>> foreverObservers = new ArrayList<>();
public LiveDataSubscription(LiveData<T> liveData) {
this.liveData = liveData;
}
#Override
public void observe(final Observer<T> observer) {
if(lifecycleOwner != null) {
liveData.observe(lifecycleOwner, new android.arch.lifecycle.Observer<T>() {
#Override
public void onChange(#Nullable T t) {
observer.onChange(t);
}
});
} else {
Observer<T> foreverObserver = new android.arch.lifecycle.Observer<T>() {
#Override
public void onChange(#Nullable T t) {
observer.onChange(t);
}
};
foreverObservers.add(foreverObserver);
liveData.observeForever(foreverObserver);
}
}
#Override
public void clear() {
if(lifecycleOwner != null) {
liveData.removeObservers(lifecycleOwner);
} else {
for(Observer<T> observer: foreverObservers) {
liveData.removeObserver(observer);
}
}
}
public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
this.lifecycleOwner = lifecycleOwner;
}
}
And now you can use your repository
val subscription = repository.findByUsernameWithChanges("blah")
if(subscription is LiveDataSubscription) {
subscription.lifecycleOwner = this
}
subscription.observe { data ->
// ...
}
When similar question is asked about using RxJava, developers usualy answer, that is ok, and RxJava now is a language part, so, you can use it in domain layer. In my opinion - you can do anything, if it helps you, so, if using LiveData don't create problems - use it, or you can use RxJava, or Kotlin coroutines instead.
Use Flow as return type in your domain
since flow is part of Kotlin language, it's fully acceptable to use this type in your domain.
here is an example
Repository.kt
package com.example.www.myawsomapp.domain
import com.example.www.myawsomapp.domain.model.Currency
import com.example.www.myawsomapp.domain.model.Result
import kotlinx.coroutines.flow.Flow
interface Repository {
fun getCurrencies(): Flow<List<Currency>>
suspend fun updateCurrencies(): Result<Unit>
}
then in your data package you can implement it
package com.example.www.myawsomapp.data
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class RepositoryImpl #Inject constructor(
private val currencyDao: CurrencyDao,
private val api: CurrencyApi,
private val connectivity: Connectivity
) :
Repository {
override fun getCurrencies(): Flow<List<Currency>> =
currencyDao.getAll().map { list -> list.map { it.toDomain() } }
override suspend fun updateCurrencies(): Result<Unit> =
withContext(Dispatchers.IO) {
val rowsInDataBase = currencyDao.getCount()
if (rowsInDataBase <= 0) {
if (connectivity.hasNetworkAccess()) {
return#withContext updateDataBaseFromApi()
} else {
return#withContext Failure(HttpError(Throwable(NO_INTERNET_CONNECTION)))
}
} else {
return#withContext Success(Unit)
}
}
}
Note that
currencyDao.getAll().map { list -> list.map { it.toDomain() } }
from your dao you are receiving data class of data/model package, while ideally your viewmodel should receive data class of domain/model package so that you are mapping it to domain model
here is dao class
package com.example.www.myawsomapp.data.database.dao
import com.blogspot.soyamr.cft.data.database.model.Currency
import kotlinx.coroutines.flow.Flow
import com.blogspot.soyamr.cft.data.database.model.Currency
#Dao
interface CurrencyDao {
#Query("SELECT * FROM currency")
fun getAll(): Flow<List<Currency>>
}
then in your viewmodel you would convert flow to livedata
val currencies =
getCurrenciesUseCase()
.onStart { _isLoading.value = true }
.onCompletion { _isLoading.value = false }.asLiveData()

LiveData is abstract android

I tried initializing my LiveData object and it gives the error: "LiveData is abstract, It cannot be instantiated"
LiveData listLiveData = new LiveData<>();
In a ViewModel, you may want to use MutableLiveData instead.
E.g.:
class MyViewModel extends ViewModel {
private MutableLiveData<String> data = new MutableLiveData<>();
public LiveData<String> getData() {
return data;
}
public void loadData() {
// Do some stuff to load the data... then
data.setValue("new data"); // Or use data.postValue()
}
}
Or, in Kotlin:
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data
fun loadData() {
viewModelScope.launch {
val result = // ... execute some background tasks
_data.value = result
}
}
}
Since it is abstract (as #CommonsWare says) you need to extend it to a subclass and then override the methods as required in the form:
public class LiveDataSubClass extends LiveData<Location> {
}
See docs for more details
Yes, you cannot instantiate it because it is an abstract class. You can try to use MutableLiveData if you want to set values in the live data object. You can also use Mediator live data if you want to observe other livedata objects.
You need to use MutableLiveData and then cast it to its parent class LiveData.
public class MutableLiveData
extends LiveData
[MutableLiveData is] LiveData which publicly exposes setValue(T) and postValue(T) method.
You could do something like this:
fun initializeLiveData(foo: String): LiveData<String> {
return MutableLiveData<String>(foo)
}
So then you get:
Log.d("now it is LiveData", initializeLiveData("bar").value.toString())
// prints "D/now it is LiveData: bar"
I think much better way of achieving this is by using, what we call is a Backing Property, to achieve better Encapsulation of properties.
Example of usage:
private val _userPoints = MutableLiveData<Int>()// used as a private member
val userPoints: LiveData<Int>
get() {
return _userPoints
} //used to access the value inside the UI controller (Fragment/Activity).
Doing so maintains an editable MutableLiveData private to ViewModel class while, the read-only version of it is maintained as a LiveData, with a getter that returns the original value.
P.S. - notice the naming convention for both fields, using an (_) underscore. This is not mandatory but advised.

Categories

Resources