How update Room DB with API response if there is not data? - retrofit2

I am trying to update my database (room) with the response of my call api if my database table is empty and return to my recyclerview
API
#GET("user/patient/")
Flowable<ResponsePatient> getPatients(#Header("Authorization") String userToken);
FactoryData.class
public Flowable<List<Patient>> getPatientFromApi(){
String token = preferences.getValue(SDConstants.token);
return apiNetwork.getPatients(token).map(new Function<ResponsePatient, List<Patient>>() {
#Override
public List<Patient> apply(ResponsePatient responsePatient) throws Exception {
return PatientMapper.transform(responsePatient);
}
});
}
public Flowable<List<Patient>> listPatient(){
return appDataBase.patientDao().listPatient()
.switchIfEmpty(getPatientFromApi())
.doOnNext(s -> appDataBase.patientDao().saveAll(s));
}
I not sure how to do this. I appreciate any help.

Finally I can solve my problem.
public Flowable<List<Patient>> getPatient(){
return getPatientFromlocal()
.take(1)
.filter(list -> !list.isEmpty())
.switchIfEmpty(
Flowable.defer(() -> getPatientFromApi()));
}
public Flowable<List<Patient>> getPatientFromlocal(){
return appDataBase.patientDao().listPatient();
}
public Flowable<List<Patient>> getPatientFromApi(){
String token = preferences.getValue(SDConstants.token);
return apiNetwork.getPatients(token).map(new Function<ResponsePatient, List<Patient>>() {
#Override
public List<Patient> apply(ResponsePatient responsePatient) throws Exception {
return PatientMapper.transform(responsePatient);
}
}).doOnNext(new Consumer<List<Patient>>() {
#Override
public void accept(List<Patient> patients) throws Exception {
appDataBase.patientDao().saveAll(patients);
}
});
}

Related

How to wait for response in request inside other request async

I would like to know how to deal with my problem. I am trying to send request in android studio (using retrofit2), and in "onResponse" method send other request which assing List to object from the first request. Problem is that the first request finish before the second can download the data and assign empty list. Some spaghetti i know, but i hope that code will help to understand my problem.
First request method
private void getTrainingPlans()
{
INodeJS inter= RetrofitClient.getGsonInstance().create(INodeJS.class);
retrofit2.Call<List<PlanTreningowy>> call=inter.getTrainingPlans(User.getId());
call.enqueue(new Callback<List<PlanTreningowy>>() {
#Override
public void onResponse(Call<List<PlanTreningowy>> call, Response<List<PlanTreningowy>> response) {
if(!response.isSuccessful())
{
Toast.makeText(getContext(),"Cant download data",Toast.LENGTH_SHORT).show();
return;
}
if (response.body() != null)
{
plany_treningowe=response.body();
for(PlanTreningowy plan:plany_treningowe)
{
//second request method
getExercisesFromTrainingPlan(plan.id);
//trying assign data from request to object but its empty
plan.exercises= cwiczenia_plan_treningowy;
}
nazwa_planu.setText(plany_treningowe.get(0).getTytul());
opis_planu.setText(plany_treningowe.get(0).getOpis());
//getExercisesName(plany_treningowe.get(0).getExercises());
TreningAdapter treningAdapter=new TreningAdapter(getContext(),t_nazwa, t_nazwa.size(),PlanTreningowyFragment.this);
recyclerView.setAdapter(treningAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
}
else
{
nazwa_planu.setText("Brak planów");
opis_planu.setText("Brak");
}
}
#Override
public void onFailure(Call<List<PlanTreningowy>> call, Throwable t) {
}
});
}
Second request method (nested)
Call<List<Exercise>> call=inter.getExercisesFromTrainingPlan(id_planu);
call.enqueue(new Callback<List<Exercise>>() {
#Override
public void onResponse(Call<List<Exercise>> call, Response<List<Exercise>> response) {
if(!response.isSuccessful())
{
Toast.makeText(getContext(),"Nie udało się wczytać ćwiczeń",Toast.LENGTH_SHORT).show();
return;
}
if (response.body() != null)
{ //assign data to list
cwiczenia_plan_treningowy=response.body();
Log.e("dlugosc" , String.valueOf(cwiczenia_plan_treningowy.size()));
return;
}
}
#Override
public void onFailure(Call<List<Exercise>> call, Throwable t) {
}
}); ```

How to showing response string in a textview?

Result: {"Status":"OK","Message":"Report Genarated.","Result":"JVBERi0xLjUKJeLjz9MKMSAwIG9iago8PC9UeXBlL0ZvbnQvU3VidHlw"}
I am getting these response from the post api calling.Now How can i am get the Result string value.
Code:
holder.downloadBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
manager = new UtilityBillManager(context, AppBaseController.get().getUserSettings());
manager.UserTransactionReceiptReport(listener,billReceiptReport); //This the api calling
}
});
}
private final TransactionReportListener listener = new TransactionReportListener() {
#Override
public void didFetch(UserReportResponse response, String message) {
}
#Override
public void didError(String message) {
}
};
UserReponse is a Model which have String status,message, result.
Just use getResult() getter of Model class
See the below code
// add this condition to prevent app crashing.
if (response.body().getResult()!=null){
textView.setText(response.body().getResult()); // getResult() is your getters of the Model Class.
}
Hope it helps.

OKHttp DNS lookup asynchronously

public class OkHttpDns implements Dns {
#NotNull
#Override
public List<InetAddress> lookup(#NotNull String hostname) throws UnknownHostException {
MyLookUpUtility.getInstance.lookup(hostname, new MyLookUpUtility.lookupCallback()
{
#Override
public void onlookupResponseSuccess(JSONObject nslookupResponseJSON) {
Log.d("LookupResponse", nslookupResponseJSON.toString());
}
#Override
public void onlookupResponseFailure(String errCode) {
Log.d("LookupResponse", "Error Code : "+errCode);
}
});
}
}
In the above code, lookup method of DNS interface of OKHttp wants to return immediately. But my custom NSLookupUtility is an asynchronous call and I will have the ip address of the hostname only after a while. How to solve this problem? how to make the synchronous call to wait for the asynchronous call within it ?
Take a look at CompletableFuture. You’ll create an instance in lookup(), kickoff the async lookup, and then call future.get(). When your async call completes, call future.complete().
#Override
public List<InetAddress> lookup(#NotNull String hostName) throws UnknownHostException {
completableFuture = new CompletableFuture<>();
performLookUp(hostName);
try {
String ipAddress = completableFuture.get();
if (ipAddress != null) {
List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ipAddress));
return inetAddresses;
}
} catch (ExecutionException e) {
Log.d(TAG, "Error : ExecutionException : "+e );
e.printStackTrace();
} catch (InterruptedException e) {
Log.d(TAG, "Error : InterruptedException : "+e );
e.printStackTrace();
}
return Dns.SYSTEM.lookup(hostName);
}
private void performLookUp(#NotNull String hostName) {
MyUtiluty.getInstance().lookup(hostName,
new MyCallBack() {
#Override
public void onSuccess(String ip) {
completableFuture.complete(ip);
}
#Override
public void onFailure(String errCode) {
completableFuture.complete(null);
}
});
}

to invoke interface method 'retrofit2.Call on a null object reference

how can i solve null object while sending post request
error says
to invoke interface method 'retrofit2.Call com.itgrepnet.foodbundle.remote.UserService.addUser(com.itgrepnet.foodbundle.model.User)' on a null object reference
in AddUserActivity
btnSubmit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
addNewUser();
}
private void addNewUser() {
User u = new User();
u.setFirstname(first_name.getText().toString());
u.setLastname(last_name.getText().toString());
u.setEmail(email.getText().toString());
u.setPassword(password.getText().toString());
Call<User> call = userService.addUser(u);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
Toast.makeText(getApplicationContext(), "User Created Successfully!", Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
Log.e("Error: ", t.getMessage());
}
});
}
});
UserService.java
#POST("user/")
Call<User> addUser(#Body User user);
RetrofitClient.java
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String url) {
if (retrofit == null) {
retrofit = new Retrofit.Builder().baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}

RxJava subscribe onNext is not called when adding element asynchronously

I have a Observable like this
Observable<String> gitHubRepoModelObservable;
I have this code
repoNames = new ArrayList<String>();
gitHubRepoModelObservable = Observable.fromIterable(repoNames);
repoNames.add("Hello");
gitHubRepoModelObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
System.out.println(s);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
repoNames is just a list of string. When I am adding a string "hello" manually the onNext is getting called but when I am adding string from a API call like bellow
call.enqueue(new Callback<List<GitHubRepoModel>>() {
#Override
public void onResponse(Call<List<GitHubRepoModel>> call, Response<List<GitHubRepoModel>> response) {
for (GitHubRepoModel repo : response.body()) {
repoNames.add(repo.getName());
}
}
#Override
public void onFailure(Call<List<GitHubRepoModel>> call, Throwable t) {
}
});
I am adding strings from the API into the repoNames the "onNext" is not getting called.
I have seen
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
can be added while initializing retrofit but I want to better understand the rxjava so in this experiment it is not working.
Please help!
It can't not be work.
When you create you api request and try subscribe you list is emty, so Observable does not work.
You need to create Observable such, that your subcribe will run your request!
Observable<String> gitHubRepoModelObservable = Observable.create(
new Observable.OnSubscribe<String>() {
#Override
public void call(final Subscriber<? super String> sub) {
call.enqueue(new Callback<List<GitHubRepoModel>>() {
#Override
public void onResponse(Call<List<GitHubRepoModel>> call, Response<List<GitHubRepoModel>> response) {
for (GitHubRepoModel repo : response.body()) {
sub.onNext(repo.getName()); //send result to rx
}
sub.onCompleted();
}
#Override
public void onFailure(Call<List<GitHubRepoModel>> call, Throwable t) {
}
});
}
}
);
gitHubRepoModelObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onNext(String s) {
System.out.println(s);
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
});
Why would onNext get called if you are just adding element to plain List?
In the first example you are seeing onNext being called because modified list is passed through the stream during subscribe.
Create Subject ex. PublishSubject and pass list to Subject.onNext in onResponse, subscribe to it and you will get what you want.
Second option is adding RxJava2CallAdapterFactory and return Observable<Response<List<GithubRepoModel>>>. This way you don't need to create stream yourself.

Resources