I am trying to integrate my application with Pexels API by using Retrofit and GSON but without success. I keep getting 404 error and therefore I am getting a null pointer exception on the console.
This is the working api:
https://api.pexels.com/v1/curated?page=1&per_page=14
public class ApiClient {
public static final String API_KEY = "xxxxxxxxxx";
public static final String BASE_URL = "https://api.pexels.com/v1/";
public static Retrofit retrofit = null;
public static Retrofit getClient(){
if(retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
public interface ApiInterface {
#Headers({"Authorization: " + ApiClient.API_KEY})
#GET("/curated")
Call<ExplorePageModelResponse>getExplorePagePhotos(
#Query("page") int page,
#Query("per_page") int per_page
);
}
This is the service class that communicates with the pexels api. The response body is empty...
public class ExplorePageService implements ItemListContract.Model<ExplorePageModelPhotos> {
private final String TAG = "ExplorePageService";
private int pageNo = 1;
private final int PER_PAGE = 14;
#Override
public void getExploreItemList(OnFinishedListener<ExplorePageModelPhotos> onFinishedListener, int pageNo) {
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<ExplorePageModelResponse> call = apiService.getExplorePagePhotos(1, PER_PAGE);
call.enqueue(new Callback<ExplorePageModelResponse>() {
#Override
public void onResponse(Call<ExplorePageModelResponse> call, Response<ExplorePageModelResponse> response) {
List<ExplorePageModelPhotos> list = response.body().getExplorePageModelPhotos();
onFinishedListener.onFinished(list);
}
#Override
public void onFailure(Call<ExplorePageModelResponse> call, Throwable t) {
Log.e(TAG, t.toString());
onFinishedListener.onFailure(t);
}
});
}
}
This is the api response:
Response{protocol=h2, code=404, message=, url=https://api.pexels.com/curated?page=1&per_page=14}
I must be doing something wrong in the ApiInterface part since it's all working on the postman side but couldn't figure out. Thanks.
Related
I'm encountering a problem with retrofit :
Im trying to make a get request to this API: https://test.spaceflightnewsapi.net/api/v2/articles
But somehow the callbacks methods from the retrofit are not called.
I get this in the logcat/ the only thing related to the retrofit (okkhttp):
I/okhttp.OkHttpClient: --> GET https://test.spaceflightnewsapi.net/api/v2/articles
I/okhttp.OkHttpClient: --> END GET
here is my api interface:
public interface SpaceNewsApi {
String BASE_URL = "https://test.spaceflightnewsapi.net/api/v2/";
#GET("articles")
Call<List<SpaceNews>> getSpaceNewsArticles();
}
here is the retrofit client (note that i will be using two apis with the retrofit this is why i'm using two methods to build it), also i've added a loginginterceptor, I've change the Level to different types but only the things from above are the messages i get each time in the logs related to retrofit
public class RetrofitClients {
private static RetrofitClients instance = null;
private SpaceNewsApi mySpaceNewsApi = null;
private LaunchLibraryApi myLaunchLibraryApi = null;
public static synchronized RetrofitClients getInstance() {
if (instance == null) {
instance = new RetrofitClients();
}
return instance;
}
public SpaceNewsApi getMySpaceNewsApi() {
if(mySpaceNewsApi == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(SpaceNewsApi.BASE_URL)
.client(getHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
mySpaceNewsApi = retrofit.create(SpaceNewsApi.class);
}
return mySpaceNewsApi;
}
public LaunchLibraryApi getMyLaunchLibraryApi(){
if(myLaunchLibraryApi == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(LaunchLibraryApi.BASE_URL)
.client(getHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
myLaunchLibraryApi = retrofit.create(LaunchLibraryApi.class);
}
return myLaunchLibraryApi;
}
public static OkHttpClient getHttpClient() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
//TODO : remove logging interceptors as it is to be used for development purpose
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(300, TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS)
.addInterceptor(logging)
.build();
return client;
}
}
I'm calling all of these in my main activity onCreate method like this:
//testing Api Call
Call<List<SpaceNews>> call = RetrofitClients.getInstance().getMySpaceNewsApi().getSpaceNewsArticles();
call.enqueue(new Callback<List<SpaceNews>>() {
#Override
public void onResponse(Call<List<SpaceNews>> call, Response<List<SpaceNews>> response) {
Log.d("gotApiData","got it");
}
#Override
public void onFailure(Call<List<SpaceNews>> call, Throwable t) {
Log.d("gotError","not got it");
t.printStackTrace();
}
});
How can I get this working,
I would appreciate any advice!
The problem I believe was with the RetrofitClients class, after adding a private constructor and moving the majority of code from the get***Api methods seems to solve the problem.
here is the modified RetrofitClients:
public class RetrofitClients {
private static RetrofitClients instance = null;
private SpaceNewsApi mySpaceNewsApi = null;
private LaunchLibraryApi myLaunchLibraryApi = null;
private RetrofitClients(){
if(mySpaceNewsApi == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(SpaceNewsApi.BASE_URL)
.client(getHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
mySpaceNewsApi = retrofit.create(SpaceNewsApi.class);
}
if(myLaunchLibraryApi == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(LaunchLibraryApi.BASE_URL)
.client(getHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
myLaunchLibraryApi = retrofit.create(LaunchLibraryApi.class);
}
}
public static synchronized RetrofitClients getInstance() {
if (instance == null) {
instance = new RetrofitClients();
}
return instance;
}
public SpaceNewsApi getMySpaceNewsApi() {
return mySpaceNewsApi;
}
public LaunchLibraryApi getMyLaunchLibraryApi(){
return myLaunchLibraryApi;
}
public static OkHttpClient getHttpClient() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
//TODO : remove logging interceptors as it is to be used for development purpose
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(300, TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS)
.addInterceptor(logging)
.build();
return client;
}
}
I am trying to access data from the network (data is in the form of gson and i am using WordPress rest API) using retrofit but can't access. it shows me an error like data is null looks like retrofit can't find data but everything is good... looks like code is good and i don't know how to solve this. please help me I am a new developer.. it takes my 3 days
whenever i call getRetrofit() method it works fine... but when i call getImageRetrofit() then looks like this method won't work...this method return null value as shown in the logcat :
ImageInfo: info: null
private void getRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
RetrofitArrayApi service = retrofit.create(RetrofitArrayApi.class);
Call<List<WPPost>> call = service.getPostInfo();
call.enqueue(new Callback<List<WPPost>>() {
#Override
public void onResponse(Call<List<WPPost>> call, Response<List<WPPost>> response) {
Log.e("Latest","response: "+response.body());
for (int i=0; i<response.body().size(); i++)
{
Log.e("main ","title "+response.body().get(i).getTitle().getRendered() + " " +
response.body().get(i).getId() );
String tempDate = response.body().get(i).getDate();
tempDate = tempDate.replace("T"," ");
String tempImageHref = response.body().get(i).getLinks().getWpFeaturedmedia().get(0).getHref();
Log.e("Href", "onResponse: "+tempImageHref);
String link = response.body().get(i).getLink();
Log.e("PostLink",link);
getImageRetrofit(tempImageHref);
list.add(new LatestModel(
response.body().get(i).getTitle().getRendered(),
tempDate,
tempImageHref,
LatestModel.IMAGE_TYPE,
response.body().get(i).getLink()
)
);
}
adapter.notifyDataSetChanged();
}
#Override
public void onFailure(Call<List<WPPost>> call, Throwable t) {
t.printStackTrace();
}
});
}
private void getImageRetrofit(String ImageHref) {
Log.e("getImageRetrofit","called "+ImageHref);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
RetrofitArrayApi service = retrofit.create(RetrofitArrayApi.class);
Call<List<WPPostImage>> callImage = service.getImageInfo(ImageHref);
callImage.enqueue(new Callback<List<WPPostImage>>() {
#Override
public void onResponse(Call<List<WPPostImage>> call, Response<List<WPPostImage>> response) {
Log.e("ImageInfo","info: "+response.body());
}
#Override
public void onFailure(Call<List<WPPostImage>> call, Throwable t) {
Log.e("Link Failed: ",": t.printStackTrace()" );
}
});
}
here is my RetrofitArrayApi Interface.:
public interface RetrofitArrayApi {
#GET("wp-json/wp/v2/posts?per_page=4")
Call<List<WPPost>> getPostInfo();
#GET("{id}")
Call<List<WPPostImage>> getImageInfo(#Path("id") String ImageHref); }
You said at comments that temImageHref: mubashirsaddique.com/wp-json/wp/v2/media/1780 and also your base url is baseUrl = "mubashirsaddique.com". So you send a request to this address mubashirsaddique.com/mubashirsaddique.com/wp-json/wp/v2/media/1780 when call getImageInfo.
Change your getPostInfo service. It should return just id(1780 in your case) as href value and modify RetrofitArrayApi.
#GET("wp-json/wp/v2/media/{id}")
Call<List<WPPostImage>> getImageInfo(#Path("id") String ImageHref);
I need to change base url at run time.
I have login button and when login button click time i am called my login api
like below :
login api = http://192.168.0.61/api/authenticate
API_BASE_URL = http://192.168.0.61/api/
when i get success response from first api i get client server url for changing baseUrl.
CompanyUrlConfigEntity companyUrlConfigEntity = response.body();
like below :
String clientUrl = companyUrlConfigEntity.
getBaseUrl();
clientUrl = http://192.168.0.238/api/
In this project mainly for client or company based.So they have their own server.
Each company has using more than 20 api's.
So i need to change base url .
I am also checked below link for changing base url:
https://futurestud.io/tutorials/retrofit-2-how-to-change-api-base-url-at-runtime-2
and changed code like that
public static void changeApiBaseUrl(String newApiBaseUrl) {
API_BASE_URL = newApiBaseUrl;
builder = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new Gson()));
}
when i debugged and checked my baseUrl then it shows properly like below:
API_BASE_URL = http://192.168.0.238/api/
But when i call my customer api it shows the my first base url calling,
the url not changed.
expected customer api : http://192.168.0.238/api/customers
reality customer api : http://192.168.0.61/api/customers
I am also checked below link :
https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests
thats working , But each api need to pass fullPath url with each api like below:
#GET
public Call<ResponseBody> profilePicture(#Url String url);
But using this method , each api calling place i need to attach full path of url.
There is any other options? Please help me.
ServiceGenerator.class
public class ServiceGenerator {
public static String API_BASE_URL = "http://192.168.0.61/api/";
private static Retrofit retrofit;
private static OkHttpClient.Builder httpClient = new
OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new
Gson()));
private ServiceGenerator() {
}
public static void changeApiBaseUrl(String newApiBaseUrl) {
API_BASE_URL = newApiBaseUrl;
builder = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new Gson()));
}
public static Retrofit retrofit() {
return retrofit;
}
public static <S> S createService(Class<S> serviceClass) {
return createService(serviceClass, null, null);
}
public static <S> S createService(Class<S> serviceClass,
final String authToken,
final ProgressListener progressListener) {
if (authToken != null) {
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
final String headerValue = AUTHORIZATION_TYPE + authToken;
Request request = original.newBuilder()
.header(AUTHORIZATION_HEADER_KEY, headerValue)
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
});
}
addResponseProgressListener(progressListener);
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor httpLoggingInterceptor = new
HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(httpLoggingInterceptor);
}
if (authToken != null) {
if (picasso == null) {
setUpPicasso(authToken);
}
}
OkHttpClient client = httpClient.build();
httpClient.connectTimeout(15, TimeUnit.SECONDS);
httpClient.readTimeout(2, TimeUnit.MINUTES);
httpClient.writeTimeout(2, TimeUnit.MINUTES);
retrofit = builder.client(client).build();
return retrofit.create(serviceClass);
}
}
LoginFragment.java
#OnClick(R.id.bt_login)
void onLogin() {
checkValidityOfUser();
}
private void checkValidityOfUser() {
final Login login = getLoginCredentials();
Call<CompanyUrlConfigEntity> callCheckValidity = dataProcessController.
getApiClient().
checkValidityOfUsers(login.getUsername());
callCheckValidity.enqueue(new Callback<CompanyUrlConfigEntity>() {
#Override
public void onResponse(Call<CompanyUrlConfigEntity> call,
Response<CompanyUrlConfigEntity> response) {
if (response.code() == 200) {
CompanyUrlConfigEntity companyUrlConfigEntity = response.body();
boolean status = companyUrlConfigEntity.isValidUser();
if (status) {
String baseUrls = companyUrlConfigEntity.
getBaseUrl();
baseUrls = baseUrls + "/api/";
ServiceGenerator.changeApiBaseUrl(baseUrls);
logins();
} else {
ToastHelper.show("please contact admin");
}
} else {
ToastHelper.show("" + response.code() + response.message());
}
}
#Override
public void onFailure(Call<CompanyUrlConfigEntity> call, Throwable t) {
ToastHelper.show("please contact admin");
}
});
}
private void logins() {
login = getLoginCredentials();
Call<Void> callLogin = dataProcessController.
getApiClient().
login(login);
callLogin.enqueue(new Callback<Void>() {
#Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.code() == 200) {
} else if (response.code() == 401) {
}
}
#Override
public void onFailure(Call<Void> call, Throwable t) {
}
});
}
Base on your comments, I would say that you are correctly changing the API url on your builder, but that your second call still uses an instance of service where the url has not changed.
To explain a little more, from what I understand this is how everything gets executed:
when fragment is created, the apiClient is created and pointing to the first url
with dataProcessController.getApiClient() in your first call, you are getting the service that is pointing to the first url and then execute the call.
when the call is successful, you read the new url from result and update the ServiceGenerator with that new url. Then you execute the logins() method.
and in that method, you recall the dataProcessController.getApiClient() and do the second call with it. However, as you never redid apiClient = ServiceGenerator.createService(ApiClient.class);, the apiClient instance you are getting is still pointing to the first url, because it hasn't been notified that the url changed.
What I would try here, would be to change the method getApiClient() in your DataProcessController class to something like this:
public ApiClient getApiClient() {
apiClient = ServiceGenerator.createService(ApiClient.class);
return apiClient;
}
and see if this is work better.
Or if you don't want to regenerate the service inside that function, you can also do something like this:
public class DataProcessController {
private ApiClient apiClient = null;
private DataProcessController() {
regenerateClient();
}
public ApiClient getApiClient() { return apiClient; }
// add this to regenerate the client whenever url changes
public void regenerateClient() {
apiClient = ServiceGenerator.createService(ApiClient.class);
}
}
then, everytime you do change the url, do this:
ServiceGenerator.changeApiBaseUrl(baseUrls);
dataProcessController.regenerateClient();
and you should get a client that points to the correct url everytime you do dataProcessController.getApiClient()
https://segunfamisa.com/posts/firebase-remote-config
You should follow concept of firebase remote config. Here you dont need to store base Url in source code it will be retrieved from firebase config values which is stored at server of firebase.
// fetch
mRemoteConfig.fetch(3000)
.addOnCompleteListener(this, new OnCompleteListener<Void>() {
#Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
// update your base url here.
} else {
//task failed
}
}
});
I am using retrofit library with post request, but i did not found data. Give "Internal server error" message.
API_1 : http://www.fabgrad.com/dummy_api_1/
type : POST
data : { us_id:23 }
interface -
public interface FirstApi {
public static String URl = "http://www.fabgrad.com/";
#FormUrlEncoded
#POST("dummy_api_1")
Call<Titles> getData(#Field("us_id") String id);
}
Using retrofi in main activity -
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(FirstApi.URl)
.addConverterFactory(GsonConverterFactory.create())
.build();
FirstApi categoryMenuApi = retrofit.create(FirstApi.class);
String s="23";
Call<Titles> categoryMenuCall = categoryMenuApi.getData(s);
categoryMenuCall.enqueue(new Callback<Titles>() {
#Override
public void onResponse(Call<Titles> call, Response<Titles> response) {
Titles list = response.body();
}
#Override
public void onFailure(Call<Titles> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
I am new in retrofit So please help
You only have to add / at the end of your endpoint #POST("dummy_api_1")
Just like:
#POST("dummy_api_1/")
I have setup a retrofit2 singleton instance which is accessed by all API methods. However, upon logging out the user and re-signing in, all API calls fail with a 403. The likely cause of this is retrofit re-using the previous destroyed access token and not resetting the retrofit instance.
Singleton:
public class RetroGenerator {
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder;
public static Retrofit retrofit;
static synchronized private Retrofit.Builder getBuilder() {
if (builder == null) {
initRetrofit();
}
return builder;
}
public static void initRetrofit() {
builder =
new Retrofit.Builder()
.baseUrl(SessionManager.getInstance().baseUrl)
.addConverterFactory(GsonConverterFactory.create(initGson()));
}
private static Gson initGson() {
return new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").create();
}
public static Retrofit getRetrofit() {
return getBuilder().client(httpClient.build()).build();
}
public static <S> S createService(Class<S> serviceClass) {
return getRetrofit().create(serviceClass);
}
public static <S> S createService(Class<S> serviceClass, final Auth auth) {
if (retrofit == null) {
if (auth != null) {
//Adding request payload logging
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(logging);
}
httpClient.retryOnConnectionFailure(true);
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("Accept", "application/json")
.header("Authorization",
auth.token_type + " " + auth.access_token)
.method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
}
OkHttpClient client = httpClient.build();
if (retrofit == null) {
retrofit = getBuilder().client(client).build();
}
}
return retrofit.create(serviceClass);
}
public static void clearRetrofit(){
retrofit = null;
}
}
An API class would use this by:
RetroGenerator.createService(APIServices.class).getUsers().enqueue(new Callback<ArrayList<User>> () {
#Override
public void onResponse(Call <ArrayList<User>> call, Response <ArrayList<User>> response) {
if (response.isSuccessful()) {
callBack.onSuccess(response.body());
}
}
#Override
public void onFailure(Call <ArrayList<User>> call, Throwable t) {
}
});
To amend this, my attempt was to set the retrofit instance as null upon signing out. Thus, the use of clearRetrofit() method. However, the issue still exists and setting it null didn't fix the issue. What is the reoolution for this?
I solved this problem. Here's the write-up:
Problem:
I needed global access to a retrofit instance without having to re-initialise an instance every time
The code in the question was confusing the public retrofit instance with authenticated endpoints thus leading to a 401: Unauthorised error.
Solution:
The solution I've attached below was written to deliver the following:
- Re-usability of a retrofit instance by using a Singleton design pattern
Ability to write an interface APIServices and include all endpoints in it. This allows extensibility.
Ability to switch between a retrofit built for public endpoints and a retrofit built for authenticated endpoints.
Here's my solution:
https://gist.github.com/dinukapj/b315e5f4438bc670d7509f7aa7aaffdd