java.lang. RuntimeException No Retrofit annotation found. (parameter #3) - java

I'm trying to update this RetroFit + Otto tutorial, so my code updated is:
IWeather.java
RetroFit 2.+ doesn't allow to return void, so instead of void getWeather(...) I added Call<Weather> getWeather(...).
public interface IWeather {
#GET("/{latitude},{longitude}")
Call<Weather> getWeather(#Path("latitude") String latitude,
#Path("longitude") String longitude,
Callback<Weather> callback);
}
ForecastClient.java
RetroFit 2.+ has changed his constructor, so the new ForecastClient has the next form. The IllegalArgumentException points to the weather.getWeather(...) method.
public class ForecastClient {
private static final String BASE_URL = "https://api.darksky.net/forecast/";
private static final String API_KEY = "******************";
public static final String API_URL = BASE_URL + API_KEY + "/";
private static ForecastClient mForecastClient;
private static Retrofit mRetroAdapter;
public static ForecastClient getClient() {
if (mForecastClient == null) {
mForecastClient = new ForecastClient();
}
return mForecastClient;
}
private ForecastClient() {
mRetroAdapter = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(new OkHttpClient())
.build();
}
public void getWeather(String latitude, String longitude, Callback<Weather> callback) {
IWeather weather = mRetroAdapter.create(IWeather.class);
weather.getWeather(latitude, longitude, callback);
}
}
ForecastManager.java
public class ForecastManager {
private Context mContext;
private Bus mBus;
private ForecastClient sForecastClient;
public ForecastManager(Context context, Bus bus) {
this.mContext = context;
this.mBus = bus;
sForecastClient = ForecastClient.getClient();
}
#Subscribe
public void onGetWeatherEvent(GetWeatherEvent getWeatherEvent) {
String latitude = Double.toString(getWeatherEvent.getLatitude()).trim();
String longitude = Double.toString(getWeatherEvent.getLongitude()).trim();
Callback<Weather> callback = new Callback<Weather>() {
#Override
public void onResponse(Call<Weather> call, Response<Weather> response) {
Log.d(ForecastManager.class.getSimpleName(), response.body().toString());
mBus.post(new SendWeatherEvent(response.body()));
}
#Override
public void onFailure(Call<Weather> call, Throwable t) {
Log.e(ForecastManager.class.getSimpleName(), t.getMessage());
}
};
sForecastClient.getWeather(latitude, longitude, callback);
}
}
I'm receiving a No Retrofit annotation found and I guess the reason is something related with my callback, but it is a RetroFit callback, so I don't understand why the error.
The stacktrace points to the MainActivity, in particular to the Otto's method post() with a java.lang.RuntimeException: Could not dispatch event, caused by the error previously mentioned java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #3):
MainActivity.java
public class MainActivity extends AppCompatActivity {
#BindView(R.id.activity_main_textView)
TextView textView;
#BindView(R.id.activity_main_button)
Button button;
private static final double LATITUDE = **.******;
private static final double LONGITUDE = **.******;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
#OnClick(R.id.activity_main_button)
public void onClick(View view) {
BusProvider.getInstace().post(new GetWeatherEvent(LATITUDE, LONGITUDE));
}
#Subscribe
public void onSendWeatherEvent(SendWeatherEvent sendWeatherEvent) {
Weather weather = sendWeatherEvent.getWeather();
Currently currently = weather.getCurrently();
textView.setText(currently.getSummary());
}
Any clue about where is the error or how should I handle the bus subscriptions, the callback, etcetera?

java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #3)
As the error says, the problem is that the third parameter of the getWeather method does not have an annotation. The Callback class is used for the Call#enqueue(Callback) method.
Simply change
sForecastClient.getWeather(latitude, longitude, callback)
to
sForecastClient.getWeather(latitude, longitude).enqueue(callback)
and remove the third parameter.

Try it and modify your retrofit version number to 2.6.0. The use of coroutines in Retrofit 2.6.0 can return a Response object directly. If await() is not needed, Retrofit will automatically call it. This is not applicable when the version is below 2.6.0.

Related

No response from Retrofit2

Android newly here. I am working on a popular movies app that fetches some movie data from moved.org and display to users when tapping on movie thumbnail. Recently, I have been working on setting up retrofit in my app for two weeks. I am trying to use MVVM pattern. My goals is to get response from api.moviedb.org so that, eventually, I can display movie posters in recycler view. For some reason, I am not able to get any response from the api call using retrofit. I think I did something wrong with setting up retrofit but can't find out how I messed up... I feel like I am hard stuck.. Any help from experienced android developers would be much appreciated:)
Github link to my project
On create method in MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.rvMovies);
movieViewModel = ViewModelProviders.of(this).get(MovieViewModel.class);
// Log.d("viewmodel", String.valueOf(movieViewModel));
movieViewModel.getMoviesLiveData().observe(this, new Observer<MovieResult>() {
#Override
public void onChanged(MovieResult movieResult) {
Log.d("movieResult", String.valueOf(movieResult));
}
});
}
ViewModel Class:
public class MovieViewModel extends AndroidViewModel {
private MutableLiveData<MovieResult> movieLiveData;
private MovieRepository movieRepository;
public MovieViewModel(#NonNull Application application){
super(application);
}
public void init(){
if (movieLiveData != null){
return;
}
movieRepository = MovieRepository.getInstance();
movieLiveData = movieRepository.getMovies("api_key_here");
}
public LiveData<MovieResult> getMoviesLiveData() {
init();
return movieLiveData;
}
}
Respository:
public class MovieRepository {
private static MovieRepository movieRepository;
public static MovieRepository getInstance(){
if (movieRepository == null){
movieRepository = new MovieRepository();
}
return movieRepository;
}
private GetMovieService service;
public MovieRepository(){
service = RetrofitInstance.getRetrofitInstance().create(GetMovieService.class);
}
public MutableLiveData<MovieResult> getMovies(String api){
Log.d("getmovies","called");
final MutableLiveData<MovieResult> movieData = new MutableLiveData<>();
service.getMovieResult(api).enqueue(new Callback<MovieResult>() {
#Override
public void onResponse(Call<MovieResult> call, Response<MovieResult> response) {
if (response.isSuccessful()){
movieData.setValue(response.body());
Log.d("debug", String.valueOf(movieData));
}
}
#Override
public void onFailure(Call<MovieResult> call, Throwable t) {
movieData.setValue(null);
Log.d("onfailure","why");
}
});
return movieData;
}
}
RetrofitInstance:
public class RetrofitInstance {
private static Retrofit retrofit;
private static final String BASE_URL = "http://api.themoviedb.org/3/";
public static Retrofit getRetrofitInstance(){
if(retrofit == null){
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
GetMovieService.Java
public interface GetMovieService {
#GET("movie/popular")
Call<MovieResult> getMovieResult(#Query("api_key") String apiKey);
}
logical:
log output

why do we use a call list with retrofit

I have an example ,I want to understand some parts,In this exampl ,It was working fine ,but when I changed part
from:
call list<model>> method();
to:
call <model> method();
It caused an error ,What's the reason for that?
What is the difference between the two cases?
// MainActivity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GetData service = RetrofitClient.getRetrofitInstance().create(GetData.class);
Call<RetroUsers> call = service.getAllUsers();
call.enqueue(new Callback<RetroUsers>() {
#Override
public void onResponse(Call<RetroUsers> call, Response<RetroUsers> response) {
Log.i("print", "" + response.body().getUser());
}
#Override
public void onFailure(Call<RetroUsers> call, Throwable t) {
Log.i("print", "Dont" + t.getMessage());
}
});
}
///Error message :
I/print: Dontjava.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
// interface GetData:
public interface GetData {
#GET("/users")
Call<RetroUsers>getAllUsers();
/*
#GET("/users")
Call<List<RetroUsers>> getAllUsers();
*/
}
// RetrofitClient :
public class RetrofitClient {
private static Retrofit retrofit;
private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
// model class :
public class RetroUsers {
#SerializedName("name")
private String name;
public RetroUsers(String name) {
this.name = name;
}
public String getUser() {
return name;
}
public void setUser(String name) {
this.name = name;
}
}
This: Call<RetroUsers> getAllUsers();
will cause you an error because the getAllUsers() will return more than one records of RetroUsers. Due to which it requires you to provide it a type as List so that its datatype is set to List and it can then handle multiple records.
Go through basics of Core Java to better understand this.
In one case you tell the deserializer that you expect a single Model, in the other case you tell it to expect a list of models, aka a List<Model>. Depending on what data you actually get you need to use one or the oter.
Of course you can "hide" the List<...> within your model by not using List<Model> but:
public class MoreThanOneModel {
public List<Model> entries;
...
}
But that does not change the underlying reasoning.

Android Callback method not fills the list

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
}
}

java.lang.IllegalArgumentException: FormUrlEncoded can only be specified on HTTP methods with request body (e.g., #POST)

I am trying to get data from database by GET method on API
Here is my coding
APIServive.Interface
public interface APIService {
#FormUrlEncoded
#GET("Event")
Call<ApiResponseModel> viewEvent();
}
EventModel.Java
public class EventModel {
#SerializedName("nama_event") String nama_event;
#SerializedName("jenis_event") String jenis_event;
#SerializedName("creator") String creator;
#SerializedName("deskripsi_event") String deskripsi_event;
#SerializedName("tanggal") String tanggal;
#SerializedName("status") String status;
public String getNama_event() {
return nama_event;
}
public String getJenis_event() {
return jenis_event;
}
public String getCreator() {
return creator;
}
public String getDesk_event() {
return deskripsi_event;
}
public String getTanggal_event() {
return tanggal;
}
public String getStatus() {
return status;
}
}
ViewActivity.Java
public class ViewEventActivity extends AppCompatActivity {
#OnClick(R.id.back_arrow)void balik(){
finish();
}
#BindView(R.id.search_acara)EditText searchEvent;
public static final String URL = "http://iseoo.id/rest_ci_iseoo/";
private List<EventModel> acara = new ArrayList<>();
RecyclerView.LayoutManager mlayoutManager;
private RecyclerViewAdapter viewAdapter;
#BindView(R.id.recyclerViewEvent)RecyclerView recyclerView;
#BindView(R.id.progress_bar)ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_event);
ButterKnife.bind(this);
viewAdapter = new RecyclerViewAdapter(ViewEventActivity.this, acara);
mlayoutManager = new LinearLayoutManager(this,
LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(mlayoutManager);
recyclerView.setAdapter(viewAdapter);
Retrofit retrofit=new
Retrofit.Builder().baseUrl(URL).
addConverterFactory(GsonConverterFactory.create()).build();
APIService API = retrofit.create(APIService.class);
Call<ApiResponseModel> getData = API.viewEvent();
getData.enqueue(new Callback<ApiResponseModel>() {
#Override
public void onResponse(Call<ApiResponseModel> call,
Response<ApiResponseModel> response) {
progressBar.setVisibility(View.GONE);
acara = response.body().getResult();
viewAdapter = new
RecyclerViewAdapter(ViewEventActivity.this, acara);
recyclerView.setAdapter(viewAdapter);
viewAdapter.notifyDataSetChanged();
}
// }
#Override
public void onFailure(Call<ApiResponseModel> call, Throwable t) {
}
});}}
And When i run the application it gives this
Caused by: java.lang.IllegalArgumentException: FormUrlEncoded can only be
specified on HTTP methods with request body (e.g., #POST).
for method APIService.viewEvent
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:752)
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:743)
at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:185)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
at java.lang.reflect.Proxy.invoke(Proxy.java:397)
at $Proxy0.viewEvent(Unknown Source)
at
com.example.lenovog480.iseooalpha.ViewEventActivity.onCreate
(ViewEventActivity.java:61)
at android.app.Activity.performCreate(Activity.java:6127)
at android.app.Instrumentation.callActivityOnCreate
(Instrumentation.java:1128)
at android.app.ActivityThread.performLaunchActivity
(ActivityThread.java:2630)
I've searched for this problems and i've tried to solve it, but until now i didnt got the right answers for this problem, if possible you can contact me cause i really need help
Please Anybody help me and save my life thanks :')
If you add #FromUrlEncoded to the top of #GET, you will have java.lang.IllegalArgumentException: FormUrlEncoded can only be
specified on HTTP methods with request body (e.g., #POST).
Remove #FormUrlEncoded in your code .
public interface APIService {
#GET("Event")
Call<ApiResponseModel> viewEvent();
}
If you replace #POST request with #GET, change also parameters and remove #FormUrlEncoded:
#GET(/some/request/)
suspend fun getData(
#Query("name") name: String?,
#Query("age") age: Int?
): SomeResponse

Retrofit 2: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

I am use Retrofit 2. I use test JSON on http://ip.jsontest.com/.It is very simple JSON. Why I am take this error?
In real project i have this ERROR too, but i think, it is because I have very big JSON. And I thy use test JSON. Need HELP))
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
This is JSON
{
"ip": "54.196.188.78"
}
My Interface
public interface UmoriliApi {
#GET(".")
Call<List<Test>> getData();
}
My Test class
public class Test {
#SerializedName("ip")
#Expose
private String ip;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
}
My API class
public class App extends Application {
private static UmoriliApi umoriliApi;
private Retrofit retrofit;
#Override
public void onCreate() {
super.onCreate();
retrofit = new Retrofit.Builder()
.baseUrl("http://ip.jsontest.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
umoriliApi = retrofit.create(UmoriliApi.class);
}
public static UmoriliApi getApi() {
return umoriliApi;
}
}
My MainActivity class
public class MainActivity extends AppCompatActivity {
private static final String TAG = "TAG";
List<Test> posts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
posts = new ArrayList<>();
App.getApi().getData().enqueue(new Callback<List<Test>>() {
#Override
public void onResponse(Call<List<Test>> call, Response<List<Test>> response) {
posts.addAll(response.body());
Log.d(TAG, "onResponse: "+posts.size());
}
#Override
public void onFailure(Call<List<Test>> call, Throwable t) {
Log.d(TAG, "onFailure: ");
}
});
}
}
Basically you are expecting and Array but you received a JSON Object.
As Akash said in the comment:
Call<List<Test>> getData();
List<Test> is what you write when you expect and Array. You need to write Call<Test> for an object Test
You will also have to change the callback.

Categories