I am fetching data using api for that I am using Retrofit2 and RxJava2 data is fetching successfully but I don't want to show whole items.I just want to show 5 items in a list.
Below is my code:
Retrofit retrofit = RetrofitClient.getInstance();
ApiService myApi = retrofit.create(ApiService.class);
myApi.getHindiNews(data).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<HomeHindiModel>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<HomeHindiModel> homeHindiModels) {
if(homeHindiModels.size() > 0){
homeHindiList.addAll(homeHindiModels);
homeHindiAdapter = new HomeHindiAdapter(homeHindiList,getActivity());
hindiRecycler.setAdapter(homeHindiAdapter);
}
}
#Override
public void onError(Throwable e) {
Toast.makeText(getActivity(),e.getMessage(),Toast.LENGTH_SHORT).show();
}
#Override
public void onComplete() {
}
});
Someone please let me know how can I get desired output.Any help would be appreciated.
THANKS
When you get data from remote resources then you can push only 5 items into your data source which you are going to send an adapter.
Retrofit retrofit = RetrofitClient.getInstance();
ApiService myApi = retrofit.create(ApiService.class);
myApi.getHindiNews(data).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<HomeHindiModel>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<HomeHindiModel> homeHindiModels) {
if(homeHindiModels.size() > 0){
homeHindiList.addAll(homeHindiModels.subList(0, 5));
// homeHindiList.addAll(homeHindiModels);
homeHindiAdapter = new HomeHindiAdapter(homeHindiList,getActivity());
hindiRecycler.setAdapter(homeHindiAdapter);
}
}
#Override
public void onError(Throwable e) {
Toast.makeText(getActivity(),e.getMessage(),Toast.LENGTH_SHORT).show();
}
#Override
public void onComplete() {
}
})
into RecyclerView Adapter class
#Override
public int getItemCount() {
if(homeHindiList != null) {
if(homeHindiList.size() > 5) {
return 5;
} else
return homeHindiList.size();
} else
return 0;
}
Instead of adding all the items from homeHindiModels list, you can use the sublist method of arraylist to add only the required items. Here first param indicates the starting index from where the sublist would create and the second param indicates the number of items.
homeHindiList.addAll(homeHindiModels.subList(0, 5));
Related
I am getting duplicate items from an API in my adapter but I want to remove those duplicates from my side means from my adapter and print it once any idea how? Thanks in advance.
got: duplicate rows in my cardview but through an API.
want: just want to print it once and remove those duplicates.
MyAdapter:
#Override
public void onBindViewHolder(#NonNull catView holder, int position) {
holder.id.setText(oilResponses.get(position).getId());
}
#Override
public int getItemCount() {
if (oilResponses.size() != 0) {
return oilResponses.size();
}
return 0;
}
MainActivity:
private void oilList() {
Api.getClient().do_oil(CAR_ID)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<List<OilResponse>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(#NotNull List<OilResponse> responses) {
oilAdapter = new OilAdapter(responses);
oilRecycler.setAdapter(oilAdapter);
}
#Override
public void onError(#NotNull Throwable e) {
}
#Override
public void onComplete() {
}
});
}
you can try to save them in a Set instead of a List, it is basically the same thing but in a set each object can be saved one time, so you can't have duplicates even if you'd like to.
I have 2 Observables that do 2 different jobs that returns their observables
First one : SyncDoctors for getting doctor list from my WebService
public Observable<List<Doctor>> SyncDoctors(){
Observable<List<Doctor>> observable = MyWebService.getInterface().GetAllDoctors();
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.computation())
.subscribe(new Subscriber<List<Doctor>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Doctor> doctors) {
if(doctors.size() == 0){
logger.debug("No Coming Doctors");
return;
}
DoctorDao doctorDao = MyApplication.getDaoSession().getDoctorDao();
doctorDao.deleteAll();
doctorDao.insertInTx(doctors);
logger.debug("Doctors are synced successfully to the database");
logger.info(doctors.size()+" doctors have been added to database");
}
});
return observable;
}
Second Observable for getting patients list from my webservice
public Observable<List<Patients>> SyncPatients(){
Observable<List<Patients>> observable = MyWebService.getInterface().GetAllPatients();
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.computation())
.subscribe(new Subscriber<List<Patients>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Patients> patients) {
if(patients.size() == 0){
logger.debug("No Coming Patients");
return;
}
PatientDao PatientDao = MyApplication.getDaoSession().getPatientDao();
patientDao.deleteAll();
PatientDao.insertInTx(Patients);
logger.debug("Patients are synced successfully to the database");
logger.info(Patients.size()+" Patients have been added to database");
}
});
return observable;
}
Now i want to sync both doctors and patients lists and after both syncs finish, i want to show it on the screen of the tablet:
I have function called SyncAll
public void SyncAll(){
Observable<List<Doctor>> doctorsObservable = SyncDoctors();
Observable<List<Patient>> patientsObservable = SyncPatients();
Observable.concat(doctorsObservable, patientsObservable)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.computation())
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
// Here the code to show on ListView
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object o) {
logger.debug("On SyncAll Next!!!");
}
});
}
onNext functions I save the list of doctors and list of patients to the database.
now when I call SyncDoctors() alone, it works
also when I call SyncPatients() alone, it works as well.
when I call SyncAll() the Doctors and Patients are not being saved to the database.
The Question is why the SyncDoctors() and SyncPatients() observables' onNext functions are called when I call SyncAll() !!
It is because you activate chain by calling .subscribe() in
Observable<List<Doctor>> doctorsObservable = SyncDoctors();
Observable<List<Patient>> patientsObservable = SyncPatients();
You first create observable, then subscribe to it of SyncDoctors() and SyncPatients();
After that you return this Observable, but web response is triggered upon observable creation.
To solve that use .map():
public Observable<List<Doctor>> SyncDoctors(){
final Observable<List<Doctor>> observable = MyWebService.getInterface().GetAllDoctors();
observable.observeOn(Schedulers.io())
// in your code you performed db io on main thread, here it is fixed
.subscribeOn(Schedulers.io())
.map(new Func1<List<Doctor>, List<Doctor>>() {
#Override
public List<Doctor> call(List<Doctor> doctors) {
if(doctors.size() == 0){
logger.debug("No Coming Doctors");
return;
}
DoctorDao doctorDao = MyApplication.getDaoSession().getDoctorDao();
doctorDao.deleteAll();
doctorDao.insertInTx(doctors);
logger.debug("Doctors are synced successfully to the database");
logger.info(doctors.size()+" doctors have been added to database");
return doctors;
}
})
.observeOn(AndroidSchedulers.mainThread());
// Notice: use Observable.defer() or you'll get the same result all the tim
return Observable.defer(new Func0<Observable<List<Doctor>>>() {
#Override
public Observable<List<Doctor>> call() {
return observable;
}
});
}
You should not use .concat(), because it executes chain elements consequently. use .zip().first() intead.
There is also one issue: you perform db operations on main thread.
move chain to main thread after db update
Version with .zip:
void syncAll(){
Observable<List<Doctor>> doctorsObservable = SyncDoctors();
Observable<List<Patient>> patientsObservable = SyncPatients();
Observable.zip(doctorsObservable, patientsObservable, new Func2<List<Doctor>, List<Patient>, Boolean>() {
#Override
public Boolean call(List<Doctor> doctors, List<Patient> patients) {
return true;
}
})
.first()
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
logger.debug("On SyncAll Next!!!");
}
});
}
How can I make one retrofit 2 call after another?
I'm reading about RxJava and I'm already doing my calls using RxJava, but I havn't found a good exemple of how to use flatMaps.
Can someone explain how to do it to me?
I'm trying to make these two calls, and after they're both done, I want to start a new activity.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://api.openweathermap.org/data/2.5/")
.build();
WeatherService weatherService = retrofit.create(WeatherService.class);
final Observable<Weather> london = weatherService.getCurrent();
london.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Weather>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Weather weather) {
Log.i("WEATHER","Weather Name: " + weather.getName());
}
});
final Observable<Wind> windObservable = weatherService.getWind();
windObservable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Wind>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Wind wind) {
Log.i("WEATHER","Wind: " + wind.getSpeed().toString());
}
});
}
}
Maybe this link: https://github.com/ReactiveX/RxJava/wiki/Combining-observables will help. Checkout for zip. Eventually switchMap method may be useful in Your case.
Edit:
Maybe this example http://joluet.github.io/blog/2014/07/07/rxjava-retrofit/ will help You even more.
Edit #2: Some code
login().switchMap(new Func1<FirstResponse, Observable<SecondResponse>>() {
#Override
public Observable<SecondResponse> call(FirstResponse t) {
if (ApiUtils.isLoginValid(t)) {
return profile(t.getToken());
}
else{
return Observable.error(new CustomException());
}
}
}
}).subscribe(subscriber());
Note: profile method return type is is Observable<SecondResponse> and subscriber method type is Subscriber<? super SecondResponse>
You can either use flatMap as in the example or concatWith:
static Observable<Integer> intObservable() {
return Observable.just(1).delay(1, TimeUnit.SECONDS);
}
static Observable<String> stringObservable() {
return Observable.interval(1, TimeUnit.SECONDS).take(2).map(v -> v.toString());
}
public static void main(String[] args) {
intObservable()
.doOnNext(System.out::println)
.ignoreElements()
.cast(Object.class)
.concatWith(stringObservable()
.doOnNext(System.out::println)
.ignoreElements())
.toBlocking()
.subscribe();
}
However, you need some cast in case the two sources have different types.
You have multiple options here. One of them is to use zip:
london.zipWith(weatherService, new Func2<Weather, Wind, Something>() {
#Override
public Something call(final Weather weather, final Wind wind) {
...
return something;
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Something>() {
#Override
public void onCompleted() {
}
#Override
public void onError(final Throwable e) {
}
#Override
public void onNext(final Something something) {
}
});
So I'll try to keep this question as to-the-point as possible, but it will involve code snippets that traverse an entire codepath.
For context, I am fairly new and completely self-taught for Android dev, so please notify me of any clear misunderstandings/poor organization throughout. The main focus of the question is bug I am experiencing now, which is that, after a network request, the variable that was supposed to be set as a result of that network request is null, because the code moved forward before the network request completed.
Here is my activity method. It is supposed to populate the mFriends variable with the result of mUserPresenter.getUserList(), which is (unfortunately) null:
/**
* Grabs a list of friends, populates list with UserAdapter
*/
#Override
public void onResume(){
super.onResume();
mUserPresenter = new UserPresenter();
mFriends = mUserPresenter.getUserList();
if (mGridView.getAdapter() == null) {
UserAdapter adapter = new UserAdapter(getActivity(), mFriends);
mGridView.setAdapter(adapter);
}
else{
((UserAdapter)mGridView.getAdapter()).refill(mFriends);
}
}
Here is how I am structuring my UserPresenter method getUserList:
public List<User> getUserList()
{
ApiService.get_friends(this);
return mUserList;
}
The real magic happens in the ApiService class:
public static void get_friends(final UserPresenter userPresenter){
ApiEndpointInterface apiService = prepareService();
apiService.get_friends().
observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Action1<List<User>>()
{
#Override
public void call(List<User> users) {
userPresenter.setList(users);
}
}
);
}
My thinking was, that by calling userPresenter.setList(users) in ApiService, that would set mUserList to the response from the api request. However, instead, mUserList == null at the time that getUserList responds.
Any ideas of how I can structure this?
I have also started to learn something similar. Here, I would rather use callbacks.
In your presenter,
public void setList(List<User> users) {
yourView.setUserList(users);
}
And your activity which implements a view (MVP)
#Override
public void setUserList(List<User> users) {
((UserAdapter)mGridView.getAdapter()).refill(mFriends);
}
Also, check that retrofit is not returning null list.
I have a made a small app when I was learning about all this. It fetches user data from GitHub and shows in a list. I was also working with ORMLite and Picasso so some db stuff is there. Dagger Dependency is also used (but you can ignore that). Here's the link.
Here's how my Presenter behaves:
private DataRetrieverImpl dataRetriever;
#Override
public void getUserList(String name) {
dataRetriever.getUserList(name);
}
#Override
public void onEvent(DataRetrieverEvent event) {
UserList userList = (UserList)event.getData();
mainView.setItems(userList);
}
DataRetrieverImpl works as a module (sort of).
private DataRetriever dataRetriever;
restAdapter = new RestAdapter.Builder().setEndpoint(SERVER_END_POINT).build();
dataRetriever = restAdapter.create(DataRetriever.class);
public void getUserList(final String name) {
Log.i(TAG, "getting user list for: " + name);
Observable<UserList> observable = dataRetriever.getUserList(name);
Log.i(TAG, "subscribe to get userlist");
observable.subscribe(new Action1<UserList>() {
#Override
public void call(UserList userList) {
eventBus.post(new DataRetrieverEvent("UserList", userList));
// save to database
for (User user : userList.getItems()) {
Log.i(TAG, user.getLogin());
try {
dbHelper.create(user);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
});
}
And DataRetriever is interface for retrofit. I'm sorry for the naming confusion.
public interface DataRetriever {
#GET("/search/users")
public Observable<UserList> getUserList(#Query("q") String name);
}
Any my Activity,
#Override
public void setItems(final UserList userList) {
runOnUiThread(new Runnable() {
#Override
public void run() {
UserAdapter userAdapter = (UserAdapter)recyclerView.getAdapter();
userAdapter.setUserList(userList);
userAdapter.notifyItemRangeInserted(0, userAdapter.getItemCount());
}
});
}
i'm new in Rx programming (and I'm having a lot of fun so far ^^).
I'm trying to transform a AsyncTask call into an Rx function.
My function :
Get all the installed apps
normalize the labels
sort everything alphabetically
arrange them by group of letter (it was a Multimap(letter, list of apps)) and pass the result to an adapter to display everything.
Here is how I'm doing so far with Rx :
Observable.from(getInstalledApps(getActivity(), false))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<ResolvedActivityInfoWrapper, ResolvedActivityInfoWrapper>() {
#Override
public ResolvedActivityInfoWrapper call(ResolvedActivityInfoWrapper act) {
// Normalize labels
act.setLabel(Normalizer.normalize(act.getLabel(getPackageManager()).replace(String.valueOf((char) 160), "").trim(), Normalizer.Form.NFD).replaceAll("\\p{M}", ""));
return act;
}
})
.toList()
.subscribe(new Observer<List<ResolvedActivityInfoWrapper>>() {
List<ResolvedActivityInfoWrapper> list;
#Override
public void onCompleted() {
Observable.from(list).groupBy(new Func1<ResolvedActivityInfoWrapper, String>() {
#Override
public String call(ResolvedActivityInfoWrapper input) {
//Get groups by letter
String label = input.getLabel(getPackageManager());
if (!TextUtils.isEmpty(label)) {
String firstChar = label.substring(0, 1);
if (pattern.matcher(firstChar).matches()) {
return firstChar.toUpperCase();
}
}
return "#";
}
}).subscribe(this); // implementation below
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<ResolvedActivityInfoWrapper> list) {
Collections.sort(list, new Comparator<ActivityInfoWrapper>() {
#Override
// Sort all the apps in the list, not sure it's a good way to do it
public int compare(ActivityInfoWrapper info1, ActivityInfoWrapper info2) {
return info1.getLabel(getPackageManager()).compareToIgnoreCase(info2.getLabel(getPackageManager()));
}
});
this.list = list;
}
});
Once I groupedBy letters, on complete I subscribe with this :
#Override
public void onCompleted() {
//display the apps
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(GroupedObservable<String, ResolvedActivityInfoWrapper> input) {
//For each list of apps by letter i subscribe with an observer that will handle those apps (observer code below)
input.subscribe(new TestObserver(input.getKey()));
}
Observer :
private class TestObserver implements Observer<ResolvedActivityInfoWrapper> {
List<ResolvedActivityInfoWrapper> list;
String letter;
public TestObserver(String letter) {
list = new ArrayList<>();
this.letter = letter;
}
#Override
public void onCompleted() {
adapter.addData(letter, list);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(ResolvedActivityInfoWrapper input) {
list.add(input);
}
}
Everything works correctly excpets for one problem : the observer's onCompleted are called not in the right order. So I got all my apps, sorted by letter, but the groups are nots displayed in the right order (C first, then Y, then M etc ...).
I guess there are plenty of errors in the code, can you help me with this probleme and maybe understanding how all this works please ?
Thanks
UPDATE :
Following the advices in the commentary section (thanks people), here is what I'm trying after normalizing the labels :
Observable.from(list).groupBy(new Func1<ResolvedActivityInfoWrapper, String>() {
#Override
public String call(ResolvedActivityInfoWrapper input) {
String label = input.getLabel(getPackageManager());
if (!TextUtils.isEmpty(label)) {
String firstChar = label.substring(0, 1);
if (pattern.matcher(firstChar).matches()) {
return firstChar.toUpperCase();
}
}
return "#";
}
})
.toSortedList(new Func2<GroupedObservable<String, ResolvedActivityInfoWrapper>, GroupedObservable<String, ResolvedActivityInfoWrapper>, Integer>() {
#Override
public Integer call(GroupedObservable<String, ResolvedActivityInfoWrapper> obs1, GroupedObservable<String, ResolvedActivityInfoWrapper> obs2) {
return obs1.getKey().compareToIgnoreCase(obs2.getKey());
}
})
.subscribe(new Observer<List<GroupedObservable<String, ResolvedActivityInfoWrapper>>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<GroupedObservable<String, ResolvedActivityInfoWrapper>> input) {
String test = input.get(0).getKey();
}
});
But it never goes into the Compare function.