How to get different objects with Retrofit Android - android

I'm using Retrofit 1.9.0 in Android Studio to get a response from my REST API.
The method I want to do is GET, on this URL: http://dotfreeride.com/api/rest/adventures.php
I successfully retrieved the response for another API but that had only 1 object, this has 3 big objects.
My IApiMethods interface is like this:
#GET("/adventures.php")
JSONObject getAdventures(
Callback<AdventuresApi> cb
);
My AdventuresApi (Model class) is like this:
public class AdventuresApi {
public String adventure_id;
public String trimaps_context;
public String name;
public String video_url;
public List<ArrayPoi> array_poi;
public class ArrayPoi {
String poi_id;
String name;
String lat;
String lng;
String video_url;
}
}
My Retrofit call in the Activity is like this:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(API_URL)
.build();
IApiMethods methods = restAdapter.create(IApiMethods.class);
Callback callback = new Callback() {
#Override
public void success(Object o, Response response) {
}
#Override
public void failure(RetrofitError error) {
Log.e("JSON", "NO DATA!");
}
};
methods.getAdventures(callback);
I don't really know how to get the objects, I want to get the name of the object (Example: where trimaps_context is "verb", I need the name "Powder Hound")
For a single object I successfully did it like this in onResponse(Object o, Response response):
(ProfileApi) profileData = (ProfileApi) o;
Log.e("JSON", profileData.name + " " + profileData.email);

1) You are trying to combine both synchronous and asynchronous call. If you want to perform a request asynchronously you have to define it like this:
#GET("/adventures.php")
void getAdventures(
Callback<List<AdventuresApi>> cb
);
2) Do not create RestAdapter instance everytime you call request. It's really heavyweight operation. Use singleton pattern. You can then simply call:
ApiManager.getAdapter().getAdventures(...);
3) Object mapping is provided by parametrized Callback class:
ApiManager.getAdapter().getAdventures(
new Callback<List<AdventuresApi>>() {
#Override
public void success(List<AdventuresApi> adventures, Response response) {
// here you can access the adventures list
}
#Override
public void failure(RetrofitError error) {
// handle error
}
});

Related

RxJava thread not waiting for result

