I'm trying to get the data I parse from onResponse method via Callback. Here is my ApiClient:
public class ApiClient implements Callback<Map<String, Channel>> {
private ChannelCallback listener;
static final String BASE_URL = "https://www.radyoodtu.com.tr/";
public void start(ChannelCallback listener) {
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();
listener.setChannels(body);
List<Channel> channels = new ArrayList<>(body.values());
for (Channel channel : body.values()) {
System.out.println(channel.getSong());
}
}
}
#Override
public void onFailure(retrofit2.Call<Map<String, Channel>> call, Throwable t) {
//TODO
}
}
and this is the class I'm trying to get the data:
public class Radio implements ChannelCallback {
private ApiClient apiClient = new ApiClient();
public Radio(){
apiClient.start(this);
}
#Override
public void setChannels(Map<String, Channel> body) {
this.apiClient.onResponse(body); // NOT WORKING
}
}
here is my Interface:
public interface ChannelCallback {
void setChannels(Map<String, Channel> body);
}
what I need to do is get the onResponse body data for Radio class I'm using right now. In Radio class I have to create a List of channel objects with that data I need but I can't even get the data so I can't even create that list. I don't know how to manipulate the data from listener at all and I don't know how can I access that listener I use in ApiClient in Radio class.
It looks like you've got a cyclic reference here. Radio calls ApiClient start, which triggers the network request, which calls Radio.setChannels, which tries to call the client again.
I think you need to resolve two things:
Avoid this cyclic reference
You pass the listener to ApiClient.start() but you never assign it to the actual value inside ApiClient. So, my guess is that you get an NPE here if you have a successful response.
//i think no need to impliments ChannelCallback if this code works
//just asign your interface and apiclient in class
public class Radio implements ChannelCallback {
private ApiClient apiClient = new ApiClient();
private ChannelCallback channelCallback;
and in inside radio()
public Radio(){
apiClient.start(this);
channelCallback = apiClient.onResponse(body).create(ChannelCallback.class);
channelCallback.setChannels(Map<String, Channel> body)
}
//and recieve callback
}
Related
I want to get an ArrayList<ItemList> from my Retrofit Callback and save it in as a ArrayList variable in my class.
In other word I need to use data from retrofitList even when I leave the onResponse method.
What is the best way to do it?
Here is my code.
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
retrofitList = new ArrayList<>();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://b98afcf5.ngrok.io/")
.addConverterFactory(GsonConverterFactory.create())
.build();
jsonPlaceHolder = retrofit.create(JsonPlaceHolder.class);
Call<List<ItemList>> call = jsonPlaceHolder.getItemList();
call.enqueue(new Callback<List<ItemList>>() {
#Override
public void onResponse(Call<List<ItemList>> call, Response<List<ItemList>> response) {
if(!response.isSuccessful()) {
return;
}
List<ItemList> listOfitems = response.body();
for(ItemList itemList : listOfitems){
retrofitList.add(new ItemList(itemList.getId(),
itemList.getName(),
itemList.getPhone(),
itemList.getIsLocated()));
}
//I WANT TO SAVE "retrofitList" INTO MY CLASS FROM HERE
}
#Override
public void onFailure(Call<List<ItemList>> call, Throwable t) {}
});
}
I recommend serialization it will make your life easier. Try this and this.
Create a method in your class that you will call from onResponse with your list. Save the list in some field variable. If you call this variable before it is filled with items from onResponse it won't have them.
I want to get notifications from a server using API. I'm using Retrofit2 to work with API.
The problem is when I'm passing parameter in POST method I'm getting "IllegalArgumentException URL does not contain the parameter."
The parameter is correct and working in iOS. I want to implement in android.
Below is the result when debugging the app.
Caused by: java.lang.IllegalArgumentException: URL
"notification/newnotification" does not contain "{userid}". (parameter #1)
I've tried changing parameters and asked the API developer too. He says parameter is correct.
This is code for API Interface:
public interface API{
#FormUrlEncoded
#POST("notification/newnotification")
Call<ResponseBody> getUserNotification(
#Path("userid") int userid
);
}
RetrofitClient.java
public class RetrofitClient {
public static final String BASE_URL = "http://danceglobe.co/dance/api/";
private static RetrofitClient mInstance;
private Retrofit retrofit;
private RetrofitClient(){
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static synchronized RetrofitClient getInstance(){
if(mInstance == null) {
mInstance = new RetrofitClient();
}
return mInstance;
}
public API getApi(){
return retrofit.create(API.class);
}
}
Calling Function from MainActivity
private void getNotification(String currentUserId) {
Call<ResponseBody> call = RetrofitClient.getInstance().getApi().getUserNotification(Integer.parseInt(currentUserId));
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(!response.isSuccessful()){
Toast.makeText(MainActivity.this,response.message(),Toast.LENGTH_SHORT).show();
}
try {
String s = response.body().string();
Toast.makeText(MainActivity.this,s,Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(MainActivity.this,t.getMessage(),Toast.LENGTH_SHORT).show();
}
});
}
I want it to respond to some data.
Please help me. I got stuck into this from past 2 days.
Caused by: java.lang.IllegalArgumentException: URL
"notification/newnotification" does not contain "{userid}". (parameter #1)
means you should add the userId to the path like
public interface API {
#POST("notification/newnotification/{userid}")
Call<ResponseBody> getUserNotification(
#Path("userid") int userid
);
}
#Path("userid") maps the variable to the placeholder {userid}, that was missing.
I got it working by making some changes in API interface.
Below is new Code for API interface
#FormUrlEncoded
#POST("notification/newnotification")
Call<ResponseBody> getUserNotification(
#Field("userid") String userid // changed line
);
So I am trying to cache my http responses into a ConcurrentHashMap. I have set up my cache class and Api client class to return Observables as follows:
public class UserCache {
private static ConcurrentHashMap<Integer, User> cache = new ConcurrentHashMap<>();
public Observable<User> get(Integer key) {
return Observable.create(observableEmitter -> {
if(cache.contains(key)) observableEmitter.onNext(cache.get(key));
observableEmitter.onComplete();
});
}
public void update(Integer key, User user) {
cache.putIfAbsent(key, user);
}
public boolean contains(Integer key) {
return cache.contains(key);
}
}
ApiClient
public class ApiClient {
private UserApi api;
private static ApiClient apiClient;
private ApiClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
api = retrofit.create(UserApi.class);
}
public Observable<User> get(int id) {
return api.getUser(id);
}
public static ApiClient getInstance() {
if(apiClient == null) apiClient = new ApiClient();
return apiClient;
}
}
And in the App class
public class App {
ApiClient apiSource = ApiClient.getInstance();
UserCache userCache = new UserCache();
public Observable<User> get(Integer key) {
return Observable.concat(userCache.get(key), apiSource.get(key))
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.doOnNext(user -> {
userCache.update(user.id, user);
})
.subscribeOn(Schedulers.io());
}
public static void main(String[] args) throws InterruptedException {
App app = new App();
app.get(1).subscribe(System.out::println);
Thread.sleep(3000);
app.get(1).subscribe(System.out::println);
Thread.sleep(1000);
}
}
Mmy understanding of .concat is that if the first observable (cache) doesn't emit anything, then the second Observable (api client) will start emitting. But I can't figure out why doOnNext(user -> userCache.update(user.id, user)) is not updating the cache, and hence when i retrieve the same key, another api call is carried out again.
I'm not sure why your doOnNext does not emit, but if you're using RX there's a way that requires less code and eliminates race conditions. In the example you give, if we call the method twice on an empty cache, it will make two network calls and the last one will overwrite the first. Here's my preferred method, which prevents this from happening and requires less code:
private final ConcurrentMap<Integer, Observable<User>> userCache = Maps.newConcurrentMap();
public Observable<User> getUser(int id) {
return userCache.computeIfAbsent(id, theId -> {
ConnectableObservable<User> cachedObservable = getUserFromApi(id)
.replay();
cachedObservable.connect();
return cachedObservable;
}).doOnError(err -> userCache.remove(id));
}
As you can see I store cached observables. That way, if a second call is made while the first is still in flight, they get the same result, and it is only cached once. All calls after that get it directly from the cached observable.
However, we don't want to cache errors (probably) so I append a doOnError which makes sure that any observables that contain an error (like a network failure) are not cached as well.
Have been having issue using Rxjava with Retrofit in my android app, everything seems ok and fine in the code implementation but app crashes whenever i navigate to the activity/fragment with the error message below.
//Error message
java.lang.IllegalArgumentException: Unable to create converter for java.util.List<com.thebestprice.bestprice.model.SearchRequestModel>
for method DataEndpointService.getStarredRepositories
//Endpoint Declaration
#GET("users/{user}/starred")
Observable<List<SearchRequestModel>> getStarredRepositories(#Path("user") String username);
//Client Class
public class DataClient {
private static final String PROJECT_BASE_URL = "https://api.github.com/";
private static DataClient instance;
private DataEndpointService queryResultService;
private DataClient(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
final Retrofit retrofit = new Retrofit.Builder().baseUrl(PROJECT_BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
queryResultService = retrofit.create(DataEndpointService.class);
}
public static DataClient getInstance(){
if (instance == null){
instance = new DataClient();
}
return instance;
}
public io.reactivex.Observable<List<SearchRequestModel>> queryForUserRepo(#NonNull String searchRequestModel){
return queryResultService.getStarredRepositories(searchRequestModel);
}
}
//Fragment to display the list
private void queryResultForSearchData(String userName){
disposable = DataClient.getInstance().
queryForUserRepo(userName).
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(new Consumer<List<SearchRequestModel>>() {
#Override
public void accept(List<SearchRequestModel> searchRequestModels) throws Exception {
mShimmerFrameLayout.stopShimmerAnimation();
rvAllSearch.setAdapter(new ResultAdapter(getActivity(), searchRequestModels));
}
},
new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
}`enter code here`
});
}
//gradle dependencies for rxjava and retrofit with compileSdk27
//For using RxJava
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
//logging interceptor
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
//For Using retrofit to do network request
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
Don't know what i'm doing wrong, have search for similar issues but doesn't seems to help in my own case
Main point of using RxJava it's avoid loops and list, to invoke Subscriptions and map functions, cause Observables managed all items. So you don't neet to return List.
Just one of example of using Github API with Rx.
public interface GithubService {
String SERVICE_ENDPOINT = "https://api.github.com";
#GET("/users/{login}")
Observable<Github> getUserRx(#Path("login") String login);
#GET("/users/{login}")
Github getUser(#Path("login") String login);
}
.
public class ServiceFactory {
/**
* Creates a retrofit service from an arbitrary class (clazz)
* #param clazz Java interface of the retrofit service
* #param endPoint REST endpoint url
* #return retrofit service with defined endpoint
*/
public static <T> T createRetrofitService(final Class<T> tClass, final String endPoint) {
final RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(endPoint)
.build();
T service = restAdapter.create(tClass);
return service;
}
}
.
// .....
service = ServiceFactory.createRetrofitService(
GithubService.class, GithubService.SERVICE_ENDPOINT);
service.getUserRx(login)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.cache()
.subscribe(new Subscriber<Github>() {
#Override
public final void onCompleted() {
}
#Override
public final void onError(Throwable e) {
Log.e(LOG, " ErrorRx Default Github" + e.getMessage());
}
#Override
public final void onNext(Github response) {
mCardAdapter.addData(response);
}
});
// .....
As you add GsonConverterFactory.
Check out the Bean you use (SearchRequestModel) the variable or the #SerializedName(alternate) annotation does not have the same variable name.
it work for me
I am trying to make a class the uses Retrofit to make some API calls.
public class RedditUtils {
private RestAdapter restAdapter;
private RedditApiService service;
private final String redditBaseUrl = "https://oauth.reddit.com";
public RedditUtils()
{
restAdapter = new RestAdapter().Builder().setEndpoint(redditBaseUrl).build();
service = restAdapter.create(RedditApiService.class);
}
public void fetchToken(String token)
{
service.getToken(token, new Callback<RedditAccessToken>() {
#Override
public void success(RedditAccessToken redditAccessToken, Response response) {
Log.d("RedditAccessToken", redditAccessToken.getAccessToken());
}
#Override
public void failure(RetrofitError error) {
Log.d("RedditAccessTokenFailed", error.toString());
}
});
}
}
When I set restAdapter equalt to new RestAdapter(), I get the error
RestAdapter(*long path name*) has private access in 'retrofit.RestAdapter'
I am not sure what would be causing this error and I haven't found anyone else with this issue. Any ideas on how to solve the error?
You aren't suppose to instantiate RestAdapter, hence why it has a private constructor.
Builder is a static internal class of the RestAdapter class. You can instantiate it like so:
new RestAdapter.Builder()