I am using .aar library in my app.
It has one Interface for the Network delegation which I need to overwrite.
ApiResponse executeApiCall(String url, HTTPMethod method, String params)
I am using Retrofit for the network calls. I need to convert the Synchronous call to USE asynchronous.
#Override
public ApiResponse executeApiCall(String url, HTTPMethod method, String params) {
ApiResponse apiResponse;
try {
// Synchronous Call
Call<String> call = RestClient.get().getStringResponse(url, method, params);
Response<String> response = call.execute();
apiResponse = new ApiResponse(response.code(), response.body());
} catch (Exception e) {
apiResponse = new ApiResponse();
}
return apiResponse;
}
Now I am stuck in how to use Asynchronous call within the Network Interface I must overwrite.
#Override
public ApiResponse executeApiCall(String url, HTTPMethod method, String params) {
ApiResponse apiResponse;
// Asynchronous Call
Call<String> call = RestClient.get().getStringResponse(url, method, params);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(#NotNull Call<String> call, #NotNull Response<String> response) {
if (response.isSuccessful()) {
apiResponse = new ApiResponse(response.code(), response.body());
}
}
#Override
public void onFailure(#NotNull Call<String> call, #NotNull Throwable t) {
apiResponse = new ApiResponse();
}
});
return apiResponse;
}
I can not change the Network delegation interface. I must overwrite it and I need to use retrofit Asynchronous.
Your feedback is most appreciated. Thanks guys.
I found some code on a different stack overflow thread. Here is how you could use the code to achieve what you are looking for.
Here is the class
public abstract class AsyncRunnable<T> {
protected abstract void run(AtomicReference<T> notifier);
protected final void finish(AtomicReference<T> notifier, T result) {
synchronized (notifier) {
notifier.set(result);
notifier.notify();
}
}
public static <T> T wait(AsyncRunnable<T> runnable) {
final AtomicReference<T> notifier = new AtomicReference<>();
// run the asynchronous code
runnable.run(notifier);
// wait for the asynchronous code to finish
synchronized (notifier) {
while (notifier.get() == null) {
try {
notifier.wait();
} catch (InterruptedException ignore) {}
}
}
// return the result of the asynchronous code
return notifier.get();
}
}
You can use it like this
ApiResponse result = AsyncRunnable.wait(new AsyncRunnable<ApiResponse>() {
#Override
public void run(final AtomicReference<String> notifier) {
// here goes your async code, e.g.:
new Thread(new Runnable() {
#Override
public void run() {
Call<String> call =
RestClient.get().getStringResponse(url, method, params);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(#NotNull Call<String> call,
#NotNull
Response<String> response) {
if (response.isSuccessful()) {
apiResponse = new
ApiResponse(response.code(),
response.body());
finish(notifier, apiResponse);
}
}
#Override
public void onFailure(#NotNull Call<String> call, #NotNull Throwable t) {
apiResponse = new ApiResponse();
finish(notifier, apiResponse);
}
});
}
}).start();
}
});
We wait for the response to come from the callback and notify the AsyncRunnable class.
I feel you, I had a similar problem with a library that needed to do heavy work (non-UI) on the UI thread. Yep, sounds like a psycopath's lib. Coroutines runBlocking are your friend. I realize that you're asking for a rx-java solution, but can't beat coroutines for this.
/**
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
fun main() {
val apiResponse = executeApiCall()
println(apiResponse) // prints "response"
println("goodbye, world!!!")
}
/**
* Synchronous function that waits for coroutine to finish
*/
fun executeApiCall():String {
return runBlocking(Dispatchers.IO){
retrofitCall()
}
}
/**
* Method that invokes Retrofit call in the background.
*/
suspend fun retrofitCall():String{
// RestClient.get() ...
return "response"
}
Note that if you call executeApiCall from UI thread you still will block the UI thread. So possible you need something like lifecycleScope.launch{executeApiCall() } if you're inside an activity or fragment (gradle dependency androidx.lifecycle:lifecycle-runtime-ktx:2.1.0), or CoroutineScope(Dispatchers.IO).launch { executeApiCall() } otherwise.
Related
In MainActivityViewModel class i have one Getter method that returns an instance of CurrentWeather (pojo class) and this method needs response from OnResponse method but I get null for first time.
The first methods invoke from MainActivity, viewModel is not null but the currentWeather instance is.
MainActivityViewModel viewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
currentWeather = viewModel.getCurrentWeather();
I don't know if I can ask to wait for a moment before return currentWeather in first method or not.
public class MainActivityViewModel extends ViewModel implements Callback<ResponseBody> {
private CurrentWeather currentWeather;
public CurrentWeather getCurrentWeather() {
if (currentWeather == null) {
createCurrentWeather("London");
}
return currentWeather;
}
public void createCurrentWeather(String city) {
RetrofitApiManager.getInstance().getCurrentWeatherApi(this, city);
}
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
ResponseBody body = response.body();
try {
String serverResponde = body.string();
Timber.e(serverResponde);
Gson gson = new Gson();
currentWeather = gson.fromJson(serverResponde, CurrentWeather.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
}
It's because it takes a while before a response is returned.
Usually, you need a LiveData object to get results from background tasks.
In your MainActivityViewModel, add the following:
private MutableLiveData currentWeatherData = new MutableLiveData<CurrentWeather>();
public LiveData<CurrentWeather> getCurrentWeatherData() {
return currentWeatherData;
}
When you get response, update your LiveData
currentWeather = gson.fromJson(serverResponde, CurrentWeather.class);
currentWeatherData.postValue(currentWeather);
In your activity, you need to observe this LiveData.
viewModel.getCurrentWeatherData().observe(this, new Observer<CurrentWeather>() {
#Override
public void onChanged(CurrentWeather c) {
// Do whatever you want with c.
}
});
I have a Restful API whos return a Java Object for me. When return that object it is still empty, because the async thread is still working. How can get that response and return then to my Presenter and it directs the correct response to the view?
That is my retrofit call:
public String checkUser(final ModelUser modelUser) throws IOException {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UserRetrofitAPI.BASE_SERVICE)
.addConverterFactory(GsonConverterFactory.create())
.build();
UserRetrofitAPI userRetrofitAPI = retrofit.create(UserRetrofitAPI.class);
Call<ModelUser> requestCheckUser = userRetrofitAPI.checkUser(modelUser.getUser(), modelUser.getPassword());
requestCheckUser.enqueue(new Callback<ModelUser>() {
#Override
public void onResponse(Call<ModelUser> call, retrofit2.Response<ModelUser> response) {
if(!response.isSuccessful()){
myModelUser = new ModelUser(modelUser.getUser(),modelUser.getPassword(), String.valueOf(response.code()));
} else {
ModelUser modelUserChecked = response.body();
myModelUser = modelUserChecked;
}
}
#Override
public void onFailure(Call<ModelUser> call, Throwable t) {
Exception ex = new Exception(t);
myModelUser = new ModelUser(modelUser.getUser(), modelUser.getPassword(), ex.toString());
}
});
return myModelUser.getResponse();
}
when I do this debugging, it works, by processing time.
help me?
You shouldn't return that directly.
As you mentioned Retrofit response is updated in background thread.
I would suggest to return requestCheckUser only and observe that in your Presenter
public Call<ModelUser> checkUser(final ModelUser modelUser) throws IOException {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UserRetrofitAPI.BASE_SERVICE)
.addConverterFactory(GsonConverterFactory.create())
.build();
UserRetrofitAPI userRetrofitAPI = retrofit.create(UserRetrofitAPI.class);
Call<ModelUser> requestCheckUser = userRetrofitAPI.checkUser(modelUser.getUser(), modelUser.getPassword());
return requestCheckUser;
}
Observe response of that call in Presenter and perform required operations as follows
checkUser(modelUser).enqueue(new Callback<ModelUser>() {
#Override
public void onResponse(Call<ModelUser> call, retrofit2.Response<ModelUser> response) {
if(!response.isSuccessful()){
myModelUser = new ModelUser(modelUser.getUser(),modelUser.getPassword(), String.valueOf(response.code()));
} else {
ModelUser modelUserChecked = response.body();
myModelUser = modelUserChecked;
}
}
#Override
public void onFailure(Call<ModelUser> call, Throwable t) {
Exception ex = new Exception(t);
myModelUser = new ModelUser(modelUser.getUser(), modelUser.getPassword(), ex.toString());
}
});
This would be the simple option and will satisfy this use case and scope.
You can use custom Interface Listeners if you don't prefer to write observer in Presenter.
I would recommend to look into RxJava and use it with Retrofit to convert this into more maintainable code
I have this method that I am trying to pull data from an API, and then update the text view. Everything works except getRecipeName doesn't finish after the "end Method" log. .getRecipeName() uses RetroFit to pull from an API.
I am currently learning MVP, Dagger, RxJava, and Butterknife all at once using
Mindork's Github page on MVP Architecture
I commented out the .subscribeOn and .observeOn to see the result difference and nothing changed.
#Override
public void onRandomButtonClicked() {
getMvpView().showLoading();
Log.e(TAG, "Random Method Open");
getCompositeDisposable().add(getDataManager()
.getRecipeName()
//.subscribeOn(getSchedulerProvider().io())
//.observeOn(getSchedulerProvider().ui())
.subscribe(new Consumer<String>() {
#Override
public void accept(String s) throws Exception {
Log.e(TAG, "accept");
getMvpView().updateTextView(title);
}
}));
Log.e(TAG, "end method");
}
Here is my getRecipeName() method
#Override
public Observable<String> getRecipeName() {
/*Create handle for the RetrofitInstance interface*/
GetDataService service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
Call<RecipeList> call = service.getRecipe();
call.enqueue(new Callback<RecipeList>() {
#Override
public void onResponse(#NonNull Call<RecipeList> call, #NonNull retrofit2.Response<RecipeList> response) {
Log.e("onResponse","Recipe is Successful = " + response.isSuccessful());
//if response is false then skip to avoid null object reference
if (response.isSuccessful()) {
RecipeList drinkRecipe = response.body();
List<Recipe> recipes = drinkRecipe.getDrinks();
jokeText = String.valueOf(recipes.size());
Recipe myRecipe = recipes.get(0);
jokeText = myRecipe.getStrDrink();
Log.e("On Response", "Result2: " + jokeText);
}
//jokeText = "null";
}
#Override
public void onFailure(Call<RecipeList> call, Throwable t) {
Log.e("On Response","Failure");
}
});
//return jokeText;
return Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
return jokeText;
}
});
}
Solution
So as the comments stated RxJava Adapter was the correct way to go. I will just post my working code on myself using the adapter. I found it very difficult to find a working example.
//single api call using retrofit and rxjava
#SuppressLint("CheckResult")
private void getRandomButtonClick(){
retrofit = RetrofitClientInstance.getRetrofitInstance();
retrofit.create(GetDataService.class).getRecipe()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleResults, this::handleError );
}
private void handleResults(RecipeList recipeList) {
int i = recipeList.getDrinks().size();
Log.e(TAG, "size is: "+ i);
Recipe recipe = recipeList.getDrinks().get(0);
getMvpView().updateTextView(recipe.getStrDrink());
}
private void handleError(Throwable t){
Log.e("Observer", "");
}
My Retrofit Client Instance
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
return retrofit;
}
My Interface
public interface GetDataService {
//#Headers({})
#GET("random.php")
Observable<RecipeList> getRecipe();
I found a great resource to reference for me to correctly implement this. Retrofit Android
The reason is because your observable is returning jokeText every time it is subscribed upon. It returns immediately after invocation and will not wait for your network operation.
One possible solution is to use the RxJavaCallAdapter. Link here: https://github.com/square/retrofit/tree/master/retrofit-adapters/rxjava2
It will automatically convert your API returns to observables. No need to manually invoke retrofit requests. Just process the response and convert it to your desired object from there.
Another approach would be to wrap your entire sequence in an Observable.create or Observable.fromAsync.
I use retrofit2 with rxjava extension.
I have a list of REST API urls and want to do this:
for each
check whether a corresponding file locally exists
if yes: call the API and store the response or the HTTP error
if not: store a customized error
return the list of those results
My problem is: apply returns (with an empty RequestResult) before the server response is received. I think, I understand why, but I don't know how to fix it, because I need to return a RequestResult and not the Retrofit observable.
How can this be solved?
Here is my code:
#GET
Observable<Response<ResponseBody>> enroll(#Url String url);
class RequestResult {
CustomException error;
Response<ResponseBody> response;
}
Observable<ClassOfListItem> observable = Observable.fromIterable(listOfItems);
observable
.flatMap(new Function<ClassOfListItem, ObservableSource<RequestResult>>() {
#Override
public ObservableSource<RequestResult> apply(ClassOfListItem listItem) throws Exception {
RequestResult requestResult = new RequestResult();
if (fileExists(listItem.url)) {
Observable<Response<ResponseBody>> callObservable = restAPI.enroll(listItem.url)
.subscribeOn(Schedulers.io());
callObservable
.subscribe(new DisposableObserver<Response<ResponseBody>>() {
#Override
public void onNext(Response<ResponseBody> responseBodyResponse) {
onPremiseEnrollmentResult.response = responseBodyResponse;
}
#Override
public void onError(Throwable e) {
onPremiseEnrollmentResult.error = new CustomException(e);
}
#Override
public void onComplete() {
}
});
}
else {
requestResult.error = new CustomException("file not found");
}
return Observable.just(requestResult);
}
}
.toList()
.observerOn(AndroidScheduler.mainThread())
.subscribe(new DisposableSingleObserver<List<RequestResult>>() {
#Override
public void onError(Throwable e) {
Log.d("onError", e.getMessage());
}
#Override
public void onSuccess(List<RequestResult> requestResults) {
// parse results
}
}
)
The flatMap() operator allows you to turn one observable into a different observable. You have a nested observer chain inside your apply() which is not part of the observer chain, so it will be empty because it has not completed yet.
To fix this, when the file exists, return the observable.
observable
.flatMap(new Function<ClassOfListItem, ObservableSource<RequestResult>>() {
#Override
public ObservableSource<RequestResult> apply(ClassOfListItem listItem) throws Exception {
RequestResult requestResult = new RequestResult();
if (fileExists(listItem.url)) {
return restAPI.enroll(listItem.url)
.subscribeOn(Schedulers.io());
}
return Observable.error( new CustomException("file not found") );
}
}
.toList()
.observerOn(AndroidScheduler.mainThread())
.subscribe(new DisposableSingleObserver<List<RequestResult>>() {
#Override
public void onError(Throwable e) {
Log.d("onError", e.getMessage());
}
#Override
public void onSuccess(List<RequestResult> requestResults) {
// parse results
}
}
If you need to capture both errors and successes into the list, then you can add map() operator to wrap RequestResult around the response and onErrorResumeNext() to wrap RequestResult around the error before the toList() operator.
If you are making api call on background thread then what you can do is invoke it synchronously....in your case your retrofit api method would change to following
Call<Response<ResponseBody>> enroll(#Url String url);
and you'd invoke by calling restAPI.enroll(listItem.url).execute()
I am beginner to android, am using Retrofit 2.0 for http calls. Since, I use same call in different activities, I created the function in non-activity class.
Here my retrofit calls in non-activity class code,
public class ServerRequests {
private static ServerRequests serverRequests = new ServerRequests();
public static ServerRequests getInstance(){
return serverRequests;
}
public LoginResponse ClientLogin(final LoginRequest request, Context context){
final ProgressDialog dialog = DialogueUtils.getInstance().showProgressDialog(context);
dialog.setMessage("Loading...");
dialog.show();
RestApi mApi = RetrofitProvider.getInstance().getRestApi();
Call<UserToken> call = mApi.clientLogin(request);
// Stores Login response
final LoginResponse loginResponse = new LoginResponse();
call.enqueue(new Callback<UserToken>() {
#Override
public void onResponse(Call<UserToken> call, Response<UserToken> response) {
dialog.dismiss();
if (response.isSuccessful()){
loginResponse.setMloginstatus(true);
loginResponse.setStatusCode(response.code());
loginResponse.setUserToken(response.body());
return;
}
// response isn't successful
loginResponse.setMloginstatus(false);
loginResponse.setStatusCode(response.code());
loginResponse.setMessage(response.message());
loginResponse.setUserToken(null);
}
#Override
public void onFailure(Call<UserToken> call, Throwable t) {
dialog.dismiss();
loginResponse.setMloginstatus(false);
loginResponse.setUserToken(null);
loginResponse.setMessage(t.getMessage());
loginResponse.setStatusCode(FAILURE_ERROR);
}
});
return loginResponse;
}
}
I will make call to above function in Activity class,
Here is the code,
LoginRequest request = new LoginRequest();
request.setPassword(PASSWORD);
request.setEmail(USER_NAME);
// Calling Login function
LoginResponse response = ServerRequests.getInstance().ClientLogin(request, this);
Here, before receiving response in activity class, next set of code lines are executing. So, Activity class doesn't waiting till function to return back to the calling sequence.
Can anyone please suggest me what's the better approach to do this.
Thank you
I think you can use a interface to handle callback.
Step1: Define an interface
public interface LoginListener{
public void success(Response<UserToken> response);
public void failed(String message);
}
Step2: Use this interface on the method that you use retrofit.
public LoginResponse ClientLogin(final LoginRequest request, Context context, LoginListener listener){
final ProgressDialog dialog = DialogueUtils.getInstance().showProgressDialog(context);
dialog.setMessage("Loading...");
dialog.show();
RestApi mApi = RetrofitProvider.getInstance().getRestApi();
Call<UserToken> call = mApi.clientLogin(request);
// Stores Login response
final LoginResponse loginResponse = new LoginResponse();
call.enqueue(new Callback<UserToken>() {
#Override
public void onResponse(Call<UserToken> call, Response<UserToken> response) {
dialog.dismiss();
listener.success(response);
}
#Override
public void onFailure(Call<UserToken> call, Throwable t) {
dialog.dismiss();
listener.failed("message error");
}
});
return loginResponse;
}
I hope, This way can help you.
You can't return the result from your method, because you're making the Retrofit call asynchronously. Use a callback instead.
public interface GenericCallback<T> {
void success(T result);
void failure(... whatever you need);
}
public void ClientLogin(final LoginRequest request, Context context, final GenericCallback<LoginResponse> callback){
final ProgressDialog dialog = DialogueUtils.getInstance().showProgressDialog(context);
dialog.setMessage("Loading...");
dialog.show();
RestApi mApi = RetrofitProvider.getInstance().getRestApi();
Call<UserToken> call = mApi.clientLogin(request);
// Stores Login response
final LoginResponse loginResponse = new LoginResponse();
call.enqueue(new Callback<UserToken>() {
#Override
public void onResponse(Call<UserToken> call, Response<UserToken> response) {
dialog.dismiss();
if (response.isSuccessful()){
loginResponse.setMloginstatus(true);
loginResponse.setStatusCode(response.code());
loginResponse.setUserToken(response.body());
callback.success(loginResponse);
return;
}
// response isn't successful
loginResponse.setMloginstatus(false);
loginResponse.setStatusCode(response.code());
loginResponse.setMessage(response.message());
loginResponse.setUserToken(null);
callback.failure(...);
}
#Override
public void onFailure(Call<UserToken> call, Throwable t) {
dialog.dismiss();
loginResponse.setMloginstatus(false);
loginResponse.setUserToken(null);
loginResponse.setMessage(t.getMessage());
loginResponse.setStatusCode(FAILURE_ERROR);
callback.failure(...);
}
});
}
call.enqueue() is asynchronous.. the call is done in the background, and then, as soon as the call finishes, one of the two callbacks is called, either onResponse or onFailure.
call.execute() is synchronous, it will block the execution until the call finishes, but in that case you need to handle threading manually (remember that in android you cannot block the UI thread).
I recommend enqueue.