How can I use jsonapi in Retrofit2? - java

I need use jsonapi in retrofit2. I try with moshi-jsonapi but I can't use moshi ConverterFactory.
TokenModel.java
#JsonApi(type = "tokens")
public class TokenModel extends Resource {
#Json(name = "cell_phone")
public String cellPhone;
}
TestService.java:
public interface TestService {
#POST("token")
Call<TokenModel> newOtp(#Body TokenModel tokenModel);
}
TestProvider.java:
public class TestProvider {
private TestService testService;
public TestProvider() {
OkHttpClient httpClient = new OkHttpClient();
Retrofit refRetrofit = new Retrofit.Builder()
.baseUrl(ClientConfigs.BASE_URL)
.client(httpClient)
.addConverterFactory(MoshiConverterFactory.create())
// .addConverterFactory(????????????????????????????)
.build();
testService = refRetrofit.create(TestService.class);
}
publicTestService getTestService() {
return testService;
}
}
If I use MoshiConverterFactory make error Unable to create converter for class com.xxx.xxx.model.TokenModel!
Use Retrofit:
TsetProvider testProvider = new TestProvider();
TestService testService = testProvider.getTestService();
TokenModel tokenModel = new TokenModel();
tokenModel.cellPhone = "121212129999";
Call<TokenModel> call = testService.newOtp(tokenModel);
call.enqueue(new Callback<TokenModel>() {
#Override
public void onResponse(Call<TokenModel> call, Response<TokenModel> response) {
}
#Override
public void onFailure(Call<TokenModel> call, Throwable t) {
}
});

From moshi-jsonapi documentation you need to add the libraries factory to the moshi instance:
// First create the factory
JsonAdapter.Factory jsonApiAdapterFactory = ResourceAdapterFactory.builder()
.add(TokenModel.class)
.build();
// Create a custom moshi instacne
Moshi moshi = new Moshi.Builder()
.add(jsonApiAdapterFactory)
.build();
// Add the custom moshi instance to Retrofits Converter Factory
Retrofit refRetrofit = new Retrofit.Builder()
.baseUrl(ClientConfigs.BASE_URL)
.client(httpClient)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build();
This should do the trick.

Related

I want to work with multiple base url in retrofit2

I have a structure whose base url address is constantly variable. When my user changes his location in the application, I change the base url to show the closest server to him. but the retrofit client does not regenerate even though I restart the activities and change all the static variables. The only way to do this is to close the application completely and restart it.
I don't have a fixed base url. There could be 4 different locations today, 12 different locations tomorrow. so i need to find a permanent solution.
I have examined a lot in debug mode, but; When the base url changes, it never goes inside the getClient and cannot reassign the base url.
public static Retrofit retrofit = null;
// I want this address to change.
public static String baseUrl = "";
#Provides
public static Retrofit getClient() {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
#Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new Date(json.getAsJsonPrimitive().getAsLong());
}
});
Gson gson = builder.create();
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader("AUTHORIZATION", "" + JWT_TOKEN)
.build();
return chain.proceed(request);
}
}).build();
if (retrofit == null) {
retrofit = new Retrofit.Builder().baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build();
return retrofit;
}
return retrofit;
}
You can use a dynamic URL in your application during runtime. Retrofit2 introduced the #Url annotation that allows us to pass a complete URL for an endpoint:
#GET
public Call<ResponseBody> profilePicture(#Url String url);
EDITED
You should create a new Retrofit instance when you want to use a new base URL.
public class ServiceGenerator {
public static String apiBaseUrl = "http://futurestud.io/api";
private static Retrofit retrofit;
private static Retrofit.Builder builder =
new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(apiBaseUrl);
private static OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
// No need to instantiate this class.
private ServiceGenerator() {
}
public static void changeApiBaseUrl(String newApiBaseUrl) {
apiBaseUrl = newApiBaseUrl;
builder = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(apiBaseUrl);
}
public static <S> S createService(Class<S> serviceClass, AccessToken token) {
String authToken = token.getTokenType().concat(token.getAccessToken());
return createService(serviceClass, authToken);
}
// more methods
// ...
}
https://futurestud.io/tutorials/retrofit-2-how-to-change-api-base-url-at-runtime-2
I used SharedPreferences to handle this problem:
#Singleton
#Provides
suspend fun getBaseUrl(preferencesManager: PreferencesManager): String {
return preferencesManager.getPrefBaseUrl()
}
#Singleton
#Provides
fun provideGsonBuilder(): Gson {
return GsonBuilder()
.setLenient()
.create()
}
#Provides
#Singleton
fun provideOkHttpClient( ) =
OkHttpClient
.Builder()
.connectTimeout(3, TimeUnit.MINUTES)
.readTimeout(3, TimeUnit.MINUTES)
.writeTimeout(3, TimeUnit.MINUTES)
.build()
#Singleton
#Provides
fun provideRetrofit(gson: Gson, okHttpClient: OkHttpClient, preferencesManager: PreferencesManager): Retrofit.Builder {
return Retrofit.Builder()
.baseUrl( preferencesManager.getPrefBaseUrl())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient)
}
#Singleton
#Provides
fun provideBlogService(retrofit: Retrofit.Builder): MyApi {
return retrofit
.build()
.create(MyApi::class.java)
}

