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();
Related
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)
}
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);
}
i'm beginner of java, and i need some help, I had see many posts about, but not work they issues
I had json object from url like that
{
'history':[{
'id':2,
'name':'irine'},
{
'id':3,
'name':'karine'
}]
}
but i need
[
{
'id':2,
'name':'irine'},
{
'id':3,
'name':'karine'}
]
my ApiClient.java
public class ApiClient {
public static final String BASE_URL = "http://192.168.150.100";
public static Retrofit retrofit;
public static Retrofit getApiClient(final String authToken){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request.Builder ongoing = chain.request().newBuilder();
ongoing.addHeader("Accept", "application/json;");
ongoing.addHeader("Authorization", authToken);
return chain.proceed(ongoing.build());
}
})//.addInterceptor(interceptor)
.build();
if (retrofit==null){
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
getting response from url
call.enqueue(new Callback<List<Contact>>() {
#Override
public void onResponse(Call<List<Contact>> call, Response<List<Contact>> response) {
progressBar.setVisibility(View.GONE);
contacts=response.body();
if(contacts==null)
Snackbar.make(findViewById(R.id.lt_search), "Nothing found",Snackbar.LENGTH_LONG).show();
adapter = new Adapter(contacts, SearchActivity.this);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
#Override
public void onFailure(Call<List<Contact>> call, Throwable t) {
progressBar.setVisibility(View.GONE);
Log.d("GHaa ka sk aksjdk j", contacts.toString());
Toast.makeText(SearchActivity.this, "Error\n"+t.toString(), Toast.LENGTH_LONG).show();
}
});
how to convert object to array or clear word 'history' from object?
You have provided the wrong class for API response.You are getting the list object against a string key which might change(not known). So, you will have to use HashMap. The API response is in the form of HashMap<String,<List<Contact>>>. So in the code, you will have to use Callback<HashMap<String,List<Contact>>>() instead of Callback<List<Contact>>().
Then from the HashMap you can easily get the list using hashmap.get('key'). In your case, hashmap.get('history').
The right way to reselve this problem:
call.enqueue(new Callback<HashMap<String,List<Contact>>>() {
#Override
public void onResponse(Call<HashMap<String, List<Contact>>> call, Response<HashMap<String, List<Contact>>> response) {
HashMap<String, List<Contact>> history=response.body();
contacts= new ArrayList<>(history.get("hitory"));
Log.d(TAG, contacts.toString());
progressBar.setVisibility(View.GONE);
//contacts=response.toString();
Log.d(TAG,response.toString());
if(contacts==null)
Snackbar.make(findViewById(R.id.lt_search), "Nothing found",Snackbar.LENGTH_LONG).show();
adapter = new Adapter(contacts, SearchActivity.this);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
#Override
public void onFailure(Call<HashMap<String, List<Contact>>> call, Throwable t) {
progressBar.setVisibility(View.GONE);
//Log.d("GHaa ka sk aksjdk j", contacts.toString());
Toast.makeText(SearchActivity.this, "Error\n"+t.toString(), Toast.LENGTH_LONG).show();
}
});
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
}
...
}
I have JSON file like this http://androiddocs.ru/api/friends.json
{
"data":"dbfriends",
"friends": [{"id":"1","name":"Andrew","city":"Moscow","contacts":{"mobile":"+7 0000000","email":"andrew#androiddocs.ru","skype":"andrew"}}, {"id":"2","name":"Ivan","city":"Kiev","contacts":{"mobile":"+38 0000000","email":"ivan#androiddocs.ru","skype":"ivan"}}]
}
my retrofit interface
public interface Friends_API {
String BASE_URL = " http://androiddocs.ru/api/";
#GET("friends.json") Call<Friends> getFriends();
class Factory {
private static Friends_API service;
public static Friends_API getInstance(){
if (service == null) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build();
service = retrofit.create(Friends_API.class);
return service;
} else {
return service;
}
}
}
}
POJO file generated by http://www.jsonschema2pojo.org/
and my code to get data value and:
Friends_API.Factory.getInstance().getFriends().enqueue(new Callback<Friends>() {
#Override
public void onResponse(Call<Friends> call, Response<Friends> response) {
String getData = response.body().getData();
}
#Override
public void onFailure(Call<Friends> call, Throwable t) {
}
}
i can't understood how i can get value: id, name, city...
thanks for help!
You can get the data from retrieved response using:
Friends friends = response.body();
And then you can retrieve the List<Friend> using friends.getFriends() object. Now you can iterate through this list of friends and can get id,name, city or contact details.