I am writing an android app that will use Retrofit to make API requests.
I have a helper class like this:
public class ApiService {
public static final String TAG = ApiService.class.getSimpleName();
public static final String BASE_URL = "https://myapiurl.com";
public static void testApi(){
ApiEndpointInterface apiService = prepareService();
apiService.ping(new Callback<Response>() {
#Override
public void success(Response apiResponse, retrofit.client.Response response) {
Log.e(TAG, apiResponse.toString());
}
#Override
public void failure(RetrofitError error) {
Log.e("Retrofit:", error.toString());
}
});
}
private static ApiEndpointInterface prepareService() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(BASE_URL)
.build();
ApiEndpointInterface apiService =
restAdapter.create(ApiEndpointInterface.class);
restAdapter.setLogLevel(RestAdapter.LogLevel.FULL);
return apiService;
}
}
And my actual Retrofit implementation is simple:
public class ApiEndpointInterface {
#GET("/v1/myendpoint")
void ping(Callback<Response> cb);
}
The problem is, I cannot build the project, I get the error:
Error:(12, 10) error: missing method body, or declare abstract
Referring to my ApiEndpointInterface class.
Any idea what's going on?
Try public interface for your API declaration.
public interface ApiEndpointInterface {
#GET("/v1/myendpoint")
void ping(Callback<Response> cb);
}
Also, looks like you're creating your ApiEndpointInterface before telling the builder to set log level to full.
private static ApiEndpointInterface prepareService() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(BASE_URL)
.setLogLevel(RestAdapter.LogLevel.FULL);
.build();
ApiEndpointInterface apiService =
restAdapter.create(ApiEndpointInterface.class);
return apiService;
}
In case you update to okHttp Version 2.4.0 , you will get an exception for empty Body as latest version no more allows zero length request , in which case you would have to use the following syntax
public interface ApiEndpointInterface {
#GET("/v1/myendpoint")
void ping(Callback<Response> cb, #Body String dummy);
}
call
ApiEndpointInterface apiService =
restAdapter.create(ApiEndpointInterface.class);
apiService.ping(callback,"");
Ref
https://github.com/square/okhttp/issues/751
Related
I'm working on an Android Project right now and I'm trying to parse from an URL. In my "ApiClient" I have no problem to parse. Here is my "ApiClient" class:
public class ApiClient implements Callback<Map<String, Channel>> {
static final String BASE_URL = "someURL";
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());
}
...
}
I'm trying to get the response into a List from using callback in my "Radio" class. This the place where I'm having the problem. I tried this three too but it didn't solved my problem:
private List<Channel> listChannels = new ArrayList<Channel>();
private List<Channel> listChannels = new ArrayList<>();
private List<Channel> listChannels = new List<>();
Here is my "Radio" class:
public class Radio {
private static final String STORAGE = "radio";
private List<Channel> listChannels;
public static Radio get() {
return new Radio();
}
private SharedPreferences storage;
private Radio() {
storage = App.getInstance().getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
}
public List<Channel> getData() {
RestInterface restInterface = SingletonClass.getService();
restInterface.getChannels().enqueue(new Callback<Map<String, Channel>>() {
#Override
public void onResponse(Call<Map<String, Channel>> call, Response<Map<String, Channel>> response) {
if(response.isSuccessful()){
Map<String, Channel> body = response.body();
List<Channel> channels = new ArrayList<>(body.values());
loadChannels(channels);
}
}
#Override
public void onFailure(Call<Map<String, Channel>> call, Throwable t) {
}
});
System.out.println(listChannels.get(1).getArtist());
return listChannels;
}
public boolean isRated(int itemId) {
return storage.getBoolean(String.valueOf(itemId), false);
}
public void setRated(int itemId, boolean isRated) {
storage.edit().putBoolean(String.valueOf(itemId), isRated).apply();
}
private void loadChannels(List<Channel> channels){
listChannels.clear();
listChannels.addAll(channels);
}
}
Here is my interface class:
public interface RestInterface {
#GET("someURL")
retrofit2.Call<Map<String, Channel>> getChannels();
}
and my SingletonClass:
public class SingletonClass{
private static final Retrofit RETROFIT = new Retrofit.Builder()
.baseUrl(someURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
private static final RestInterface SERVICE = RETROFIT.create(RestInterface.class);
public static RestInterface getService(){
return SERVICE;
}
}
I don't know what should I do to fill the List in my Radio class now. I'm totally open to suggestions. Thanks for the help.
Are you getting an empty list? You're asynchronously setting in the channel data in getData(), so if you're trying to get the data by reading it in the next line, it may not be loaded yet.
This means that when you call System.out.println(listChannels.get(1).getArtist()), you won't see the result of loadChannels, because that call happens right after you call enqueue() while loadChannels() is running on a separate thread. If you moved that into onResponse() you might have more luck.
In Android, a fairly easy way to do things like this and interact with the UI is by using AsyncTask, which for you would look something like this:
private class loadChannelTask extends AsyncTask<Void, Void, List<Channel>> {
protected List<Channel> doInBackground() {
//get response
//pass to load channels
}
protected void onPostExecute() {
System.out.println(listChannels.get(1).getArtist()); //presumably the artist name
}
}
i try this for just 1 time create retrofit but i have error
i want call my retrofit class and give endPoint of url and body class , and get body from server clearly
ApiClient
public class ApiClient {
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(App.SERVER)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
ApiService
public interface ApiService {
#POST("{urlEndPoint}")
<C, T> Call<C> request(#Body T body, #Path("urlEndPoint") String urlEndPoint);
}
Retrofit Object
public class Request<C,T> {
private C c = null;
public C rest(T body, String urlEndPoint) {
ApiService apiService = ApiClient.getClient().create(ApiService.class);
Call<C> call = apiService.request(body, urlEndPoint);
call.enqueue(new Callback<C>() {
#Override
public void onResponse(Call<C> call, Response<C> response) {
if (response.isSuccessful())
c = response.body();
else
Toaster.shorter(App.context.getString(R.string.serverError));
#Override
public void onFailure(Call<C> call, Throwable t) {
Toaster.shorter(App.context.getString(R.string.connectionError));
}
});
return c;
}
}
calling method:
private void requestForCode() {
Request request = new Request();
int i = (int) request.rest(App.car, "/Rest/ReturnActivationCode");
if (i == 0)
Toaster.longer(App.context.getString(R.string.validateYourNumber));
else
Toaster.shorter(App.context.getString(R.string.serverError));
}
error:
12-05 12:18:04.119 773-907/? E/ConnectivityService: RemoteException caught trying to send a callback msg for NetworkRequest [ id=535, legacyType=-1, [ Capabilities: INTERNET&NOT_RESTRICTED&TRUSTED] ]
12-05 12:18:09.575 10359-10359/com.rayanandisheh.peysepar E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.rayanandisheh.peysepar, PID: 10359
java.lang.IllegalArgumentException: Method return type must not include a type variable or wildcard: retrofit2.Call<C>
for method ApiService.request
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:755)
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:746)
at retrofit2.ServiceMethod$Builder.createCallAdapter(ServiceMethod.java:229)
at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:165)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
at java.lang.reflect.Proxy.invoke(Proxy.java:393)
at $Proxy0.request(Unknown Source)
retrofit don't support generic objects???
It seems that you're trying to minimize your boilerplate by having a generic function to be called, but there's a better way to do this.
First, you're encapsulating the retrofit setup with your:
#POST("{urlEndPoint}")
<C, T> Call<C> request(#Body T body, #Path("urlEndPoint") String urlEndPoint);
And then you're calling it with the function you created:
request.rest(App.object1, "endpoint");
But actually, this will just make things complicated and the code is very tightly coupled. You will still need to call the same method on every different APIs (request.rest(App.object2, "endpoint2"), request.rest(App.object3, "endpoint3")). This also limits the capability of retrofit (such as multiple params, customize headers, etc). What you can do is just follow the setup of retrofit:
#POST("yourendpoint")
Call<YourObjectResp> saveObject(#Body YourObjectParam param)
And to minimize your boilerplate, I suggest to make it functional:
Call<YourObjectResp> call = apiService.saveObject(new YourObjectParam());
call.enqueue(new ApiServiceOperator<>(new
ApiServiceOperator.OnResponseListener<YourObjectResp>() {
#Override
public void onSuccess(YourObjectResp body) {
// do something with your response object
}
#Override
public void onFailure(Throwable t) {
// here, you can create another java class to handle the exceptions
}
}));
And for your ApiServiceOperator.java:
/**
* Handles retrofit framework response.
* Extract the body if success, otherwise throw an exception.
*/
public class ApiServiceOperator<T> implements Callback<T> {
interface OnResponseListener<T> {
void onSuccess(T body);
void onFailure(Throwable t);
}
private OnResponseListener<T> onResponseListener;
public ApiServiceOperator(OnResponseListener<T> onResponseListener) {
this.onResponseListener = onResponseListener;
}
#Override
public void onResponse(#NonNull Call<T> call, #NonNull Response<T> response) {
if (response.isSuccessful()) { // here, do the extraction of body
onResponseListener.onSuccess(response.body());
} else {
onResponseListener.onFailure(new ServerErrorException());
}
}
#Override
public void onFailure(#NonNull Call<T> call, #NonNull Throwable t) {
onResponseListener.onFailure(new ConnectionErrorException());
}
// these exception can be on a separate classes.
public static class ServerErrorException extends Exception {
}
public static class ConnectionErrorException extends Exception {
}
}
With these setup, you still minimize your boilerplate and also, it makes thing reusable, scalable, and testable. ApiServiceOperator also is loosely couple with Android Context and instead, throws a plain java exception, in which, you can create a function that knows Android Context to get the appropriate message base on the exception thrown.
I am using from rx for connect to service with retrofit, bellow is RetrofitApi.java :
public class RetrofitApi {
private static PublicApi retrofit = null;
public static PublicApi getClient(String url) {
retrofit = new Retrofit.Builder()
.baseUrl(url)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build().create(PublicApi.class);
return retrofit;
}
}
And here is PublicApi.java :
public interface PublicApi {
#GET("/web_service/mobile/rest")
Observable<LastNews> lastNews(#Query("function") String function);
}
Bellow I am connecting to my service :
#Override
public void fetchLastNewsStartPage(RemoteDataSource.ResultListener<List<LastNews>> resultListener) {
PublicApi publicApi = RetrofitApi.getClient("https://xxx.xxx.xxx/web_service/");
CompositeDisposable mCompositeDisposable = new CompositeDisposable();
mCompositeDisposable.add(publicApi.lastNews("getLastNews")
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse, this::handleError));
}
My problem is here, how can I send parameter to handleResponse and handleError. I need to send this RemoteDataSource.ResultListener<List<LastNews>> resultListener to handleResponse and handleError:
private void handleResponse(LastNews lastNewses) {
}
private void handleError(Throwable error) {
}
Just don't use method reference as it can only accept one parameter. You can achieve the result with a lambda expression. Instead of
this::handleResponse
write
lastNews -> handleResponse(lastNews, resultListener)
I am trying to use Retrofit and RxJava to make an API call within a custom view in an app that I am working on, but I encounter an incompatible type error when trying to subscribe to the Observable from my Retrofit API call.
My retrofit interface:
public interface ApiQueryInterface{
// Request method and URL specified in the annotation
// Callback for the parsed response is the last parameter
#GET("users/")
Observable<Users> getUsers (
#Query("key") String key,
#Query("address") String address
);
#GET("posts/")
Observable<Posts> getPosts (
#Query("key") String key,
#Query("address") String address
);
}
and the Retrofit call located within the onFinishInflate() of the custom view:
// Create RxJava adapter for synchronous call
RxJava2CallAdapterFactory rxAdapter = RxJava2CallAdapterFactory.create();
// Create Retrofit2 instance for API call
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(rxAdapter)
.build();
// Make API call using retrofit
final ApiQueryInterface apiQueryInterface = retrofit.create(ApiQueryInterface.class);
// API return type defined by interface
Observable<Users> query = apiQueryInterface
.getUsers(KEY, ADDRESS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Users>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Users users) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
}
When I build the project I hit an incompatible types error in the custom view on the line beginning with Observable<Users> query = ...:
Error:(60, 27) error: incompatible types: void cannot be converted to Observable<Users>
"Users" is a generic model class which matches the JSON object returned from the API
RxJava 1 returns a Subscription object not an Observable. RxJava 2 subscription returns void. That's why you are getting Error:(60, 27) error: incompatible types. You are getting the Disposable in the callback onSubscribe. If you need a reference to it, you can assign it to a class level member when the callback is invoked
Change returned object to Subscription
private Subscription subscription;
....
subscription = ApiClient.getInstance()
.getUsers(KEY, ADDRESS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Users>>() {
#Override public void onCompleted() {
}
#Override public void onError(Throwable e) {
}
#Override public void onNext(List<Users> users) {
}
});
apiclient
public class ApiClient {
private static ApiClient instance;
private ApiQueryInterface apiqueryinterface;
private ApiClient() {
final Gson gson =
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
final Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
apiqueryinterface = retrofit.create(ApiQueryInterface.class);
}
public static ApiClient getInstance() {
if (instance == null) {
instance = new ApiClient();
}
return instance;
}
public Observable<List<Users>> getUsers(#NonNull String key, #NonNull String address) {
return apiqueryinterface.getUsers(key, address);
}
}
interface
public interface ApiQueryInterface{
// Request method and URL specified in the annotation
// Callback for the parsed response is the last parameter
#GET("users")
Observable<<List<Users>> getUsers (
#Query("key") String key,
#Query("address") String address
);
I'm new to retrofit and i am trying te get a json response to an object called RootObject. The error that i am stuck with is :
"Error:(21, 44) error: incompatible types: NewsController cannot be
converted to Callback>"
Does someone now my mistake here? thanks in regards!
public class NewsController {
public void getNews(){
Retrofit retrofit = new Retrofit.Builder().baseUrl("apilink").addConverterFactory(GsonConverterFactory.create()).build();
GetNewsService service = retrofit.create(GetNewsService.class);
try {
service.GetNewsItems().enqueue(this); //asynchronous
Response<List<RootObject>> response = service.GetNewsItems().execute(); //synchronous
}
catch (IOException e){
}
}
}
class to put the data:
public class RootObject implements Serializable {
public ArrayList<Result> results ;
public int nextId;
public ArrayList<Result> getResults() { return results; }
public int getNextId() { return nextId; }
public String toString() {
return String.format("JEEJ" + nextId);
}
}
Interface:
public interface GetNewsService {
#GET("/Articles")
Call<List<RootObject>> GetNewsItems();
}
First of all,
change your interface to this:
public interface GetNewsService {
#GET("/Articles")
void GetNewsItems(Callback<List<RootObject>> cb);
}
Also change your newsController class.
public class NewsController {
private RestAdapter restAdapter;
static final String API_URL = "[Enter your API base url here]";
public void getNews(){
OkHttpClient mOkHttpClient = new OkHttpClient();
mOkHttpClient.setConnectTimeout(15000,TimeUnit.MILLISECONDS);
mOkHttpClient.setReadTimeout(15000,TimeUnit.MILLISECONDS);
restAdapter = new RestAdapter.Builder().setEndpoint(API_URL).setClient(new OkClient(mOkHttpClient)).setLogLevel(RestAdapter.LogLevel.FULL) .build();
GetNewsService service = restAdapter.create(GetNewsService.class);
Callback<List<RootObject> cb = new Callback<List<RootObject>>() {
#Override
public void success(List<RootObject> rootObjectList, Response response) {
//whatever you want to do with the fetched news items
}
#Override
public void failure(RetrofitError error) {
//whatever you want to do with the error
}
};
service.GetNewsItems(cb);
}
}
You'll need to add the following dependencies in your build.gradle:
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'com.google.code.gson:gson:2.3.1'
compile 'com.squareup.okhttp:okhttp:2.4.0'
#megh vidani's answer works, but he had you switch your code from Retrofit 2 to Retrofit 1. Here is how to do it in Retrofit 2. You would need to go back to your original gradle settings, etc. --
public class NewsController {
public void getNews(){
Retrofit retrofit = new Retrofit.Builder().baseUrl("apilink").addConverterFactory(GsonConverterFactory.create()).build();
GetNewsService service = retrofit.create(GetNewsService.class);
service.GetNewsItems().enqueue(new Callback<List<RootObject>>() {
#Override
public void onResponse(Response<List<RootObject>> response) {
// Handle your response
// Note HTTP errors are delivered here, you can check
// response.isSuccess() or response.code() to determine
// HTTP failures
}
#Override
public void onFailure(Throwable t) {
// Network errors
}
});
}
}