How make a custom Retrofit ConverterFactory to transform ReponseBody to a new ResponseBody?

I'd like to transform the answer of a service which is codified in "windows-1252" to "unicode". I am trying to do using a custom converter factory in retrofit to acomplish that work. My custom converter try to intercept the ResponseBody class, change the character codification, and return a new ResponseBody to retrofit to be processed.
But it didn't work. Apparently, retrofit built in converters intercept first the ResponseBody class and it can't be processed again.
Is there any way to avoid that behavior?
Note: I can get it using a map function when I have the response but I'd like to learn to do in retrofit.
This is my code.
AnsiconverterFactory.java
-------------------------
public class AnsiConverterFactory extends Converter.Factory {
public static AnsiConverterFactory create() {
return new AnsiConverterFactory();
}
private AnsiConverterFactory() {
}
#Override
public #Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == ResponseBody.class) {
return AnsiResponseBodyConverter.INSTANCE;
}
return null;
}
#Override
public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return super.stringConverter(type, annotations, retrofit);
}
#Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
}
AnsiResponseBodyConverter.java
------------------------------
public class AnsiResponseBodyConverter implements Converter<ResponseBody, ResponseBody> {
static final AnsiResponseBodyConverter INSTANCE = new AnsiResponseBodyConverter();
#Override
public ResponseBody convert(ResponseBody value) throws IOException {
Log.d("CONVERTER", " En convert de responsebodyconverter");
final CharBuffer todo = Charset.forName("windows-1252").decode(ByteBuffer.wrap(value.bytes()));
return ResponseBody.create(value.contentType(), todo.toString());
}
}
And the use in the app:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://...some webservice here")
.addConverterFactory(BitmapConverterFactory.create())
.addConverterFactory(AnsiConverterFactory.create())
.client(clientBuilder.build())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build();
I finally managed to solve the problem using an interceptor in okhttp. This is the code:
class LoggingInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
ResponseBody body = response.body();
if (body.contentType().equals(MediaType.parse("text/csv"))) {
final CharBuffer todo = Charset.forName("windows-1252").decode(ByteBuffer.wrap(body.bytes()));
body = ResponseBody.create(body.contentType(), todo.toString());
Response.Builder builder = response.newBuilder();
return builder.headers(response.headers())
.body(body)
.build();
}
return response;
}
}
And this just before Retrofit build.
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor());

Fetch data from server using retrofit method

