How to wait for response in request inside other request async - android-studio

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) {
}
}); ```

Related

Spring Boot GRPC: ServerIntereceptor to read data in the request, and set it in the response

There is a field called "metadata" (not to be confused with GRPC metadata) that is present in every request proto that comes to the GRPC service:
message MyRequest {
RequestResponseMetadata metadata = 1;
...
}
And the same field is also present in all responses:
message MyResponse {
RequestResponseMetadata metadata = 1;
...
}
I am trying to write a ServerInterceptor (or something else, if it works) to read the "metadata" field from the request, keep it somewhere, and then set it in the response once done processing the request.
Attempt 1: ThreadLocal
public class ServerInterceptor implements io.grpc.ServerInterceptor {
private ThreadLocal<RequestResponseMetadata> metadataThreadLocal = new ThreadLocal<>();
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
final Metadata requestHeaders,
ServerCallHandler<ReqT, RespT> next) {
return new SimpleForwardingServerCallListener<ReqT>(
next.startCall(
new SimpleForwardingServerCall<ReqT, RespT>(call) {
#Override
public void sendMessage(RespT message) {
super.sendMessage(
(RespT)
MetadataUtils.setMetadata(
(GeneratedMessageV3) message, metadataThreadLocal.get()));
metadataThreadLocal.remove();
}
},
requestHeaders)) {
#Override
public void onMessage(ReqT request) {
// todo nava see if ReqT can extend GenericV3Message
var metadata = MetadataUtils.getMetadata((GeneratedMessageV3) request);
metadataThreadLocal.set(metadata);
super.onMessage(request);
}
};
}
}
I tried to use ThreadLocal, to later realise that sendMessage and onMessage need not necessary to be on the same thread.
Attempt 2: GRPC Context
public class ServerInterceptor implements io.grpc.ServerInterceptor {
public static final Context.Key<RequestResponseMetadata> METADATA_KEY = Context.key("metadata");
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
final Metadata requestHeaders,
ServerCallHandler<ReqT, RespT> next) {
return new SimpleForwardingServerCallListener<ReqT>(
next.startCall(
new SimpleForwardingServerCall<ReqT, RespT>(call) {
#Override
public void sendMessage(RespT message) {
super.sendMessage(
(RespT)
MetadataUtils.setMetadata(
(GeneratedMessageV3) message, METADATA_KEY.get()));
}
},
requestHeaders)) {
#Override
public void onMessage(ReqT request) {
var metadata = MetadataUtils.getMetadata((GeneratedMessageV3) request);
var newContext = Context.current().withValue(METADATA_KEY, metadata);
oldContext = newContext.attach();
super.onMessage(request);
}
};
}
}
I am planning to detach the context in a onComplete(), but before it reaches there itself, METADATA_KEY.get() in sendMessage returns null, while I was expecting it to return the data.
Even before hitting the sendMessage() function, I get this in the console, indicating that I am doing something wrong:
3289640 [grpc-default-executor-0] ERROR i.g.ThreadLocalContextStorage - Context was not attached when detaching
java.lang.Throwable: null
at io.grpc.ThreadLocalContextStorage.detach(ThreadLocalContextStorage.java:48)
at io.grpc.Context.detach(Context.java:421)
at io.grpc.Context$CancellableContext.detach(Context.java:761)
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:39)
at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
How do I read data when a request is received, store it somewhere and use it when the response is send back?
You can use Metadata to pass values from the request to the response:
public class MetadataServerInterceptor implements ServerInterceptor {
public static final Metadata.Key<byte[]> METADATA_KEY = Metadata.Key.of("metadata-bin", Metadata.BINARY_BYTE_MARSHALLER);
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
var serverCall = new ForwardingServerCall.SimpleForwardingServerCall<>(call) {
#Override
public void sendMessage(RespT message) {
byte[] metadata = headers.get(METADATA_KEY);
message = (RespT) MetadataUtils.setMetadata((GeneratedMessageV3) message, metadata);
super.sendMessage(message);
}
};
ServerCall.Listener<ReqT> listenerWithContext = Contexts.interceptCall(Context.current(), serverCall, headers, next);
return new ForwardingServerCallListener.SimpleForwardingServerCallListener<>(listenerWithContext) {
#Override
public void onMessage(ReqT message) {
byte[] metadata = MetadataUtils.getMetadata((GeneratedMessageV3) message);
headers.put(METADATA_KEY, metadata);
super.onMessage(message);
}
};
}
}
Note: Since it is not possible to put the instance of RequestResponseMetadata in the metadata (at least without implementing a custom marshaller), you can save it there as a byte array. You can use toByteArray() on your RequestResponseMetadata object to get byte[] and RequestResponseMetadata.#parseFrom(byte[]) to get the object from byte[].

When are we getting the exact message "timeout" in Retrofit onFailure()?

I got a SocketTimeoutException. In the onFailure method, when I call t.getMessage(), it returns exactly "timeout"
Here is my code:
private void getCouponsService() {
APIInterface mObjInterface = APIClient.getClient().create(APIInterface.class);
Call<CouponsResponseModel> call = mObjInterface.getCoupons();
call.enqueue(new Callback<CouponsResponseModel>() {
#Override
public void onResponse(Call<CouponsResponseModel> call, Response<CouponsResponseModel> response) {
if (response.code() == 200) {
// Handle success response
}
}
#Override
public void onFailure(Call<CouponsResponseModel> call, Throwable t) {
String message = t.getMessage();
}
});
}
The value of message is exactly "timeout". In which scenario are we supposed to get that message? I expected the output to be Unable to resolve host "abc.domainname.in": No address associated with hostname.

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.

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

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);
}
});
}

How to manage session between cakephp version 2.x and android for subsequent call using retrofit2

#GET("product/allProduct.json;cakephp={session_key}")
Call<Product> getProductData(#Path("cakephp") String session_Key);
Call<Product> call=apiInterface.getAssetsData(session_key);
call.enqueue(new Callback<Product>() {
#Override
public void onResponse(Call<Product> call, Response<Product> response) {
Log.d("TAG","success"+new Gson().toJson(response.body()));
}
#Override
public void onFailure(Call<Product> call, Throwable t) {
Log.d(TAG, "onResponseFail ");
}
});
I am getting response null from above code,please provide some inputs if i am missing anything.

Resources