Receiving custom parameter in retrofit callback - java

This is my post :
#POST("/path")
#FormUrlEncoded
void postIt(#Field("id") String id , Callback<Response> response);
and this is the Callback:
private Callback<Response> responseCallBack = new Callback<Response>() {
#Override
public void success(Response response, Response response2) {
// get the id
}
#Override
public void failure(RetrofitError error) {
// do some thing
}
};
Question:
in the callback i want to receive the id which posted in #POST, how should i do that?
and i can't change the server API

to do this we need an abstract class
abstract class CallBackWithArgument<T> implements Callback<T> {
String arg;
CallBackWithArgument(String arg) {
this.arg = arg;
}
CallBackWithArgument() {
}
and make an instance
new CallBackWithArgument<Response>(id) {
#Override
public void success(Response response, Response response2) {
//do something
}
#Override
public void failure(RetrofitError error) {
//do something
}
}

It's easy. You can simply make Callback to hold requested id and create new callback every time
class MyCallback extends Callback<Response> {
private final String id;
MyCallback(String id) {
this.id = id
}
#Override
public void success(Response response, Response response2) {
// get the id
}
#Override
public void failure(RetrofitError error) {
// do some thing
}
}
So when you call service
myService.postIt("777", new MyCallback("777"))

Related

How can I retry to connect with the server using RXJava and Retrofit?

Code
new Retrofit.Builder().baseUrl(Constants.BASE_URL_FILES).addCallAdapterFactory(RxJava3CallAdapterFactory.create()).addConverterFactory(GsonConverterFactory.create()).build().create(RetrofitInterface.class).getCountries().observeOn(AndroidSchedulers.mainThread()).subscribe(new SingleObserver<List<CountryModel>>() {
#Override
public void onSubscribe(#io.reactivex.rxjava3.annotations.NonNull Disposable d) {
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.VISIBLE);
}
#Override
public void onSuccess(#io.reactivex.rxjava3.annotations.NonNull List<CountryModel> countryModels) {
fragmentCountriesBinding.fragmentCountriesRecyclerViewCountries.setAdapter(new CountriesAdapter(requireContext(), countryModels));
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.GONE);
}
#Override
public void onError(#io.reactivex.rxjava3.annotations.NonNull Throwable e) {
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.GONE);
if (!MainActivity.isNetworkConnected(requireContext()))
Snackbar.make(MainActivity.activityMainBinding.getRoot(), R.string.no_internet, BaseTransientBottomBar.LENGTH_INDEFINITE).setMaxInlineActionWidth(1).setAction(R.string.retry, view ->
{
}).show();
else
Snackbar.make(MainActivity.activityMainBinding.getRoot(), R.string.something_went_wrong, BaseTransientBottomBar.LENGTH_INDEFINITE).setMaxInlineActionWidth(1).show();
}
});
RetrofitInterface
public interface RetrofitInterface {
#GET("GetCountries.php")
Single<List<CountryModel>> getCountries();
}
CountryModel
public class CountryModel {
private int countryId;
private String countryName, countryIcon;
public int getCountryId() {
return countryId;
}
public String getCountryName() {
return countryName;
}
public String getCountryIcon() {
return countryIcon;
}
}
As you can see inside the onError method, I want when the user clicks on the retry button must retry the call with the server again.
I read the articles talking about retryWhen method and tried to understand this image, but I can't understand it. And I don't know how to use it with my scenario.
My problem is like this question exactly.
My solution is simple and just a temporary/hack rather than spending couple of days using retryWhen().
In the code below, I encapsulate your request call to a function.
If there is a network error, just do a recursive call with the function until it will be successful.
private void sendRequest(){
new Retrofit.Builder().baseUrl(Constants.BASE_URL_FILES).addCallAdapterFactory(RxJava3CallAdapterFactory.create()).addConverterFactory(GsonConverterFactory.create()).build().create(RetrofitInterface.class).getCountries().observeOn(AndroidSchedulers.mainThread()).subscribe(new SingleObserver<List<CountryModel>>() {
#Override
public void onSubscribe(#io.reactivex.rxjava3.annotations.NonNull Disposable d) {
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.VISIBLE);
}
#Override
public void onSuccess(#io.reactivex.rxjava3.annotations.NonNull List<CountryModel> countryModels) {
fragmentCountriesBinding.fragmentCountriesRecyclerViewCountries.setAdapter(new CountriesAdapter(requireContext(), countryModels));
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.GONE);
}
#Override
public void onError(#io.reactivex.rxjava3.annotations.NonNull Throwable e) {
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.GONE);
if (!MainActivity.isNetworkConnected(requireContext()))
Snackbar.make(MainActivity.activityMainBinding.getRoot(), R.string.no_internet, BaseTransientBottomBar.LENGTH_INDEFINITE).setMaxInlineActionWidth(1).setAction(R.string.retry, view ->
{
}).show();
// IF NO NETWORK AVAILABLE just do recursive call of the method
sendRequest();
else
Snackbar.make(MainActivity.activityMainBinding.getRoot(), R.string.something_went_wrong, BaseTransientBottomBar.LENGTH_INDEFINITE).setMaxInlineActionWidth(1).show();
}
});
}
I didn’t reach a solution, so I decided to use retryUntil instead of retryWhen.