I have this method that I am trying to pull data from an API, and then update the text view. Everything works except getRecipeName doesn't finish after the "end Method" log. .getRecipeName() uses RetroFit to pull from an API.
I am currently learning MVP, Dagger, RxJava, and Butterknife all at once using
Mindork's Github page on MVP Architecture
I commented out the .subscribeOn and .observeOn to see the result difference and nothing changed.
#Override
public void onRandomButtonClicked() {
getMvpView().showLoading();
Log.e(TAG, "Random Method Open");
getCompositeDisposable().add(getDataManager()
.getRecipeName()
//.subscribeOn(getSchedulerProvider().io())
//.observeOn(getSchedulerProvider().ui())
.subscribe(new Consumer<String>() {
#Override
public void accept(String s) throws Exception {
Log.e(TAG, "accept");
getMvpView().updateTextView(title);
}
}));
Log.e(TAG, "end method");
}
Here is my getRecipeName() method
#Override
public Observable<String> getRecipeName() {
/*Create handle for the RetrofitInstance interface*/
GetDataService service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
Call<RecipeList> call = service.getRecipe();
call.enqueue(new Callback<RecipeList>() {
#Override
public void onResponse(#NonNull Call<RecipeList> call, #NonNull retrofit2.Response<RecipeList> response) {
Log.e("onResponse","Recipe is Successful = " + response.isSuccessful());
//if response is false then skip to avoid null object reference
if (response.isSuccessful()) {
RecipeList drinkRecipe = response.body();
List<Recipe> recipes = drinkRecipe.getDrinks();
jokeText = String.valueOf(recipes.size());
Recipe myRecipe = recipes.get(0);
jokeText = myRecipe.getStrDrink();
Log.e("On Response", "Result2: " + jokeText);
}
//jokeText = "null";
}
#Override
public void onFailure(Call<RecipeList> call, Throwable t) {
Log.e("On Response","Failure");
}
});
//return jokeText;
return Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
return jokeText;
}
});
}
Solution
So as the comments stated RxJava Adapter was the correct way to go. I will just post my working code on myself using the adapter. I found it very difficult to find a working example.
//single api call using retrofit and rxjava
#SuppressLint("CheckResult")
private void getRandomButtonClick(){
retrofit = RetrofitClientInstance.getRetrofitInstance();
retrofit.create(GetDataService.class).getRecipe()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleResults, this::handleError );
}
private void handleResults(RecipeList recipeList) {
int i = recipeList.getDrinks().size();
Log.e(TAG, "size is: "+ i);
Recipe recipe = recipeList.getDrinks().get(0);
getMvpView().updateTextView(recipe.getStrDrink());
}
private void handleError(Throwable t){
Log.e("Observer", "");
}
My Retrofit Client Instance
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
return retrofit;
}
My Interface
public interface GetDataService {
//#Headers({})
#GET("random.php")
Observable<RecipeList> getRecipe();
I found a great resource to reference for me to correctly implement this. Retrofit Android
The reason is because your observable is returning jokeText every time it is subscribed upon. It returns immediately after invocation and will not wait for your network operation.
One possible solution is to use the RxJavaCallAdapter. Link here: https://github.com/square/retrofit/tree/master/retrofit-adapters/rxjava2
It will automatically convert your API returns to observables. No need to manually invoke retrofit requests. Just process the response and convert it to your desired object from there.
Another approach would be to wrap your entire sequence in an Observable.create or Observable.fromAsync.

how to deserialize json object and add response to arraylist

I am creating an app using soundcloud api but I am getting error while parsing json object, I am new in this things so don't know what I am doing wrong here
Here is my interface
ScService.java
public interface SCService
{
#GET("/resolve.json?url=https://m.soundcloud.com/kshmr/sets/materia&client_id=iZIs9mchVcX5lhVRyQGGAYlNPVldzAoX")
Call<Track> getTrack();
}
Here is my model class
Track.java
public class Track
{
#SerializedName("title")
private String mTitle;
#SerializedName("stream_url")
private String mStreamUrl;
public String getTitle()
{
return mTitle;
}
public String getStreamUrl()
{
return mStreamUrl;
}
}
MainActivity.class
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Config.API_URL)
.addConverterFactory(GsonConverterFactory.create()).build();
SCService Scservice = retrofit.create(SCService.class);
Call<Track> call = Scservice.getTrack();
call.enqueue(new Callback<Track>(){
#Override
public void onResponse(Call<Track> call, Response<Track> response)
{
// TODO: Implement this method
if(response.isSuccessful())
{
//String track = response.body().toString();
//Log.e("jsonres",track);
//gson = new GsonBuilder().create();
gson = new Gson();
Track track = gson.fromJson(response.body().toString(), Track.class);
}
#Override
public void onFailure(Call p1, Throwable p2)
{
// TODO: Implement this method
}
});
}
Here is the JSON response from api callcall
enter code here
{"kind":"track","id":399448641,"created_at":"2018/02/14 11:40:02 +0000","user_id":319295181,"duration":188726,"commentable":true,"state":"finished","original_content_size":33279566,"last_modified":"2018/03/10 17:33:18 +0000","sharing":"public","tag_list":"KSHMR \"House of Cards\" \"Sidnie Tipton\" Dharma \"Spinnin' \"","permalink":"houseofcards-mixmaster-05b","streamable":true,"embeddable_by":"all","purchase_url":"http://www.spinninrecords.com/releases/house-of-cards","purchase_title":"Download/Stream","label_id":null,"genre":"Dance & EDM","title":"KSHMR - House of Cards (Ft. Sidnie Tipton)","description":"KSHMR and Sidnie Tipton team up again, this time for the bittersweet sound of \"House of Cards\" \n\nDownload / Stream here: https://www.spinninrecords.com/releases/house-of-cards/","label_name":null,"release":null,"track_type":null,"key_signature":null,"isrc":null,"video_url":null,"bpm":null,"release_year":null,"release_month":null,"release_day":null,"original_format":"wav","license":"all-rights-reserved","uri":"https://api.soundcloud.com/tracks/399448641","user":{"id":319295181,"kind":"user","permalink":"dharmaworldwide","username":"Dharma Worldwide","last_modified":"2018/03/09 12:08:27 +0000","uri":"https://api.soundcloud.com/users/319295181","permalink_url":"http://soundcloud.com/dharmaworldwide","avatar_url":"https://i1.sndcdn.com/avatars-000324744374-jdrkyv-large.jpg"},"permalink_url":"https://soundcloud.com/dharmaworldwide/houseofcards-mixmaster-05b","artwork_url":"https://i1.sndcdn.com/artworks-000302088414-recq7g-large.jpg","stream_url":"https://api.soundcloud.com/tracks/399448641/stream","download_url":"https://api.soundcloud.com/tracks/399448641/download","playback_count":135077,"download_count":0,"favoritings_count":7351,"reposts_count":1354,"comment_count":120,"downloadable":false,"waveform_url":"https://w1.sndcdn.com/0Bcy6WpC8dzY_m.png","attachments_uri":"https://api.soundcloud.com/tracks/399448641/attachments","policy":"ALLOW","monetization_model":"NOT_APPLICABLE"}
I can't use gson.fromJson(...) method, how could I fix this?
Ps-I have pretty much changed my code.
You should do:
EDIT:
ScService.java
public interface SCService
{
#GET("users/17586135/tracks?client_id=iZIs9mchVcX5lhVRyQGGAYlNPVldzAoX")
Call<Track> getTrack();
}
MainActivity.class
Call<Track> call = Scservice.getTracks();
call.enqueue(new Callback<Track>(){
#Override
public void onResponse(Call call, Response<Track> response)
{
// Get the result
Track track = response.body();
}
#Override
public void onFailure(Call p1, Throwable p2)
{
// TODO: Implement this method
}
});
}
More here
The Gson object should be used in this way:
gson = new GsonBuilder().create();
Track track = gson.fromJson(response.body().toString(),Track.class);

