I want to use RxAndroid in my project,
and i make the thread sleep for 50ms
but it caused anr,the code
public void getTypeAndCommodity() {
Observable.from(getCommodities())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Commodity>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Commodity commodity) {
}
});
}
and the getCommodities:
private ArrayList<Commodity> getCommodities() {
// some test info
ArrayList<Commodity> list = new ArrayList<>();
for (int i = 0; i < 99; i++) {
Commodity commodity = new Commodity();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
commodity.setName("name" + i);
commodity.setType("type" + (i + 1) / 10);
list.add(commodity);
}
return list;
}
why it cause anr?please help
This happens because getCommodities() is executed in main thread, and only the item emited is executed in io thread with subscribeOn(Schedulers.io()). If you want to execute getCommidities() in background thread too, you need to create an observable with defer() method:
Observable.defer(new Func0<Observable<Object>>() {
#Override public Observable<Object> call() {
return Observable.from(getCommodities());
}
}).subscribeOn(Schedulers.io())...
If you need more info: http://blog.danlew.net/2015/07/23/deferring-observable-code-until-subscription-in-rxjava/
Related
In my todoApp I've implemented MediatorLiveData for learning purpose in the following manner:
private val todoListMediator = MediatorLiveData<NetworkResult<List<TodoEntity>>>()
private var todoSource: LiveData<NetworkResult<List<TodoEntity>>> =
MutableLiveData()
val todoResponse: LiveData<NetworkResult<List<TodoEntity>>> get() = todoListMediator
viewModelScope.launch(dispatcherProvider.main) {
todoSource =
todoRepository.getTodoList()
todoListMediator.addSource(todoSource) {
todoListMediator.value = it
}
}
The above code works fine. Now I wanna make following changes and I don't have clear picture how can I achieve them.
As soon as todoListMediator.addSource() observes the todoList:
1] I want to iterate over that original Todo list and make a network call for each element and add some more field to them.
2] I wanna save the new todo list(where each Todo now has some extra field we received by network call in step 1)
3] and finally, I want to assign that new todo list(with extra field) to the todoListMediator.
// sudo to illustrate the above scenario
viewModelScope.launch(dispatcherProvider.main) {
//step.1 get original todo list
todoListMediator.addSource(todoSource) { it ->
// step 2. iterate over original todo list from step 1 and make network call to get extra field for each element and update the original list
//for example
val newFieldForTodoElement = NetworkResult<PendingTodoValues?> = todoRepository.getPendingTodoValues()
// step 3. one all the original list is updated with new field values, save the Todo list in local db
// step 4. Then pass the todo list with new fields to mediator live data from db
todoListMediator.value = it
}
}
Any tricks with detailed explanation on code will be a great help for my learning. Thanks!
you can use RXjava with Flatmap operator
something like that may help ?
getPostsObservable()
.subscribeOn(Schedulers.io())
.flatMap(new Function<Post, ObservableSource<Post>>() {
#Override
public ObservableSource<Post> apply(Post post) throws Exception {
return getCommentsObservable(post);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Post>() {
#Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
#Override
public void onNext(Post post) {
updatePost(post);
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError: ", e);
}
#Override
public void onComplete() {
}
});
}
private Observable<Post> getPostsObservable(){
return ServiceGenerator.getRequestApi()
.getPosts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Function<List<Post>, ObservableSource<Post>>() {
#Override
public ObservableSource<Post> apply(final List<Post> posts) throws Exception {
adapter.setPosts(posts);
return Observable.fromIterable(posts)
.subscribeOn(Schedulers.io());
}
});
}
private void updatePost(final Post p){
Observable
.fromIterable(adapter.getPosts())
.filter(new Predicate<Post>() {
#Override
public boolean test(Post post) throws Exception {
return post.getId() == p.getId();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Post>() {
#Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
#Override
public void onNext(Post post) {
Log.d(TAG, "onNext: updating post: " + post.getId() + ", thread: " + Thread.currentThread().getName());
adapter.updatePost(post);
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError: ", e);
}
#Override
public void onComplete() {
}
});
}
private Observable<Post> getCommentsObservable(final Post post){
return ServiceGenerator.getRequestApi()
.getComments(post.getId())
.map(new Function<List<Comment>, Post>() {
#Override
public Post apply(List<Comment> comments) throws Exception {
int delay = ((new Random()).nextInt(5) + 1) * 1000; // sleep thread for x ms
Thread.sleep(delay);
Log.d(TAG, "apply: sleeping thread " + Thread.currentThread().getName() + " for " + String.valueOf(delay)+ "ms");
post.setComments(comments);
return post;
}
})
.subscribeOn(Schedulers.io());
I'm new in RxJava. I have currently executed three API calls parallel which is independent of each other via Retrofit using Single.Zip Operator. On getting a successful response of all three API calls, I have to insert the data from all three APIs into Room database into Different entities which takes 20 seconds.
So I need to execute database operations inside Single.Zip operator. Because the logic is written inside onSuccess method running away before Database Operation performed.
I have tried to take separate Observer for performing database operation but didn't work.
public void callOfflineDataAPIs() {
setIsLoading(true);
Single<BaseResponse<ProductResponse>> single1 = getDataManager().getOfflineProductListApiCall(getDataManager().getLastTimeStampOfflineProductCall()).subscribeOn(getSchedulerProvider().io()).observeOn(getSchedulerProvider().ui());
Single<BaseResponse<LocationResponse>> single2 = getDataManager().getOfflineLocationListApiCall(getDataManager().getLastTimeStampOfflineLocationCall()).subscribeOn(getSchedulerProvider().io()).observeOn(getSchedulerProvider().ui());
Single<BaseResponse<OfflineMasterData>> single3 = getDataManager().getOfflineMasterDataListApiCall(getDataManager().getLastTimeStampOfflineMasterCall()).subscribeOn(getSchedulerProvider().io()).observeOn(getSchedulerProvider().ui());
DisposableSingleObserver<List<Boolean>> result = Single.zip(single3, single1, single2,
(offlineMasterDataBaseResponse, productResponseBaseResponse, locationResponseBaseResponse) -> {
List<Boolean> apiCalls = new ArrayList<>();
apiCalls.add(masterDataCRUDOperation(offlineMasterDataBaseResponse));
apiCalls.add(productDataCRUDOperation(productResponseBaseResponse));
apiCalls.add(locationDataCRUDOperation(locationResponseBaseResponse));
return apiCalls;
}).subscribeOn(getSchedulerProvider().io()).observeOn(getSchedulerProvider().ui()).subscribeWith(new DisposableSingleObserver<List<Boolean>>() {
#Override
public void onSuccess(List<Boolean> apiCalls) {
setIsLoading(false);
LogHelper.e(TAG, "DisposableSingleObserver- onSuccess");
boolean isSync = true;
for (int i = 0; i < apiCalls.size(); i++) {
if (!apiCalls.get(i)) {
isSync = false;
LogHelper.e(TAG, "DisposableSingleObserver- onSuccess- apiCalls.get(i)", i);
callOfflineDataAPIs();
break;
}
}
if (isSync) {
LogHelper.e(TAG, "IF-isSync");
if (BuildConfig.IS_CLIENT_BUILD) {
LogHelper.e(TAG, "IF-isSync-IS_CLIENT_BUILD-true");
getDataManager().setCurrentWarehouseKey(1);
getNavigator().onGoButtonClick();
} else {
LogHelper.e(TAG, "ELSE-isSync-IS_CLIENT_BUILD-false");
getWarehouseList();
}
}
}
#Override
public void onError(Throwable e) {
LogHelper.e(TAG, "DisposableSingleObserver- Throwable");
setIsLoading(false);
String errorMessage = new NetworkError(e).getAppErrorMessage();
getNavigator().exitApplicationOnError(errorMessage);
}
});
}
Logic written inside onSuccess Method execute once all DB Operation performed.
You can modify your code to something like:
DisposableSingleObserver<List<Boolean>> result = Single.zip(single3, single1, single2,
(offlineMasterDataBaseResponse, productResponseBaseResponse, locationResponseBaseResponse) -> {
List<Boolean> apiCalls = new ArrayList<>();
apiCalls.add(masterDataCRUDOperation(offlineMasterDataBaseResponse));
apiCalls.add(productDataCRUDOperation(productResponseBaseResponse));
apiCalls.add(locationDataCRUDOperation(locationResponseBaseResponse));
return apiCalls;
}).subscribeOn(getSchedulerProvider().io())
.map(new Function<List<Boolean> apiCalls, List<Boolean> apiCalls>() {
#Override
public List<Boolean> apiCalls apply(List<Boolean> apiCalls) throws Exception {
// perform database operations here
return apiCalls;
}
})
.observeOn(getSchedulerProvider().ui())
.subscribe(new Observer<List<Boolean>>() {
#Override
public void onNext(User user) {
// Do something
}
#Override
public void onError(Throwable e) {
// Do something
}
#Override
public void onComplete() {
// Do something
}
});
I am very new to RxJava and have a problem. I try to chain two Completables. So the scenario is to update some Category Objects and afterwards update the UI. Both tasks will be executed within a Completable. I chained those two with the andThen operator but sometimes the UI Completable does not get executed and i dont get any error.
Disposable d = FilterController.getInstance().updateChosenCategoriesCompletable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.andThen(FilterController.getInstance().updateChosenCategoriesInterfaceCompletable())
.subscribeWith(new DisposableCompletableObserver() {
#Override
public void onComplete() {
Log.d("MultiselectDialog", "Categories Update Success");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
ExceptionHandler.saveException(e, null, null);
Log.d("MultiselectDialog", "error");
}
});
I am not sure but i think this is caused by executing the first Completable on the IO Thread. Is this forbidden when i try to chain two Completables? The weird thing about it is that the second Completable does not get execute sometimes (1 out of 10 Times for example). When i insert a delay or do everything on the Main Thread then everything works. I just want to know if its forbidden to chain two Completables with two different threads.
Thanks for your help in advance!
/edit
The updateChosenCategoriesCompletable() is using this Completable:
public Completable setChosenCategoryCompletable(final ArrayList<CategoryHolder> category, final boolean callFilterOptionsChanged) {
return Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
try {
Log.d("Workflow", "setChosenCategoryCompletable " + category.size());
Log.d("Workflow", "setChosenCategoryCompletable " + Thread.currentThread().getName());
mChosenCategories = category;
if (callFilterOptionsChanged) {
mFilterOptionsChangedListener.filterOptionsChanged();
}
} catch (Exception e){
Log.d("Workflow", "error set");
e.printStackTrace();
}
}
});
}
and the updateChosenCategoriesCompletable() is using this Completable
public Completable updateCategoryLabelTextCompletable(final ArrayList<CategoryHolder> categories) {
return Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
Log.d("Workflow", "updateCategoryLabelTextCompletable " + categories.size());
Log.d("Workflow", "updateCategoryLabelTextCompletable " + Thread.currentThread().getName());
TextView selectedValue = mLabels.get(mActivity.getString(R.string.CATEGORY));
ImageView categoryIcon = (ImageView) mRootView.findViewById(R.id.category_icon);
if(categories.size() > 1) {
selectedValue.setText(String.valueOf(categories.size()));
categoryIcon.setVisibility(View.GONE);
}
else if(categories.isEmpty()) {
selectedValue.setText(mActivity.getString(R.string.CATEGORY_EMPTY));
categoryIcon.setVisibility(View.GONE);
}
else {
selectedValue.setText(categories.get(0).getCategory());
// Set category image if only one category is selected
categoryIcon.setVisibility(View.VISIBLE);
String categoryImage = categories.get(0).getCategoryImage();
if(!categoryImage.isEmpty()){
int imageResource = mActivity.getResources().getIdentifier(categoryImage, "drawable", mActivity.getPackageName());
Drawable image = null;
// getDrawable(int) is deprecated since API 21
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
image = mActivity.getResources().getDrawable(imageResource, null);
} else {
image = mActivity.getResources().getDrawable(imageResource);
}
}catch (Exception e){
e.printStackTrace();
}
if(image != null){
categoryIcon.setImageDrawable(image);
}
}else {
categoryIcon.setVisibility(View.GONE);
}
}
}
});
}
Is it mandatory to use fromCallable instead?
This is the method that updates the button i mentioned:
public void setResultButtonText(final String text, final boolean buttonEnabled) {
if(mShowResultsButton != null) {
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
mShowResultsButton.setText(text);
mShowResultsButton.setEnabled(buttonEnabled);
if (buttonEnabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mShowResultsButton.setBackground(mActivity.getDrawable(R.drawable.ripple));
} else {
mShowResultsButton.setBackground(mActivity.getResources().getDrawable(R.drawable.ripple));
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mShowResultsButton.setBackground(mActivity.getDrawable(R.drawable.ripple_grey));
} else {
mShowResultsButton.setBackground(mActivity.getResources().getDrawable(R.drawable.ripple_grey));
}
}
} catch (Exception e){
e.printStackTrace();
Log.d("Filterinterface", "ERROR");
}
}
});
}
}
mSubscription = RxSearchView.queryTextChangeEvents(mSearchView)
.subscribeOn(AndroidSchedulers.mainThread())
.debounce(600, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.filter(new Func1<SearchViewQueryTextEvent, Boolean>() {
#Override
public Boolean call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
String query = searchViewQueryTextEvent.queryText().toString();
if (!TextUtils.isEmpty(query) && query.length() >= 11) {
if (!CommonUtil.isMobileNumber(query)) {
PromptManager.getInstance().showToast("please input valid phone");
return false;
}
}
boolean b = !TextUtils.isEmpty(searchViewQueryTextEvent.queryText().toString());
return b;
}
})
.switchMap(new Func1<SearchViewQueryTextEvent, Observable<BaseResponseWrapper<SearchUserResponse>>>() {
#Override
public Observable<BaseResponseWrapper<SearchUserResponse>> call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
// Why run in the main thread here
// 2016/6/12 reset api request
String res = searchViewQueryTextEvent.queryText().toString();
return RetrofitManager.newInstance().getApi().searchUserByPhone(res);
}
})
// switch io thread
.subscribeOn(Schedulers.io())
.map(new Func1<BaseResponseWrapper<SearchUserResponse>, List<SearchUserResponse>>() {
#Override
public List<SearchUserResponse> call(BaseResponseWrapper<SearchUserResponse> fuzzyUserRes) {
// some code here
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<SearchUserResponse>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
LogUtils.w("end thread:" + Thread.currentThread().getName());
LogUtils.w("e :" + e);
**//there throw exception android.os.NetworkOnMainThreadException**
}
#Override
public void onNext(List<SearchUserResponse> fuzzyUsers) {
updateUI(fuzzyUsers);
}
});
When I input to the searchview , onError method will throw android.os.NetworkOnMainThreadException.
I have been in the
After switchmap switch to the IO thread.
I use rxjava version :
'io.reactivex:rxjava:1.1.5'.
what can I do?
You can't call subscribeOn twice in the same stream. Only the first will count.
Your code should look like this:
mSubscription = RxSearchView.queryTextChangeEvents(mSearchView)
.debounce(600, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.filter(new Func1<SearchViewQueryTextEvent, Boolean>() {
#Override
public Boolean call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
String query = searchViewQueryTextEvent.queryText().toString();
if (!TextUtils.isEmpty(query) && query.length() >= 11) {
if (!CommonUtil.isMobileNumber(query)) {
PromptManager.getInstance().showToast("please input valid phone");
return false;
}
}
boolean b = !TextUtils.isEmpty(searchViewQueryTextEvent.queryText().toString());
return b;
}
})
// switch io thread
.observeOn(Schedulers.io())
.switchMap(new Func1<SearchViewQueryTextEvent, Observable<BaseResponseWrapper<SearchUserResponse>>>() {
#Override
public Observable<BaseResponseWrapper<SearchUserResponse>> call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
// Why run in the main thread here
// 2016/6/12 reset api request
String res = searchViewQueryTextEvent.queryText().toString();
return RetrofitManager.newInstance().getApi().searchUserByPhone(res);
}
})
.map(new Func1<BaseResponseWrapper<SearchUserResponse>, List<SearchUserResponse>>() {
#Override
public List<SearchUserResponse> call(BaseResponseWrapper<SearchUserResponse> fuzzyUserRes) {
// some code here
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<SearchUserResponse>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
LogUtils.w("end thread:" + Thread.currentThread().getName());
LogUtils.w("e :" + e);
**//there throw exception android.os.NetworkOnMainThreadException**
}
#Override
public void onNext(List<SearchUserResponse> fuzzyUsers) {
updateUI(fuzzyUsers);
}
});
As long as observeOn is a downstream operator, you should use them to switch over threads many times.
Hope that it helps.
I've two RxJava Observables and I get an arraylist from first observable and then use it to get data from another observable like this.
Observable<KarobarTvVod> observable1 = youtubeDataHelper.getTVData();
observable1.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(new Subscriber<KarobarTvVod>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(KarobarTvVod karobarTvVod) {
Log.d(TAG, "onNext: size" + karobarTvVod.getEtag());
tvObjArrayList = new ArrayList<TVObj>();
for (int i = 0; i < karobarTvVod.getItems().size(); i++) {
TVObj tvObj = new TVObj();
tvObj.setVideoDate(karobarTvVod.getItems().get(i).getSnippet().getPublishedAt());
tvObj.setVideoIcon(karobarTvVod.getItems().get(i).getSnippet().getThumbnails().getHigh().getUrl());
tvObj.setVideoTitle(karobarTvVod.getItems().get(i).getSnippet().getTitle());
tvObj.setVideoID(karobarTvVod.getItems().get(i).getId().getVideoId());
tvObjArrayList.add(tvObj);
}
}
});
Observable<YoutubeViews> observable2 = youtubeDataHelper.getTVDataViews(tvObjArrayList);
observable2.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(new Subscriber<YoutubeViews>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError: in 2nd obs");
e.printStackTrace();
}
#Override
public void onNext(YoutubeViews youtubeViews) {
Log.d(TAG, "onNext: views" + youtubeViews.getEtag());
viewsList = new ArrayList<String>();
for (int i = 0; i < youtubeViews.getItems().size(); i++) {
viewsList.add(youtubeViews.getItems().get(i).getStatistics().getViewCount());
}
tvView.displayList(tvObjArrayList, viewsList);
}
});
This is just sample code, I need to pass the tvObjArrayList when it gets populated from 1st Observable to 2nd Observable, what is the best practice to do so ? And also I'm using for-loop inside the 1st Observable, is there a better way to achieve it using rxjava ? Thanks
You should use flatMap operator. It won't get much easier than that.
Observable<KarobarTvVod> observable1 = youtubeDataHelper.getTVData();
observable1.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.flatMap(new Func1<KarobarTvVod, Observable<YoutubeViews>>() {
#Override
public Observable<YoutubeViews> call(KarobarTvVod karobarTvVod) {
Log.d(TAG, "onNext: size" + karobarTvVod.getEtag());
tvObjArrayList = new ArrayList<TVObj>();
for (int i = 0; i < karobarTvVod.getItems().size(); i++) {
TVObj tvObj = new TVObj();
tvObj.setVideoDate(karobarTvVod.getItems().get(i).getSnippet().getPublishedAt());
tvObj.setVideoIcon(karobarTvVod.getItems().get(i).getSnippet().getThumbnails().getHigh().getUrl());
tvObj.setVideoTitle(karobarTvVod.getItems().get(i).getSnippet().getTitle());
tvObj.setVideoID(karobarTvVod.getItems().get(i).getId().getVideoId());
tvObjArrayList.add(tvObj);
}
return youtubeDataHelper.getTVDataViews(tvObjArrayList);
}
}).subscribe(new Subscriber<YoutubeViews>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError: in 1st or 2nd obs");
e.printStackTrace();
}
#Override
public void onNext(YoutubeViews youtubeViews) {
Log.d(TAG, "onNext: views" + youtubeViews.getEtag());
viewsList = new ArrayList<String>();
for (int i = 0; i < youtubeViews.getItems().size(); i++) {
viewsList.add(youtubeViews.getItems().get(i).getStatistics().getViewCount());
}
tvView.displayList(tvObjArrayList, viewsList);
}
});
You can use operator
toList().flatMap()
for Observable A, and in flatMap function, do work for Observable B.
For example:
observableA
.toList()
.flatMap(observableB.subscribe())
.subscribe()
I assume that getTVData and getTVDataViews each emits one item and than calls onComplete. If it true, than the following example works. No loop's, just pure rx :)
//getTVData should emit one item and then call obComplete
//otherwise toList() will wait forever
service.getTVData()
.flatMap(karobarTvVod -> Observable.from(karobarTvVod.getItems()))
.map(item -> {
TVObj tvObj = new TVObj();
//set other fields
//by the way, I recommend you to use immutable objects
return tvObj;
})
.toList()
//here we have List<TVObj>
.flatMap(
objs -> {
//getTVDataViews should emit one item and then call onComplete
//otherwise toList will wait forever
return service.getTVDataViews(objs)
.flatMap(youtubeViews -> Observable.from(youtubeViews.getItems()))
.map(integer -> integer.toString())
//after that we will have List<String>
.toList();
},
//a function that combines one item emitted by each of the source and collection Observables
// and returns an item to be emitted by the resulting Observable
new Func2<List<TVObj>,List<String>,Pair<List<TVObj>,List<String>>>() {
#Override
public Pair<List<TVObj>, List<String>> call(List<TVObj> objs, List<String> strings) {
return new Pair(objs, strings);
}
})
.subscribe(pair -> tvView.displayList(pair.first, pair.second));
PS. While this approach is more concise, I believe that loop for creating list of items is more efficient.
You need to subscribe the second observable in the onComplete of the first one
Observable<KarobarTvVod> observable1 = youtubeDataHelper.getTVData();
Observable<YoutubeViews> observable2 = youtubeDataHelper.getTVDataViews(tvObjArrayList);
observable1.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(new Subscriber<KarobarTvVod>() {
#Override
public void onCompleted() {
observable2.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(new Subscriber<YoutubeViews>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError: in 2nd obs");
e.printStackTrace();
}
#Override
public void onNext(YoutubeViews youtubeViews) {
Log.d(TAG, "onNext: views" + youtubeViews.getEtag());
viewsList = new ArrayList<String>();
for (int i = 0; i < youtubeViews.getItems().size(); i++) {
viewsList.add(youtubeViews.getItems().get(i).getStatistics().getViewCount ());
}
tvView.displayList(tvObjArrayList, viewsList);
}
});
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(KarobarTvVod karobarTvVod) {
Log.d(TAG, "onNext: size" + karobarTvVod.getEtag());
tvObjArrayList = new ArrayList<TVObj>();
for (int i = 0; i < karobarTvVod.getItems().size(); i++) {
TVObj tvObj = new TVObj();
tvObj.setVideoDate(karobarTvVod.getItems().get(i).getSnippet().getPublishedAt());
tvObj.setVideoIcon(karobarTvVod.getItems().get(i).getSnippet().getThumbnails().getHigh().getUrl());
tvObj.setVideoTitle(karobarTvVod.getItems().get(i).getSnippet().getTitle());
tvObj.setVideoID(karobarTvVod.getItems().get(i).getId().getVideoId());
tvObjArrayList.add(tvObj);
}
}
});
Of course to make this code more readable I would use a Consumer function for the observable2.subsriberOn on the onComplete method