Return value from OkHttpClient async request

In my first android project i made an onClick event to call a function:
public void doSomething(View v) {
String result = authenticate();
[...]
}
This function calls the method:
private String authenticate() {
OkHttpClient client = new OkHttpClient();
[...]
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
[...]
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String responseString = response.body().string();
try {
JSONObject responseObject = new JSONObject(responseString);
String responseObjectAccessToken = responseObject.getString("accesstoken");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});
}
now i would like to return the responseObjectAccessToken to my doSomething function. A detailed explanation would be great since I am new to Java and Android Studio.
One suggestion is to handle that async response through a callback which is passed as an argument to your authenticate method. Here I'm using Java 8 lambda for that one method interface.
public class MyActivity extends Activity {
public interface AuthCallback {
void onAuthResult(String token);
}
private String authenticate(AuthCallback callback) {
// ...
String responseObjectAccessToken = responseObject.getString("accesstoken");
callback.onAuthResult(responseObjectAccessToken)
// ...
}
public void doSomething(View v) {
authenticate((token) -> {
// do something with token
});
}
// ...
}
As you can see that callback could be stored if you wanted to:
// ...
AuthCallback cb = new AuthCallback() {
#Override
public void onAuthResult(String token) {
// do something in the view/fragment/activity
}
}
// then pass it as argument
or your class could implement this interface and pass itself into the method:
public class MyActivity extends Activity implements AuthCallback {
#Override
public void onAuthResult(String token) {
// do something
}
// ...
public void doSomething(View v) {
authenticate(MyActivity.this); // <-- pass itself
}
}
One important point here is that networking happens on a separate thread, so if you want to have some UI changes after your API responds you could use a helper method runOnUiThread to do changes on main ui thread instead:
authenticate((token) -> {
runOnUiThread(() -> {
// do something with UI here
})
});

How to simplify Retrofit by ommiting the mediator class