How can I avoid request parameter names getting encoded when making a form-urlencoded Get request in Retrofit?

I am currently developing android app which uses Retrofit & OkHttpClient to get/send data from the server.
That was great when calling my own server, while it runs into 404 error when trying to call google map api.
The following represents response with error.
Response{protocol=h2, code=404, message=, url=https://maps.googleapis.com/maps%2Fapi%2Fgeocode%2Fjson%3Fkey=defesdvmdkeidm&latlng=11.586215,104.893197}
This is obviously because '/' and '?' was encoded into "%2F" and "%3F".
The solution could be prevent urlencode for those special characters, but couldn't make it.
What I tried is add custom header "Content-Type:application/x-www-form-urlencoded; charset=utf-8" to OkHttpClient via intercepter but that does not work.
Best detailed response will be appreciated.
Regards.
private Retrofit createRetrofit(OkHttpClient client, String _baseUrl) {
return new Retrofit.Builder()
.baseUrl(_baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
}
private Retrofit createGoogleRetrofit() {
return createRetrofit(createGoogleClient(), baseUrl);
}
public DenningService getGoogleService() {
_baseUrl = "https://maps.googleapis.com/";
final Retrofit retrofit = createGoogleRetrofit();
return retrofit.create(DenningService.class);
}
public interface DenningService {
#GET("{url}")
#Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")
Single getEncodedRequest(#Path("url") String url);
}
private void sendRequest(final CompositeCompletion completion, final ErrorHandler errorHandler) {
mCompositeDisposable.add(mSingle.
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Function() {
#Override
public JsonElement apply(JsonElement jsonElement) throws Exception {
return jsonElement;
}
})
.subscribeWith(new DisposableSingleObserver() {
#Override
public void onSuccess(JsonElement jsonElement) {
completion.parseResponse(jsonElement);
}
#Override
public void onError(Throwable e) {
if (e instanceof HttpException && ((HttpException) e).code() == 410) {
errorHandler.handleError("Session expired. Please log in again.");
} else {
errorHandler.handleError(e.getMessage());
}
e.printStackTrace();
}
})
);
}
public void sendGoogleGet(String url, final CompositeCompletion completion) {
mSingle = getGoogleService().getEncodedRequest(url);
sendRequest(completion, new ErrorHandler() {
#Override
public void handleError(String error) {
ErrorUtils.showError(context, error);
}
});
}
The problem is in the definition of your Retrofit service interface and the values you pass to it.
public interface DenningService {
#GET("{url}")
#Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")
Single getEncodedRequest(#Path("url") String url);
}
From what you've posted, I'm going to assume the value of url is:
maps/api/geocode/json?key=defesdvmdkeidm&latlng=11.586215,104.893197
Here's how it should look:
public interface DenningService {
#FormUrlEncoded
#GET("/maps/api/geocode/json")
Single getEncodedRequest(#Field("key") String key,
#Field("latlng") String latlng);
}
And then you'd call it like this:
mSingle = getGoogleService().getEncodedRequest(key, latlng);
Of course, you will have to figure out how to separate the key and latlng parameters out of the current url string.
Edit
It's not obvious to me whether or not you actually want your request to be application/x-www-form-urlencoded, or if you were just trying that to see if it solved your problem. If you do not want it, then your interface would look like this instead:
public interface DenningService {
#GET("/maps/api/geocode/json")
Single getEncodedRequest(#Query("key") String key,
#Query("latlng") String latlng);
}

Android i get NULL when i post data with Retrofit

I'm novice on using Retrofit, I want to post data as an json data with object format to server and get response from that, I tested my restful url with fake data and that work fine without any problem, but when i post data from android i get null. what i want to do? i want to post data to server and get response with this format:
public class UserLoginInformation {
private String username;
private String userUniqueId;
}
My interface:
public interface SignalRetrofitServiceProviders {
#POST("joinUserToApplication")
Call<List<UserLoginInformation>> joinUserToApplication(#Body Object data);
}
post data:
private void joinUserToApplication(String data) {
AlachiqRestFullProvider signalProvider = new AlachiqRestFullProvider();
SignalRetrofitServiceProviders signalRetrofitServiceProviders = signalProvider.getServices();
Call<List<UserLoginInformation>> call = signalRetrofitServiceProviders.joinUserToApplication(data);
call.enqueue(new Callback<List<UserLoginInformation>>() {
#Override
public void onResponse(Call<List<UserLoginInformation>> call, Response<List<UserLoginInformation>> response) {
List<UserLoginInformation> result = response.body();
final String r = new Gson().toJson(result);
}
#Override
public void onFailure(Call<List<UserLoginInformation>> call, Throwable t) {
t.printStackTrace();
Log.e("onFailure ", t.getMessage());
}
});
}
RestFull provider:
public class AlachiqRestFullProvider {
private SignalRetrofitServiceProviders signalRetrofitServiceProviders;
public AlachiqRestFullProvider() {
OkHttpClient httpClient = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ClientSettings.ALACHIQ_WEB_BASE_URL)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
signalRetrofitServiceProviders = retrofit.create(SignalRetrofitServiceProviders.class);
}
public SignalRetrofitServiceProviders getServices() {
return signalRetrofitServiceProviders;
}
}
data for post:
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("mobileNumber", mobileNumber);
jsonObject.put("userUniqueId", uuid);
jsonObject.put("userPhoneNumbers", phoneContacts);
startService(
new Intent(context, AlachiqRestFullWebServiceProvider.class)
.putExtra("request_type", "joinUserToApplication")
.putExtra("data", jsonObject.toString()));
} catch (JSONException e) {
e.printStackTrace();
}
server response data like with this format:
{"username":"mahdi","userUniqueId":"fwcrwcrwr23234c24"}
server side application to get data is:
Route.post('joinUserToApplication', function *(request, response) {
console.log(request._raw);
response.send({username: "mahdi", userUniqueId: "fwcrwcrwr23234c24"});
});
The POST body that is being serialized is a generic Object.
Create a POJO with the fields that you require and use a deserializer that retrofit understands
public interface SignalRetrofitServiceProviders {
#POST("joinUserToApplication")
Call<List<UserLoginInformation>> joinUserToApplication(#Body UserLoginInformation data);
}
Please note the parameter of the function is not changed to UserLoginInformation
http://square.github.io/retrofit/#restadapter-configuration

Retrofit parse empty array []

I need to parse list of object, whith can be emply. {"data":[]}
I use tamplated callback CallBack<T>called with
public static DataList {
public List<Data> data
};
api.getData(new Callback<DataList>() {...});
it crashed with error:java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com...DataList
Please help
Your model should work fine. Perhaps your server isn't returning what you think it does, or maybe its not application/json what it's returning?
Here's a quick demo:
Doing a GET on the url http://www.mocky.io/v2/5583c7fe2dda051e04bc699a will return the following json:
{
data: [ ]
}
If you run the following class, you'll see it works just fine:
public class RetrofitDemo {
interface API {
#GET("/5583c7fe2dda051e04bc699a")
void getDataList(Callback<DataList> cb);
}
static class DataList {
List<Data> data;
}
static class Data {
}
public static void main(String[] args) {
API api = new RestAdapter.Builder()
.setEndpoint("http://www.mocky.io/v2")
.build()
.create(API.class);
api.getDataList(new Callback<DataList>() {
#Override
public void success(DataList dataList, Response response) {
System.out.println("dataList=" + dataList);
}
#Override
public void failure(RetrofitError retrofitError) {
throw retrofitError;
}
});
}
}
Your issue is your java model doesn't reflect the data it's trying to deserialize to.
//{"data":[]} does not map to List<Data> data.
// If the server was just returning an array only then it would work.
// It will match to the entity below make sure your cb = Callback<MyItem>
public class MyItem {
List<Data> data;
}

Categories

Resources