i'm working on project where i have to insert and delete data from room db , so basically i was using the old approach which is to implement Asynctask for background operations but since it is no longer recommended , i decided to use Rxjava instead , i tried to implement it but i'm not getting any result so far , and this is a piece of code where it shows the insertion of data
Completable.fromAction(new Action() {
#SuppressLint("CheckResult")
#Override
public void run() throws Exception {
recordingDb.insertRecording(modelUidd);
}
}).subscribeOn(Schedulers.io());
}
And this is the deletion method
public void DeleteData(modelUidd modelUidd) {
Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
recordingDb.delete(modelUidd);
}
}).subscribeOn(Schedulers.io());
}
So basically i tried to use completable with the operator fromaction , i'm not sure if what i implemented is correct or not , any help would appreciated guys , thank you
The problem is that you are actually not subscribing to the observables, so nothing is happening.
To subscribe to an observable, you have to call the .subscribe() method.
I suggest that your methods defined in your DAO classes (or you "repository" classes), such as DeleteData in your example, return the Observable. Then, you can call the method in the DAO to get the Observable and subscribe to it from (ideally) a ViewModel or, if not, directly from an Activity. The moment you call the subscribe you will trigger the actual insertion or deletion, and will get a response from the onSuccess or onError defined callbacks.
For example:
public class MyViewModel extends ViewModel {
private MyRepository myRepository;
private final CompositeDisposable disposables;
#Inject
public MyViewModel(MyRepository myRepository) {
...
this.myRepository = myRepository;
disposables = new CompositeDisposable();
...
}
public void callObservableInRepository() {
disposables.add(myRepository.myObservable()
.subscribe(onSuccess -> {...} , onError -> {...}));
}
#Override
protected void onCleared() {
disposables.clear();
}
}
You can also check these two other answers for more information:
About async operations in RxJava
Using CompositeDisposable in ViewModel
Related
I'm trying to learn MVVM to make my app's architecture more clean. But I'm having a hard time grasping how to create a "Domain" layer for my app.
Currently this is how the structure of my project is looking:
My View is the activity. My ViewModel has a public method that the activity can call. Once the method in the ViewModel is called, it calls a method in my Repository class which performs a network call, which then returns the data back to the ViewModel. I then update the LiveData in the ViewModel so the Activity's UI is updated.
This is where I'm confused on how to add a Domain layer to the structure. I've read a lot of Stackoverflow answers and blogs about the Domain layer and they mostly all tell you to remove all the business logic from the ViewModel and make a pure Java/Kotlin class.
So instead of
View --> ViewModel --> Repository
I would be communicating from the ViewModel to the Domain class and the Domain class would communicate with the Repository?
View --> ViewModel --> Domain --> Repository
I'm using RxJava to make the call from my ViewModel to the Repository class.
#HiltViewModel
public class PostViewModel extends ViewModel {
private static final String TAG = "PostViewModel";
private final List<Post> listPosts = new ArrayList<>();
private final MutableLiveData<List<Post>> getPostsLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> centerProgressLiveData = new MutableLiveData<>();
private final MainRepository repository;
#Inject
public PostViewModel(MainRepository repository) {
this.repository = repository;
getSubredditPosts();
}
public void getSubredditPosts() {
repository.getSubredditPosts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
centerProgressLiveData.setValue(true);
}
#Override
public void onNext(#NonNull Response response) {
Log.d(TAG, "onNext: Query called");
centerProgressLiveData.setValue(false);
listPosts.clear();
listPosts.addAll(response.getData().getChildren());
getPostsLiveData.setValue(listPosts);
}
#Override
public void onError(#NonNull Throwable e) {
Log.e(TAG, "onError: getPosts", e);
centerProgressLiveData.setValue(false);
}
#Override
public void onComplete() {
}
});
}
public class MainRepository {
private final MainService service;
#Inject
public MainRepository(MainService service) {
this.service = service;
}
public Observable<Response> getSubredditPosts() {
return service.getSubredditPosts();
}
}
Could someone please give me an example of how I could do it? I'm quite lost here
I had a hard time while trying to figure out the domain layer.
The most common example of it is the use case.
Your viewmodel won't communicate directly to the repository. As you said, you need viewmodel 》domain 》repository.
You may think of a usecase as a abstraction for every repository method.
Let's say you have a Movies Repository where you call a method for a movie list, another method for movie details and a third method for related movies.
You'll have a usecase for every single method.
What's the purpose of it?
Let's say you have a DetailActivity that communicate with a Detail Viewmodel. Your viewmodel doesn't need to know all the repository (what's the purpose of calling a movie list method on you Detail screen?). So, all your DetailViewModel will know is "Detail Usecase " (that calls the Detail method in repository).
Google has updated the architecture documentation few hours ago, take a look!
https://android-developers.googleblog.com/2021/12/rebuilding-our-guide-to-app-architecture.html?m=1&s=09
PS: Usecase is not a special android class, you do not need to inherent any behavior (as fragment, activity, viewmodel...) it's a normal class that will receive the repository as parameter.
You'll have something like:
Viewmodel:
function createPost(post Post){
createUseCase.create(post)
}
UseCase
function createPost(post Post): Response {
return repository.create(post)
}
I spent quite a bit of time trying to learn how to add a domain layer using RxJava by reading a lot of blogs and Stackoverflow answers, but all of them were missing the conversion of the response from the api call to what you'd like to display on screen (For example if the back end returns a username dave123 and you'd like to display by dave123).
I finally figured it out and the secret sauce was to use a RxJava .map() operator inside the UseCase class. I also decided to keep the RxJava call inside my ViewModel.
So in my Repository class I have a method that calls the Api and returns a type of Single<Response>. This is the raw json data the Api returns.
public class MainRepository {
private final MainService service;
private final PostDao postDao;
#Inject
public MainRepository(MainService service, PostDao postDao) {
this.service = service;
this.postDao = postDao;
}
public Single<Response> getResponse() {
return service.getSubredditPosts();
}
}
Inside my GetPostsUseCase class, I'm call the getResponse() method from the MainRepository and altering the Response by performing business logic on it (the stuff I want to display on the UI. In this case I add the String "by " to the username)
And the secret or the part I had alot of trouble understanding/figuring out how to do was converting the Type inside the Single<>. I used the .map() operator to change the return type and filter the Response to a List<Post>
public class GetPostsUseCase {
private final MainRepository mainRepository;
#Inject
public GetPostsUseCase(MainRepository mainRepository) {
this.mainRepository = mainRepository;
}
public Single<List<Post>> getSubredditPosts(){
return mainRepository.getResponse().map(response ->
getPostsFromResponse(response.getData().getChildren())
);
}
private List<Post> getPostsFromResponse(List<Child> listChildren) {
List<Post> listPosts = new ArrayList<>();
for (Child child : listChildren) {
Post post = child.getPost();
post.setCreatedBy("by " + post.getUsername());
listPosts.add(post);
}
return listPosts;
}
}
And this is how my ViewModel looks like
public class PostViewModel extends ViewModel {
private static final String TAG = "PostViewModel";
private final List<Post> listPosts = new ArrayList<>();
private final MutableLiveData<List<Post>> getPostsLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> centerProgressLiveData = new MutableLiveData<>();
private final GetPostsUseCase getPostsUseCase;
#Inject
public PostViewModel(GetPostsUseCase getPostsUseCase) {
this.getPostsUseCase = getPostsUseCase;
getSubredditPosts();
}
public void getSubredditPosts() {
getPostsUseCase.getSubredditPosts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<Post>>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
centerProgressLiveData.setValue(true);
}
#Override
public void onSuccess(#NonNull List<Post> list) {
Log.d(TAG, "onNext: Query called");
centerProgressLiveData.setValue(false);
listPosts.clear();
listPosts.addAll(list);
getPostsLiveData.setValue(listPosts);
}
#Override
public void onError(#NonNull Throwable e) {
centerProgressLiveData.setValue(false);
}
});
}
I couldn't find any blogposts or answers that had an example like this. Hopefully this helps anyone out there who is struggling to learn how to implement clean architecture with MVVM, Hilt, RXJava and a Domain layer.
If I did do something incorrectly or not considered clean architecture please let me know.
As the document says about the Livedata#postValue(T):
If you called this method multiple times before a main thread executed
a posted task, only the last value would be dispatched.
But I really need to call postValue() multiple times on a Livedata in a short time. I am designing a application with downloading function. I write the downloading code inside an AndroidViewmodel, and start a new Thread for each asyns downloading task. As soon as each task is completed, it postValue() to a Livedata, so there may be many tasks posting value to the same Livedata at the same time, which resulted that only the last posting works.
Is there anyway to enable me to postValue() multiple times to a Livedata at the same time and ensure all of the relative Observer react to all the posting?
------My code example is as below---------------
In the AndroidViewModel class:
private MutableLiveData<DataHolder_Task> completedTask;
......
public void bulkDownload(List<DataHolder_Task> tasks){
new Thread(new Runnable() {
#Override
public void run() {
for(DataHolder_Task task:tasks)
{
//download() blocks untill task completed
download(task);
completedTask.postValue(task);
}
}
}).start();
}
public MutableLiveData<DataHolder_Task> getCompletedTask() {
if(completedTask==null){
completedTask=new MutableLiveData<>();
}
return completedTask;
}
In an Activity:
androidViewModel.bulkDownload(a_list_of_tasks)
androidViewModel.getCompletedTask().observe(this, new Observer<DataHolder_Task>() {
#Override
public void onChanged(DataHolder_Task task) {
doSomething(task)
}
});
I achieved this by subclass MutableLiveData, add a queue to buffer the bulk live datas, then dispatch one by one to the main thread:
import androidx.lifecycle.MutableLiveData
import java.util.*
class QueuedMutableLiveData<T> : MutableLiveData<T>() {
private val queue = LinkedList<T?>()
override fun setValue(value: T) {
super.setValue(value)
synchronized(queue) {
queue.pollFirst()
queue.peekFirst()?.run {
super.postValue(this)
}
}
}
override fun postValue(value: T?) {
synchronized(queue) {
queue.add(value)
if (queue.size == 1) {
super.postValue(value)
}
}
}
}
You can try to use .setValue() (or .value for Kotlin) instead of .postValue(). Works for me.
LiveData is designed as a life-cycle aware observable object that informs a UI of the latest state. It is not an event stream.
Is there any alternative solution for that?
Try a Kotlin Flow.
Is this a good approach or I've just found a nasty workaround?
I'm using MediatorLiveData class because seems useful to update the source of a LiveData object.
I mean, the majority of tutorials that I've found on internet just use Livedata or MutableLivedata without a dynamic source, in example:
fun search(/*no input params*/): Call<List<Person>>
But in my case, I have the following web service that performs a search by name:
interface APIServidor {
#GET("search")
fun search(#Query("name") name: String): Call<List<Person>>
}
public class PeopleRepository {
public LiveData<List<Person>> search(String name){
final MutableLiveData<List<Person>> apiResponse = new MutableLiveData<>();
Call<List<Person>> call = RetrofitService.Companion.getInstance().getApiServer().search(name);
call.enqueue(new Callback<List<Person>>() {
#Override
public void onResponse(#NonNull Call<List<Person>> call, #NonNull Response<List<Person>> response) {
if (response.isSuccessful()) {
apiResponse.postValue(response.body());
}
}
#Override
public void onFailure(#NonNull Call<List<Person>> call, #NonNull Throwable t) {
apiResponse.postValue(null);
}
});
return apiResponse;
}
}
Then in the viewmodel class I'm adding source per new request.
public class SearchViewModel extends ViewModel {
private MediatorLiveData<List<Person>> mApiResponse;
private PeopleRepository mApiRepo;
public SearchViewModel() {
mApiResponse = new MediatorLiveData<>();
mApiRepo = new PeopleRepository();
}
public LiveData<List<Person>> getPlayers() {
return mApiResponse;
}
public void performSearch(String name){
mApiResponse.addSource(mApiRepo.search(name), new Observer<List<Person>>() {
#Override
public void onChanged(List<Person> apiResponse) {
mApiResponse.setValue(apiResponse);
}
});
}
}
Activity
bt_search.setOnClickListener {
val player_name = et_player.text.toString()
viewModel.performSearch(player_name)
}
Project scope
I'm in a personal project
Goals
Use MVVM + Live data + Repository pattern
Problem
I've only found tutorials with a simple approach: observe a LiveData object that access to a repository object and fetch data only once.
In example: Fetch all people (select * from people) from web service.
My case: Fetch people that mach a name (select * from people where name=?) from web service.
https://medium.com/#elye.project/kotlin-and-retrofit-2-tutorial-with-working-codes-333a4422a890
https://medium.com/#sriramr083/error-handling-in-retrofit2-in-mvvm-repository-pattern-a9c13c8f3995
Doubts
Is a good idea use MediatorLiveData class to merge all requests took from user input?
Should I use MutableLiveData and change the repository class and use a custom clousure?
Is there a better approach?
I was using this pattern with MediatorLiveData as well, but it forms an issue.
From the user perspective it seems to function just fine, but one problem here is that every time you call performSearch() the repository creates a new LiveData object which is additionally added to MediatorLiveData via addSource().
An idea might be to have the repository create the MutableLiveData object only once and on consecutive call just update it's value. So f.e. MutableLiveData<List<Person>> apiResponse; would be a non initialized private field that gets initialized in the search() method.
Eg. if (apiResponse == null) apiResponse = new MutableLiveData();
I have defined and interface, with an endpoint that returns JSON. Retrofit converts this JSON into MyObject. It could be also a list, map, etc, it doesn't matter now.
This is how I subscribe.
subscription = Retrofit.create(MyApi.class)
.doSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MyObject>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MyObject myObject) {
}
});
My question is:
Is it possible that onNext is called more than once?
If yes, in which occasion?
In your case, no it's impossible, of course if you do not emit more items in doSomething() method.
But there is another, quite usual cases, for instance, if you use Local first approach and subscribing on hot observable which will emit new item each time when data in data base has change.
E.g. using retrofit:
#Override
public Observable<List<FollowMeUser>> getFollowMeUsers() {
return realm.where(FollowMeUser.class)
.findAll()
.asObservable()
.filter(RealmResults::isLoaded);
}
getFollowMeUsers()
.subscribe(users -> {Timber.d("saved data has changed")}, Timber::e);
Each time when you will insert/modify/delete FollowMeUser collection, all subscribers of getFollowMeUsers will be notified.
If your retrofit returns an array/list of data, onNext is called multiple times.
But if your retrofit returns a single data objext, onNext will be called only once.
Example:
//POJO
class User {
int userId;
String UserName;
}
//POJO
class UserData {
List<User> users;
}
interface RetrofitGithub {
#GET("...")
Observable<List<User>> getUsers();
#GET("...")
Observable<UserData> getUserData();
}
If you subscribe to getUsers() onNext will be called multiple N times.(N = size of the list)
If you subscribe to getUserData() onNext will be called only once.
I want to test UseCase object, in this specific case there is a LoginUseCase, which looks like this:
public class LoginUseCase implements RxUseCase<AuthResponse, AuthCredentials> {
ApiManager mApiManager;
public LoginUseCase(ApiManager apiManager) {
mApiManager =apiManager;
}
#Override
public Observable<AuthResponse> execute(final AuthCredentials authCredentials) {
return Observable.just(1)
.delay(750, TimeUnit.MILLISECONDS)
.flatMap(l -> mApiManager.login(authCredentials.getLogin(), authCredentials.getPassword()));
}
}
I wrote simple test:
#RunWith(MockitoJUnitRunner.class)
public class LoginUseCaseTest {
private LoginUseCase mLoginUseCase;
#Mock ApiManager mApiManager;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mLoginUseCase = new LoginUseCase(mApiManager);
}
#Test
public void testShouldThrowsError() throws Exception {
TestSubscriber<AuthResponse> testSubscriber = new TestSubscriber<>();
doReturn(Observable.error(new Throwable())).when(mApiManager).login("", "");
mLoginUseCase
.execute(new AuthCredentials("", ""))
.subscribe(testSubscriber);
testSubscriber.assertNoErrors();
}
}
But this test always passes and I don't know how mock error observable in this case.
EDIT: I've chaged testShouldThrowsError() according to SkinnyJ, but test still passes, any sugestions?
You need to call awaitTerminalEvent() on your test subscriber before assertions.
Because now, you schedule a delay to be run on Schedulers.computation and your test method successfully completes before completion of observable.
Alternative approach would be to pass scheduler as argument to execute method, or store scheduler in your usecase. This way, during test you can pass Schedulers.immediate() and your test will run on current thread (which will block execution for specified delay).
And last approach is to call toBlocking() on observable, but I think that passing scheduler is preferred choice. And there is no way to add this operator to your current observable.