I have the following approach for getting instance of my endpoint. I want to add a header to it. How do I do that? Please modify my code following, to include the header bit. Thanks.
public class RemoteServiceEndpointReference {
private static final boolean USING_LOCAL_SERVER = false;
private static final String LOCAL_SERVER_PATH = “…”;
private static RemoteService service;
public static RemoteService getRemoteServiceEndpoint() {
if (null != service) {
return service;
}
RemoteService.Builder builder = new RemoteService.Builder(
AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(),
null
);
forLocalServer(builder);
service = builder.build();
return service;
}
private static void forLocalServer(AbstractGoogleJsonClient.Builder builder) {
if (USING_LOCAL_SERVER) {
builder.setRootUrl(LOCAL_SERVER_PATH)
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> request) throws IOException {
request.setDisableGZipContent(true);
}
});
}
}
}
You should be able to do something like this:
#Override
public void initialize(AbstractGoogleClientRequest<?> request) throws IOException {
HttpHeaders yourHeaders = new HttpHeaders();
header.set("yourHeader", value);
// ...
request.setDisableGZipContent(true);
request.setRequestHeaders(yourHeaders); // setting the headers
}
Related
I am using spoonacular API for a recipe app project. The problem occurs when trying to making multiple GET requests to the API. The first request is a simple search with a query parameter. The resulting JSON of the first request contains a Recipe ID and I use that ID to make the second GET request , where the problem occurs.
The API responds only when I make the request the first time but after that it responds with error code 500 [Internal Server Error].
I have tested the GET request on Postman but there it works fine every time.
I'm new to working with API's and any help would be immensely appreciated.
This is my Retrofit Service Class
public class ServiceGenerator {
public static final String API_BASE_URL = "https://spoonacular-recipe-food-nutrition-v1.p.rapidapi.com/";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.build();
public static <S> S createService(Class<S> serviceClass, final String HostName, final String KeyVal)
{
if (!TextUtils.isEmpty(HostName) && !TextUtils.isEmpty(KeyVal))
{
HeadersInterceptor interceptor = new HeadersInterceptor(HostName,KeyVal);
if (!httpClient.interceptors().contains(interceptor))
{
httpClient.addInterceptor(interceptor);
builder.client(httpClient.build());
retrofit = builder.build();
}
}
return retrofit.create(serviceClass);
}
This is the Interceptor I am using to add Headers with the request.
public class HeadersInterceptor implements Interceptor {
private String HostName,KeyVal;
HeadersInterceptor(final String HostName,final String KeyVal) {
this.HostName = HostName;
this.KeyVal = KeyVal;
}
#NotNull
#Override
public Response intercept(#NotNull Chain chain) throws IOException {
Request original = chain.request();
Request.Builder builder = original.newBuilder()
.addHeader("X-RapidAPI-Host",HostName)
.addHeader("X-RapidAPI-Key",KeyVal);
Request request = builder.build();
return chain.proceed(request);
}
}
This is my Fragment which makes a search query and SUCCESSFULLY return results[Receipe ID's]
public class ListSelectedFragments extends Fragment {
private ProgressBar PreviewFragPrg;
private final String TAG = "ListSelectedFragment->";
private PreviewRecipeAdapter adapter;
private RecyclerView SelectedItemRV;
private ArrayList<RecipePreviewHolder> RecipePreviewsList = new ArrayList<>();
public ListSelectedFragments() {
// Required empty public constructor
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
final View view = inflater.inflate(R.layout.fragment_list_selected_fragments, container, false);
SelectedItemRV = view.findViewById(R.id.SelectedItemRV);
TextView DisplayNameTV = view.findViewById(R.id.DisplayNameTV);
PreviewFragPrg = view.findViewById(R.id.PreviewFragPrg);
ImageView BackBtn = view.findViewById(R.id.BackBtn);
BackBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (getFragmentManager() != null) {
getFragmentManager().popBackStackImmediate();
}
}
});
if (getArguments() != null) {
final String QueryTag = getArguments().getString("QueryTag");
final String CuisineName = getArguments().getString("CuisineName");
if(CuisineName!=null){
DisplayNameTV.setText(CuisineName);
}
if(QueryTag!=null){
ProcessQuery(QueryTag);
}
}
return view;
}
private void ProcessQuery(final String QueryStr){
String hostname = getResources().getString(R.string.spoonacular_host_name);
String key = getResources().getString(R.string.spoonacular_apikey_val);
final ServiceGenerator.GetDataService mService =
ServiceGenerator.createService(ServiceGenerator.GetDataService.class, hostname,key);
Call<RecipeInfoModel> call = mService.getRecipes(QueryStr);
call.enqueue(new Callback<RecipeInfoModel>() {
#Override
public void onResponse(#NonNull Call<RecipeInfoModel> call,
#NonNull Response<RecipeInfoModel> response)
{
Log.d(TAG, "Request Response Received");
Log.d(TAG, response.toString());
if (response.body() != null) {
Results[] mRES = response.body().getResults();
SetUpRecipePreviews(mRES);
PreviewFragPrg.setVisibility(View.GONE);
}
}
#Override
public void onFailure(#NonNull Call<RecipeInfoModel> call, #NonNull Throwable t) {
Log.d(TAG, "Request Failed");
Log.d(TAG, call.toString());
Log.d(TAG, "Throwable ->" + t);
PreviewFragPrg.setVisibility(View.GONE);
Toast.makeText(getActivity(),"Could not get required recipes",Toast.LENGTH_SHORT).show();
}
});
Log.d(TAG, "User Inputed Request\n"+call.request().url().toString());
}
private void SetUpRecipePreviews(final Results[] mRES) {
RecipePreviewsList.clear();
adapter = new PreviewRecipeAdapter(getActivity(),RecipePreviewsList);
SelectedItemRV.setLayoutManager(new GridLayoutManager(getActivity(), 2));
SelectedItemRV.setAdapter(adapter);
for (Results mRE : mRES) {
String ImgUrls = mRE.getImage();
RecipePreviewHolder obj = new RecipePreviewHolder(Integer.valueOf(mRE.getId()),
mRE.getTitle(), ImgUrls);
Log.d("GlideLogs->","Rid->"+mRE.getId());
Log.d("GlideLogs->","Img URL->"+ ImgUrls);
Log.d("GlideLogs->","Name->"+mRE.getTitle());
RecipePreviewsList.add(obj);
}
if(RecipePreviewsList.size()>1){
adapter.notifyDataSetChanged();
}
}
This is the Activity I transition to from my Fragment after clicking on a Recipe Card... Sending the Recipe ID in the extras. This function is called immediately after receiving intent extras.
private void RetrieveRecipeInfo(final int recipeID) {
String hostname = getResources().getString(R.string.spoonacular_host_name);
String key = getResources().getString(R.string.spoonacular_apikey_val);
final ServiceGenerator.GetDataService mService =
ServiceGenerator.createService(ServiceGenerator.GetDataService.class, hostname,key);
Call<RecipeDetailedInfo> call = mService.getInformation(185071);
Log.d(TAG , "Your GET Request:\n"+call.request().url().toString());
call.enqueue(new Callback<RecipeDetailedInfo>() {
#Override
public void onResponse(#NonNull Call<RecipeDetailedInfo> call, #NonNull Response<RecipeDetailedInfo> response)
{
Log.d(TAG,"OnResponse() Called\n");
Log.d(TAG,"Response = "+ response);
if(response.body()!=null) {
String obj = response.body().getSourceUrl();
Log.d(TAG,"Getting Recipe Info\n");
Log.d(TAG, String.valueOf(obj));
}
}
#Override
public void onFailure(#NonNull Call<RecipeDetailedInfo> call, #NonNull Throwable t){
}
});
}
Using postman I get the results every time but in my application the API stops responding after the first request. Is there a problem with the way I'm including headers?
So I finally got things working. The problem was with the HeadersInterceptor.java. I was using the Interceptor to add the Headers with the call but I found out a much easier way and it works like a charm.
Simply add #Header with the call to add headers without interceptor in Retrofit.
public interface GetDataService {
#GET("recipes/complexSearch?")
Call<RecipeInfoModel> getRecipes(
#Header("X-RapidAPI-Host") String api,
#Header("X-RapidAPI-Key") String apiKey,
#Query("query") String query_str);
}
The first time, I started to write test cases for Android app, I have searched a lot but could not find the full-phased proper way
Like I am testing Instrumentation Test cases in "androidTest" folder
Below is code for LoginTestCases
#Test
public void loginUserAccount() {
NetworkHelper.MOCK_URL =
String userName = "abacus#hotmail.com";
String password = "abacus#112";
boolean keepMeLoggedInChecked = false;
boolean touchIdEnabled = false;
boolean optIn = false;
mAuthenticationService.logIn(userName, password, keepMeLoggedInChecked, touchIdEnabled, optIn).done(new DoneCallback<LogInResponse>() {
#Override
public void onDone(final LogInResponse result) {
Assert.assertEquals(true,true);
}
}).fail(new FailCallback<Throwable>() {
#Override
public void onFail(Throwable result) {
Assert.assertEquals(true,false);
}
});
}
For testing I need to change MOCK_URL value to "/" so I can implement MockWebServer and choose what response I want, I have used static variable MOCK_URL like below code
public class NetworkHelper {
private final OkHttpClient mOkHttpClient;
public static HttpUrl MOCK_URL = null;
public NetworkHelper(OkHttpClient httpClient) {
mOkHttpClient = httpClient;
}
public Retrofit initialize(String baseUrl) {
if(MOCK_URL!=null) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(MOCK_URL)
.addConverterFactory(JacksonConverterFactory.create())
.client(mOkHttpClient)
.build();
return retrofit;
I know that static is not the proper way if anybody knows another way then please give me the solution.
I am working with Google App Engine .I want to Send GCM toke(RegID) to App Engine Server.But Problem is that when i pass RegId as a parameter Nothing happen and when i pass normal string like"abcefghi" the string is passed to server
my code is shown below
public class SendInstanceidAsync extends AsyncTask<Void, Void, UpdateInstanceIdResult> {
private UpdateinstanceidAPI updateinstanceidAPIservice;
private String token;
private String scholarNumber;
private String schoolId;
private RegistrationIntentService registrationIntentService;
public SendInstanceidAsync(RegistrationIntentService registrationIntentService, String token, String schoolId, String scholarNumber) {
this.token = token;
this.scholarNumber = scholarNumber;
this.schoolId = schoolId;
this.registrationIntentService = registrationIntentService;
}
#Override
protected UpdateInstanceIdResult doInBackground(Void... params) {
UpdateInstanceIdResult updateInstanceIdResult = new UpdateInstanceIdResult();
if (updateinstanceidAPIservice == null) {
UpdateinstanceidAPI.Builder builder = new UpdateinstanceidAPI.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
.setRootUrl("http://xxx.xxx.x.x:xxxx/_xx/api/")
.setGoogleClientRequestInitializer(new UpdateinstanceidAPIRequestInitializer() {
#Override
protected void initializeUpdateinstanceidAPIRequest(UpdateinstanceidAPIRequest<?> request) throws IOException {
request.setDisableGZipContent(true);
}
});
updateinstanceidAPIservice = builder.build();
}
try {
updateInstanceIdResult = updateinstanceidAPIservice.updateInstanceID(schoolId, scholarNumber, token).execute();
} catch (IOException e) {
e.printStackTrace();
}
return updateInstanceIdResult;
}
I use MockRestAdapter to return mock data in my tests, but I'd also like to test errors (401, 503, UnknownHostException, etc)
For SocketTimeoutException, there's an API, but how about different response code?
I've tried MockWebServer but no matter what I enqueue, I always get a 200 with the mock data from the adapter.
update: I want to run my tests like this:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class LoginActivityTest {
#Test public void goodCredentials() {
activity.login("username", "password");
assert(...); // Got back 200 and user object (from mock)
}
#Test public void wrongCredentials() {
activity.login("username", "wrong_password");
something.setResponse(401, "{error: wrong password}");
assert(...);
}
#Test public void someError() {
activity.login("username", "password");
something.setResponse(503, "{error: server error}");
assert(...);
}
}
update 2:
Found something, rather ugly, but does what I need:
MockApi implements ServiceApi {
public static Throwable throwable;
#Override login(Callback<User> callback) {
if (throwable != null) {
sendError(callback)
} else {
callback.success(new User("{name:test}"));
}
}
private void sendError(Callback callback) {
callback.failure(RetrofitError.unexpectedError("", throwable));
}
}
public class LoginActivityTest {
#Test public void someError() {
MockApi.throwable = new InterruptedIOException()
activity.login("username", "password");
// Assert having a time out message
}
#Test public void someError() {
MockApi.throwable = new UnknownHostException()
activity.login("username", "password");
// Assert having a no internet message
}
}
Still working on it, so any feedback will help :)
It's fairly easy to do. You just need to implement Client and pass it when you build your mock RestAdapter.
Creating client with appropriate response:
Client client = new Client() {
#Override public Response execute(Request request) throws IOException {
final String reason = "Some reason.";
final List<Header> headers = new ArrayList<>();
final TypedString body = new TypedString("");//could be json or what ever you want
final int status = 401;
return new Response(request.getUrl(), status, reason, headers, body);
}
};
And passing it to your RestAdapter.Builder:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.com")
.setClient(client)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
restAdapter.create(API.class);
My android app gets its data using REST API. I want to have client side caching implemented. Do we have any inbuilt classes for this?
if not, is these any code that i can reuse? I remember coming across such code sometime back. However I cant find it.
If nothing else works, i will write my own. following is basic structure
public class MyCacheManager {
static Map<String, Object> mycache;
public static Object getData(String cacheid) {
return mycache.get(cacheid);
}
public static void putData(String cacheid, Object obj, int time) {
mycache.put(cacheid, obj);
}
}
how do i enable time for cached objects? also - whats the best way to serialize? cache should be intact even if app is closed and reopened later (if time has not expired).
Thanks
Ajay
Now awesome library Volley released on Google I/O 2013 which helps for improve over all problems of calling REST API:
Volley is a library,it is library called Volley from the Android dev team. that makes networking for Android apps easier and most importantly, faster. It manages the processing and caching of network requests and it saves developers valuable time from writing the same network call/cache code again and again. And one more benefit of having less code is less number of bugs and that’s all developers want and aim for.
Example for volley: technotalkative
One of the best ways is to use Matthias Käppler's ignited librarys to make http requests that caches the responses in memory (weak reference) and on file. Its really configurable to do one or the other or both.
The library is located here : https://github.com/mttkay/ignition with examples located here : https://github.com/mttkay/ignition/wiki/Sample-applications
Personally, I love this lib from when it was called Droidfu
Hope this helps you as much as it did me Ajay!
First check the device is connected from the internet or not.
public class Reachability {
private final ConnectivityManager mConnectivityManager;
public Reachability(Context context) {
mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
public boolean isConnected() {
NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnectedOrConnecting();
}}
If device is connected from internet then get the data from API and cache it else get the data from cache.
public class CacheManager {
Cache<String, String> mCache;
private DiskLruCache mDiskLruCache;
private final Context mContext;
public CacheManager(Context context) throws IOException {
mContext = context;
setUp();
mCache = DiskCache.getInstanceUsingDoubleLocking(mDiskLruCache);
}
public void setUp() throws IOException {
File cacheInFiles = mContext.getFilesDir();
int version = BuildConfig.VERSION_CODE;
int KB = 1024;
int MB = 1024 * KB;
int cacheSize = 400 * MB;
mDiskLruCache = DiskLruCache.open(cacheInFiles, version, 1, cacheSize);
}
public Cache<String, String> getCache() {
return mCache;
}
public static class DiskCache implements Cache<String, String> {
private static DiskLruCache mDiskLruCache;
private static DiskCache instance = null;
public static DiskCache getInstanceUsingDoubleLocking(DiskLruCache diskLruCache){
mDiskLruCache = diskLruCache;
if(instance == null){
synchronized (DiskCache.class) {
if(instance == null){
instance = new DiskCache();
}
}
}
return instance;
}
#Override
public synchronized void put(String key, String value) {
try {
if (mDiskLruCache != null) {
DiskLruCache.Editor edit = mDiskLruCache.edit(getMd5Hash(key));
if (edit != null) {
edit.set(0, value);
edit.commit();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public synchronized String get(String key) {
try {
if (mDiskLruCache != null) {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(getMd5Hash(key));
if (snapshot == null) {
// if there is a cache miss simply return null;
return null;
}
return snapshot.getString(0);
}
} catch (IOException e) {
e.printStackTrace();
}
// in case of error in reading return null;
return null;
}
#Override
public String remove(String key) {
// TODO: implement
return null;
}
#Override
public void clear() {
// TODO: implement
}
}
public static String getMd5Hash(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
BigInteger number = new BigInteger(1, messageDigest);
String md5 = number.toString(16);
while (md5.length() < 32)
md5 = "0" + md5;
return md5;
} catch (NoSuchAlgorithmException e) {
Log.e("MD5", e.getLocalizedMessage());
return null;
}
}}
Create the CacheInterceptor class to cache the network response and handle the errors
public class CacheInterceptor implements Interceptor{
private final CacheManager mCacheManager;
private final Reachability mReachability;
public CacheInterceptor(CacheManager cacheManager, Reachability reachability) {
mCacheManager = cacheManager;
mReachability = reachability;
}
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String key = request.url().toString();
Response response;
if (mReachability.isConnected()) {
try {
response = chain.proceed(request);
Response newResponse = response.newBuilder().build();
if (response.isSuccessful()) {
if (response.code() == 204) {
return response;
}
// save to cache this success model.
mCacheManager.getCache().put(key, newResponse.body().string());
// now we know that we definitely have a cache hit.
return getCachedResponse(key, request);
}else if (response.code() >= 500) { // accommodate all server errors
// check if there is a cache hit or miss.
if (isCacheHit(key)) {
// if data is in cache, the return the data from cache.
return getCachedResponse(key, request);
}else {
// if it's a miss, we can't do much but return the server state.
return response;
}
}else { // if there is any client side error
// forward the response as it is to the business layers to handle.
return response;
}
} catch (ConnectException | UnknownHostException e) {
// Internet connection exception.
e.printStackTrace();
}
}
// if somehow there is an internet connection error
// check if the data is already cached.
if (isCacheHit(key)) {
return getCachedResponse(key, request);
}else {
// if the data is not in the cache we'll throw an internet connection error.
throw new UnknownHostException();
}
}
private Response getCachedResponse(String url, Request request) {
String cachedData = mCacheManager.getCache().get(url);
return new Response.Builder().code(200)
.body(ResponseBody.create(MediaType.parse("application/json"), cachedData))
.request(request)
.protocol(Protocol.HTTP_1_1)
.build();
}
public boolean isCacheHit(String key) {
return mCacheManager.getCache().get(key) != null;
}}
Now add the this interceptor in OkHttpClient while creating the service using Retrofit.
public final class ServiceManager {
private static ServiceManager mServiceManager;
public static ServiceManager get() {
if (mServiceManager == null) {
mServiceManager = new ServiceManager();
}
return mServiceManager;
}
public <T> T createService(Class<T> clazz, CacheManager cacheManager, Reachability reachability) {
return createService(clazz, HttpUrl.parse(ServiceApiEndpoint.SERVICE_ENDPOINT), cacheManager, reachability);
}
private <T> T createService(Class<T> clazz, HttpUrl parse, CacheManager cacheManager, Reachability reachability) {
Retrofit retrofit = getRetrofit(parse, cacheManager, reachability);
return retrofit.create(clazz);
}
public <T> T createService(Class<T> clazz) {
return createService(clazz, HttpUrl.parse(ServiceApiEndpoint.SERVICE_ENDPOINT));
}
private <T> T createService(Class<T> clazz, HttpUrl parse) {
Retrofit retrofit = getRetrofit(parse);
return retrofit.create(clazz);
}
private <T> T createService(Class<T> clazz, Retrofit retrofit) {
return retrofit.create(clazz);
}
private Retrofit getRetrofit(HttpUrl httpUrl, CacheManager cacheManager, Reachability reachability) {
return new Retrofit.Builder()
.baseUrl(httpUrl)
.client(createClient(cacheManager, reachability))
.addConverterFactory(getConverterFactory())
.build();
}
private OkHttpClient createClient(CacheManager cacheManager, Reachability reachability) {
return new OkHttpClient.Builder().addInterceptor(new CacheInterceptor(cacheManager, reachability)).build();
}
private Retrofit getRetrofit(HttpUrl parse) {
return new Retrofit.Builder()
.baseUrl(parse)
.client(createClient())
.addConverterFactory(getConverterFactory()).build();
}
private Retrofit getPlainRetrofit(HttpUrl httpUrl) {
return new Retrofit.Builder()
.baseUrl(httpUrl)
.client(new OkHttpClient.Builder().build())
.addConverterFactory(getConverterFactory())
.build();
}
private Converter.Factory getConverterFactory() {
return GsonConverterFactory.create();
}
private OkHttpClient createClient() {
return new OkHttpClient.Builder().build();
}}
Cache interface
public interface Cache<K, V> {
void put(K key, V value);
V get(K key);
V remove(K key);
void clear();}