I want to retrieve a Json from server and pass it to WebView and consume it in a JavaScript function. So I think I dont need a Java class to hold the data and consume it in Java environment. Actually I want to omit the mediator calss (Users) I used in Retrofit. It was complicated for me to understand the structrure of retrofit request so I have no idea how to consume the response directly without a mediator class. This is my current code:
in Main Activity:
public void getUserList(){
GetUsers users = RetrofitInstance.getRetrofitInstance().create(GetUsers.class);
Call<Users> call = users.getUserList("myUsername");
call.enqueue(new Callback<Users>() {
#Override
public void onResponse(Call<Users> call, Response<Users> response) {
PassUsersToJavaScript(response.body().getList());
}
#Override
public void onFailure(Call<Users> call, Throwable t) {
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
}
public void PassUsersToJavaScript(String users){
//Here I pass users to WebView
}
This is the mediator Users class that I want to omit:
public class Users {
#SerializedName("users")
private String users;
public Users(String users) {
this.users = users;
}
public String getList(){
return users;
}
}
and this is the mediator Interface GetUsers :
public interface GetUsers {
#GET("list/")
Call<Users> getUserList(#Query("user") String user);
}
How can I omit the class Users.java and use the response.body() driectly in the line below:
public void onResponse(Call<Users> call, Response<Users> response) {
PassUsersToJavaScript(response.body().getList());
}
I found that I can assign retrofit to a class, object, page and even a string. The model class users in the question above can be replaced with anything else like this JsonObject directly:
public void getUserList(){
GetUsers users = RetrofitInstance.getRetrofitInstance().create(GetUsers.class);
Call<JsonObject> call = users.getUserList("myUsername");
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
PassUsersToJavaScript(response.body().getList());
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
}

Retrofit Generic Base Method for request

I have a problem with Retrofit and duplicate checking.
I have to check every time response status code or type!
I need a wrapper for request method that checks there this duplicate works.
(duplicate works includes: showLoading(),response.code(),onFailure() handle...).
I need a GenericMethod for this:
UserService service = RetrofitInstance.getRetrofitInstance().create(UserService.class);
service.RequestVerification(token, mobileNumber).enqueue(new Callback<ClientData<User>>() {
#Override
public void onResponse(#NonNull Call<ClientData<User>> call, #NonNull Response<ClientData<User>> response) {
doAction();//Action must passed to this method.
GeneralTools.hideLoading();
}
#Override
public void onFailure(#NonNull Call<ClientData<User>> call, #NonNull Throwable t) {
GeneralTools.hideLoading();
dialogError.show();
}
});
Try below
private static class CallbackHandler<T> implements Callback<T> {
#Override
public void onResponse(Call<T> call, Response<T> response) {
int code = response.code();
if (code >= 200 && code < 300) {
onSuccess(response);
} else if (code == 401) {
// logic to refresh token or user then recall the same api
call.clone().enqueue(this);
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
}
public void onSuccess(Response<T> response) {
}
}
Then change your call like below
service.RequestVerification(token, mobileNumber).enqueue(new CallbackHandler<ClientData<User>>() {
#Override
public void onSuccess(Response<ClientData<User>> response) {
doAction();//Action must passed to this method.
GeneralTools.hideLoading();
}
#Override
public void onFailure(#NonNull Call<ClientData<User>> call, #NonNull Throwable t) {
GeneralTools.hideLoading();
dialogError.show();
}
});

Building a library that uses Retrofit internally, wrapping responses

I'm trying to build a library that basically wraps our api. Basically, the structure im going for is something like this:
MySDK mySDK = new MySDK("username", "password");
mySDK.getPlaylistInfo("3423", 2323, new CustomCallback<>(){
//on response
//on failure
});
So with vanilla Retrofit, an api call usually looks something like the following:
ApiService api = retrofit.create(ApiService.class);
Call<Response> call = api.getPlaylistInfo()
call.enqueue(new Callback<Response>() {
#Override
public void onResponse(Call<Response> call, Response<Response> response) {
//handle response
}
#Override
public void onFailure(Call<Response> call, Throwable t) {
//handle failure
}
});
Basically, how would I wrap retrofits callback system into my own? Note, the reason for needing to do this is to preprocess the data returned from the api before delivering the final response.
I've written something similar so it might help you getting started, this follows an implementation I'v written for Volley, and re-used when I migrated to Retrofit2 so it resembles it (this SO question).
Create a global object (what you would refer to as MySDK) as a singelton class that handles your requests:
create a singleton class, which you instatiate when you're application comes up:
public class NetworkManager
{
private static final String TAG = "NetworkManager";
private static NetworkManager instance = null;
private static final String prefixURL = "http://some/url/prefix/";
//for Retrofit API
private Retrofit retrofit;
private ServicesApi serviceCaller;
private NetworkManager(Context context)
{
retrofit = new Retrofit.Builder().baseUrl(prefixURL).build();
serviceCaller = retrofit.create(ServicesApi.class);
//other stuf if you need
}
public static synchronized NetworkManager getInstance(Context context)
{
if (null == instance)
instance = new NetworkManager(context);
return instance;
}
//this is so you don't need to pass context each time
public static synchronized NetworkManager getInstance()
{
if (null == instance)
{
throw new IllegalStateException(NetworkManager.class.getSimpleName() +
" is not initialized, call getInstance(...) first");
}
return instance;
}
public void somePostRequestReturningString(Object param1, final SomeCustomListener<String> listener)
{
String url = prefixURL + "this/request/suffix";
Map<String, Object> jsonParams = new HashMap<>();
jsonParams.put("param1", param1);
Call<ResponseBody> response;
RequestBody body;
body = RequestBody.create(okhttp3.MediaType.parse(JSON_UTF), (new JSONObject(jsonParams)).toString());
response = serviceCaller.thePostMethodYouWant("someUrlSufix", body);
response.enqueue(new Callback<ResponseBody>()
{
#Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
{
try
{
String response = rawResponse.body().string();
// do what you want with it and based on that...
//return it to who called this method
listener.getResult("someResultString");
}
catch (Exception e)
{
e.printStackTrace();
listener.getResult("Error1...");
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
try
{
// do something else in case of an error
listener.getResult("Error2...");
}
catch (Exception e)
{
throwable.printStackTrace();
listener.getResult("Error3...");
}
}
});
}
public void someGetRequestReturningString(Object param1, final SomeCustomListener<String> listener)
{
// you need it all to be strings, lets say id is an int and name is a string
Call<ResponseBody> response = serviceCaller.theGetMethodYouWant
(String.valueOf(param1.getUserId()), param1.getUserName());
response.enqueue(new Callback<ResponseBody>()
{
#Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
{
try
{
String response = rawResponse.body().string();
// do what you want with it and based on that...
//return it to who called this method
listener.getResult("someResultString");
}
catch (Exception e)
{
e.printStackTrace();
listener.getResult("Error1...");
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
try
{
// do something else in case of an error
listener.getResult("Error2...");
}
catch (Exception e)
{
throwable.printStackTrace();
listener.getResult("Error3...");
}
}
});
}
}
This works with your interface (example with POST and GET request, GET could be without params):
public interface BelongServicesApi
{
#POST("rest/of/suffix/{lastpart}") // with dynamic suffix example
Call<ResponseBody> thePostMethodYouWant(#Path("lastpart") String suffix, #Body RequestBody params);
#GET("rest/of/suffix") // with a fixed suffix example
Call<ResponseBody> theGetMethodYouWant(#Query("userid") String userid, #Query("username") String username);
}
when your application comes up:
public class MyApplication extends Application
{
//...
#Override
public void onCreate()
{
super.onCreate();
NetworkManager.getInstance(this);
}
//...
}
a simple listener interface for your callback (seperate file would do good):
public interface SomeCustomListener<T>
{
public void getResult(T object);
}
and finally, from wherever you want, the context is already in there, just call:
public class BlaBla
{
//.....
public void someMethod()
{
//use the POST or GET
NetworkManager.getInstance().somePostRequestReturningString(someObject, new SomeCustomListener<String>()
{
#Override
public void getResult(String result)
{
if (!result.isEmpty())
{
//do what you need with the result...
}
}
});
}
}
you can use any object with the listener, just parse the response string to a corresponding object, depending on what you need to receive and you can call that from everywhere (onClicks, etc.), just remember the objects need to match between methods.
Hope this Helps!

Categories