i want fetch data from server, i have API URL like example : https://example.com/PlanController/getData/2/7k Plan, int his api url 2 is dynamic value and 7k plan is also dynamic Value. i want fetch data from retrofit method. give me some examples.
public interface APIService {
#GET("PlanController/getData")
Call<CoachListResponse> getAllData();
}
Retrofit clint
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String baseUrl) {
Gson gson = new GsonBuilder().setLenient().create();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
return retrofit;
}
}
Define service after creating the retrofit
public interface APIService {
#GET("getData/{id}/{kid}")
Call<CoachListResponse> getAllData(#Path("id") Long id, #Path("kid") String kid);
}
public class RetrofitClient {
private static APIService service;
public static Retrofit getClient(String baseUrl) {
Gson gson = new GsonBuilder().setLenient().create();
if (retrofit == null) {
retrofit = new Retrofit.Builder().baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson)).build();
}
service = retrofit.create(APIService.class);
return retrofit;
}
public static void getAllData(Callback<CoachListResponse> callback) {
Call<CoachListResponse> regionsCall = service.getAllData();
regionsCall.enqueue(callback);
}
}
, consume
RetrofitClient.getClient("https://example.com/PlanController/").getAllData(new Callback<CoachListResponse>() {
#Override
public void onResponse(Call<CoachListResponse> call, Response<CoachListResponse> response) {
CoachListResponse responseDto = response.body();
// logic
}
#Override
public void onFailure(Call<CoachListResponse> call, Throwable t) {
// logic
}
}, );
I wanted to replace only a part of the URL, and with this solution, I don't have to pass the whole URL, just the dynamic part and Your Retrofit client as it is no need to change:
public interface APIService {
#GET("PlanController/getData/{value}/{plan}")
Call<CoachListResponse> getAllData(#Path(value = "value", encoded = true) String value, #Path(value = "plan", encoded = true) String plan);
}

to call the retrofit in different classes

I am a beginner to the android. I am using Retrofit to call the API. But I would like to write the retrofit call method only once and use the same function in different API calls in my application. I try to create It's a generic method in nonactivity class and use it in my activity class.
public static generic_Retrofit_Class apiClient;
private Retrofit retrofit = null;
public static generic_Retrofit_Class getInstance() {
if (apiClient == null) {
apiClient = new generic_Retrofit_Class();
}
return apiClient;
}
public Retrofit getclient()
{
return getclient(null);
}
private Retrofit getclient(Object o) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.level(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
okHttpClient.readTimeout(60, TimeUnit.SECONDS);
okHttpClient.writeTimeout(60, TimeUnit.SECONDS);
okHttpClient.connectTimeout(60, TimeUnit.SECONDS);
okHttpClient.addInterceptor(interceptor);
okHttpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
return chain.proceed(request);
}
});
retrofit = new Retrofit.Builder()
.baseUrl(Constant.Baseurl)
.client(okHttpClient.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
After this code when I call this method in my Activity class it shows an error. Here is the implementation of the method in the Activity class.
private void Getstock() {
final String mid = medicineid.getText().toString().trim();
final String batch = batchno.getText().toString().trim();
if (medicineid.getText().length() != 0 && batchno.getText().length() != 0) {
App_Interfaces app_interfaces = (App_Interfaces) new generic_Retrofit_Class().getclient().create(App_Interfaces.class);
Map<String, String> mapdata = new HashMap<>();
mapdata.put("mid", mid);
mapdata.put("batch", batch);
final Call<Response> getstock_call = app_interfaces.getstock(mapdata);
getstock_call.enqueue(new Callback<Response>() {
#Override
public void onResponse(Call<Response> call, Response<Response> response) {
if (response.isSuccessful() && response.body() != null && response != null) {
String jsonresponse = response.body().toString();
parseStockData(jsonresponse);
System.out.print(jsonresponse);
return;
}
}
#Override
public void onFailure(Call<Response> call, Throwable t) {
}
});
}
Here is the error
java.lang.IllegalArgumentException: 'retrofit2.Response' is not a valid response body type. Did you mean ResponseBody?
for method App_Interfaces.getstock
here is my Interface Code
public interface get_stock
{
#GET("/getstock")
Call<Response> getstock(#QueryMap Map<String, String> options);
}
Welcome to SO
Change the getClient method code as
public class YOUR_CLASS{
private static YOUR_API_INTERFACE retrofit = null;
public static YOUR_API_INTERFACE getClient() { // no need to pass the object params
if(retrofit == null){
//you client code same as you written in question
retrofit = new Retrofit.Builder()
.baseUrl(Constant.Baseurl)
.client(okHttpClient.build()) //okHtttpClient from your client code
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit.create(YOUR_API_INTERFACE.class);
}
}
Now, you can easily access the retrofit api interface by YOUR_CLASS.getClient(). This will return your Retrofit Interface
Your Retrofit Interface with declare method should be like this
public interface YOUR_API_INTERFACE {
#GET("your api name")
Call<YOUR_POJO> yourApi();
}
In your activity/fragment class you can access the yourApi method as
YOUR_CLASS.getClient().yourApi();

Retrofit 2 onResponse fill an object in another class

I'm trying to use the values I get from my request but I can't use them because void onReponse method. Values I get is only staying in onResponse method. I know it is because it is void and I can't return anything but is there a way to fill an object with values I get?
Here is my ApiClient class:
public class ApiClient implements Callback<Map<String, Channel>> {
static final String BASE_URL = "some url";
public void start() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
RestInterface restInterface = retrofit.create(RestInterface.class);
Call<Map<String, Channel>> call = restInterface.getChannels();
call.enqueue(this);
}
#Override
public void onResponse(retrofit2.Call<Map<String, Channel>> call, Response<Map<String, Channel>> response) {
System.out.println(response.code());
if(response.isSuccessful()) {
Map<String, Channel> body = response.body();
List<Channel> channels = new ArrayList<>(body.values());
for (Channel channel : body.values()) {
System.out.println(channel.getSong());
}
...
What I have to do is create Channel objects with the values I get from onResponse. I tried to use it in an another class like this:
ApiClient apiClient = new ApiClient();
apiClient.start();
but it still only works in onResponse. I need to create Channel objects like:
Channel channel = new Channel(channels(1));
this but in another class not in the ApiClient.
Use callback interface to pass data to another class:
public interface ChannelCallback {
void setChannels(Map<String, Channel> body);
}
In your ApiClient assign listener:
private ChannelCallback listener;
public void start(ChannelCallback listener) {
this.listener = listener;
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
RestInterface restInterface = retrofit.create(RestInterface.class);
Call<Map<String, Channel>> call = restInterface.getChannels();
call.enqueue(this);
}
In OnResponse pass data via this listener:
#Override
public void onResponse(retrofit2.Call<Map<String, Channel>> call,
Response<Map<String, Channel>> response) {
System.out.println(response.code());
if(response.isSuccessful()) {
Map<String, Channel> body = response.body();
listener.setChannels(body);
}
Not forget to implement ChannelCallback listener in class that calls ApiClient.start(this)
public SomeClass implements ChannelCallback{
...
apiClient.start(this);
...
#Override
setChannels(Map<String, Channel> body){
// logic here with data from body
}
...
}

Categories