In my Activity I have the following, which results in the error message 'Error:(190, 35) error: incompatible types: MaybeObserver cannot be converted to Disposable'. I expected this to work because I've been doing something similar with a Completable and a DisposableCompletableObserver combination. How can I use a Maybe with a CompositeDisposable in RxJava2?
private final CompositeDisposable disposables = new CompositeDisposable();
// ...
String id = authManager.getUserID();
Maybe maybe = userManager.getUser(id);
disposables.add(maybe
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableMaybeObserver() {
#Override
public void onSuccess(Object o) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
}));
You should use ResourceMaybeObserver:
private final CompositeDisposable disposables = new CompositeDisposable();
String id = authManager.getUserID();
Maybe<Object> maybe = userManager.getUser(id);
disposables.add(maybe
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new ResourceMaybeObserver<Object>() {
#Override
public void onSuccess(#NonNull Object o) {
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
}));
Related
I've added some code to make my question more clear.
Retrofit interface:
public interface JsonPlaceHolderAPI {
public static final String BASE_URL = "https://jsonplaceholder.typicode.com/";
#GET("todos/{number}")
Call<ResponseBody> getJsonResponse(#Path("number") String number);
}
The repository: --> fetchResponse() takes Viewmodel's MutableLiveData as parameter and uses it to update its value and then trigger View to change its UI.
public class Repository {
private final JsonPlaceHolderAPI api;
public Repository() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.build();
api = retrofit.create(JsonPlaceHolderAPI.class);
}
public void fetchResponse(String number, final MutableLiveData<CharSequence> mld){
final MutableLiveData<CharSequence> ml = new MutableLiveData<>();
api.getJsonResponse(number).enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
mld.setValue(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {}
});
}
}
The viewModel:
public class MainActivityViewModel extends AndroidViewModel {
MutableLiveData<CharSequence> response = new MutableLiveData<>();
Repository repository;
public MainActivityViewModel(#NonNull Application application) {
super(application);
repository = new Repository();
}
public void fetchData(String number) {
response.setValue("Loading data");
repository.fetchResponse(number, response);
}
public LiveData<? extends CharSequence> getLiveData() {
return response;
}
}
The View:
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
initViews();
viewModel.getLiveData().observe(this, new Observer<CharSequence>() {
#Override
public void onChanged(CharSequence charSequence) {
if (charSequence != null) {
txt.setText(charSequence);
}
}
});
}
...
I am not sure if I should pass the MutableLiveData from the viewModel to the Repository.
Is there any recommended way to let viewModel know that data is ready to be published from Repository??
I have read a lot of questions and articles and still I don't get it. I would love if somebody explain to me a nice way to achieve it!
Api
public interface TodoApi {
#GET("todos/")
Call<List<Todo>> getTodos();
#GET("todos/{id}")
Call<Todo> getTodo(#Path("id") long id);
}
Respository
public class TodoRepository {
private static final String TAG = "TodoRepository";
private static final TodoRepository ourInstance = new TodoRepository();
private TodoApi api;
private MutableLiveData<List<Todo>> todoListLiveData = new MutableLiveData<>();
private MutableLiveData<Todo> todoLiveData = new MutableLiveData<>();
public static TodoRepository getInstance() {
return ourInstance;
}
private TodoRepository() {
api = ApiBuilder.create(TodoApi.class);
}
public LiveData<List<Todo>> getTodos() {
api.getTodos().enqueue(new Callback<List<Todo>>() {
#Override
public void onResponse(#NonNull Call<List<Todo>> call, #NonNull Response<List<Todo>> response) {
todoListLiveData.setValue(response.body());
}
#Override
public void onFailure(#NonNull Call<List<Todo>> call, #NonNull Throwable t) {
Log.d(TAG, "onFailure: failed to fetch todo list from server");
}
});
return todoListLiveData;
}
public LiveData<Todo> getTodo(long id) {
api.getTodo(id).enqueue(new Callback<Todo>() {
#Override
public void onResponse(#NonNull Call<Todo> call, #NonNull Response<Todo> response) {
todoLiveData.setValue(response.body());
}
#Override
public void onFailure(#NonNull Call<Todo> call, #NonNull Throwable t) {
Log.d(TAG, "onFailure: failed to get todo");
}
});
return todoLiveData;
}
}
ViewModel
public class MainActivityViewModel extends ViewModel {
private static final String TAG = "MainActivityViewModel";
private TodoRepository repository = TodoRepository.getInstance();
private MutableLiveData<Boolean> isLoading = new MutableLiveData<>();
private LiveData<List<Todo>> todoLiveData;
public MainActivityViewModel() {
super();
isLoading.setValue(true);
todoLiveData = repository.getTodos();
}
#Override
protected void onCleared() {
super.onCleared();
}
public MutableLiveData<Boolean> getIsLoading() {
return isLoading;
}
public LiveData<List<Todo>> getTodoLiveData() {
return todoLiveData;
}
}
View
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
todoListRecyclerView = findViewById(R.id.todo_recycler_view);
loadingIndicator = findViewById(R.id.todo_loading_indicator);
mViewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
getSupportActionBar().setTitle("Todos");
mViewModel.getIsLoading().observe(this, new Observer<Boolean>() {
#Override
public void onChanged(Boolean isLoading) {
if (isLoading) loadingIndicator.setVisibility(View.VISIBLE);
else loadingIndicator.setVisibility(View.GONE);
}
});
mViewModel.getTodoLiveData().observe(this, new Observer<List<Todo>>() {
#Override
public void onChanged(List<Todo> todos) {
mViewModel.getIsLoading().postValue(false);
initRecyclerView(todos);
}
});
}
For full sample
https://github.com/AnvarNazar/RetrofitTypicodeApiExample
How can I get return value from bellow code :
CompositeDisposable mCompositeDisposable = new CompositeDisposable();
mCompositeDisposable.add(observableSubject(userName, password)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(new Consumer<Disposable>() {
#Override
public void accept(#NonNull Disposable disposable) throws Exception {
}
})
.doFinally(new Action() {
#Override
public void run() throws Exception {
}
})
.subscribeWith(new DisposableObserver<String>() {
#Override
public void onComplete() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String res) {
//I need to get value from out of mCompositeDisposable.add(...)
Log.i("LOG", res);
}
}));
And :
private Observable<String> observableSubject(String userName, String password) {
return Observable.defer(() -> {
//Some Code
return Observable.just("Value");
});
}
I resolved my problem with EventBus in this method :
#Override
public void onNext(String res) {
//SomeCode ....
//.... EventBus.getDefault() ....
}
Few days ago I have faced with the treading rx problem. I was thinking how the rxJava threading works. This code didn't want to run on another thread.
mAPIInterface.getAllProjects(accessToken)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Project>>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
mView.showLoadProgress();
}
#Override
public void onNext(#NonNull List<Project> allProjects) {
projects = allProjects;
mView.hideLoadProgress();
mView.onProjectsLoaded();
}
#Override
public void onError(#NonNull Throwable e) {
mView.hideLoadProgress();
mView.onProjectsLoadError();
}
#Override
public void onComplete() {
}
});
Then I have find the solution but I'm still not sure if it's ok. That's look like there are some more good solution:
mAPIInterface.getAllProjects(accessToken)
.subscribeOn(Schedulers.newThread())
.map(new Function<List<Project>, List<Project>>() {
#Override
public List<Project> apply(#NonNull List<Project> projects) throws Exception {
return projects;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Project>>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
mView.showLoadProgress();
}
#Override
public void onNext(#NonNull List<Project> allProjects) {
projects = allProjects;
mView.hideLoadProgress();
mView.onProjectsLoaded();
}
#Override
public void onError(#NonNull Throwable e) {
mView.hideLoadProgress();
mView.onProjectsLoadError();
}
#Override
public void onComplete() {
}
});
With that map() middleware it works. But should I use that everytime when I need to run the process on another thread?
Am doing a weather API call every 3secs and append the result to a textView using showWeather("weather") but I am sure there is a better way to do it. Am not sure why I need create Class Func1 but did because map required it. Also is there a way to shorten observer? I don't use lamda unfortunately. Any suggestions?
Observer myObserver = new Observer<String>() {
#Override
public void onError(Throwable e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
#Override
public void onComplete() {
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String value) {
showWeather(value);
}
};
class Func1<T, T1> implements io.reactivex.functions.Function<Long, String > {
#Override
public String apply(Long aLong) throws Exception {
return getJSON("http://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b1b15e88fa797225412429c1c50c122a1",300);
}
}
Observable.interval(3, TimeUnit.SECONDS)
.map(new Func1<Long, Observable<String>>() {
}).observeOn(AndroidSchedulers.mainThread()).subscribe(myObserver);
I tried :
Observable
.interval(3, SECONDS)
.subscribeOn(Schedulers.io())
.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
return getJSON("http://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b1b15e88fa797225412429c1c50c122a1", 300);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(myObserver)
But I get :
03-07 21:47:25.982 21181-21181/com.alex.rxandroidexamples E/imerExampleFragment$1$1: null
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1273)
Also how do I unsubscribe onPause or onPause?
I found out the best way to do this is :
private final CompositeDisposable disposables = new CompositeDisposable();
Observable fetchWeatherInterval = Observable.interval(3, TimeUnit.SECONDS)
.map(new Function<Long, String>() {
#Override
public String apply(Long aLong) throws Exception {
return getWeather("http://samples.openweathermap.org/data/2.5/weather?", "London,uk", "b1b15e88fa797225412429c1c50c122a1");
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
Observer displayWeatherInterval = new Observer<String>() {
#Override
public void onError(Throwable e) {
Log.e("Throwable ERROR", e.getMessage());
}
#Override
public void onComplete() {
}
#Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
#Override
public void onNext(String value) {
textViewWeatherInterval.append(value);
}
};
buttonFetchIntervalWeather.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fetchWeatherInterval.subscribe(displayWeatherInterval);
}
});
I've an Observable something like this:
#GET("endpoint")
Observable<Something> getSomething();
and Subscriber like this
Subscriber<Something> somethingSubscriber = new Subscriber<Something>() {
public void onCompleted() {
}
public void onError(Throwable e) {
//handle exceptions
}
public void onNext() {
//do something
}
In my OnClickListener associated with a button, i make a subscription
getSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(somethingSubscriber);
If i don't have an internet connection, onError is called and i do some exception handling. when I press the button again (assume i want to retry), the callback methods do not get called.
I want that onNext / onError callbacks get called everytime I press the button.
There is extention for RxJava. It has a lot of "cool tools", but for handling retrofit errors you can use ResponseOrError class.
So in you case it would looks like:
final PublishSubject<Object> clickSubject = PublishSubject.create();
final Observable<ResponseOrError<Something>> responseOrErrorObservable = clickSubject
.flatMap(new Func1<Object, Observable<ResponseOrError<Something>>>() {
#Override
public Observable<ResponseOrError<Something>> call(Object o) {
return getSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.compose(ResponseOrError.<Something>toResponseOrErrorObservable());
}
})
.replay(1)
.refCount();
final Observable<Throwable> error = responseOrErrorObservable
.compose(ResponseOrError.<Something>onlyError())
.subscribe(new Action1<Segment>() {
#Override
public void call(Throwable throwable) {
// what to do on error, some toast or what ever yu need
}
});
final Observable<UserInfoResponse> success = responseOrErrorObservable
.compose(ResponseOrError.<Something>onlySuccess())
.subscribe(new Action1<Something>() {
#Override
public void call(Something some) {
// code what to do on success
}
});
And now, into onClick you just need to put clickSubject.onNext(null)
.replay(1).refCount(); needed because there are 2 Observables that uses responseOrErrorObservable, so without it retrofit request will "happens" two times.
You are reusing the same Subscriber. Once you get the onError or a result (so it completes) the subscriber is unsubscribed. Try to pass every time a new subscriber.
use this code
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Something>() {
#Override
public void call(Something something) {
//do something
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
//handle exceptions
}
},
new Action0() {
#Override
public void call() {
}
});
}
});
Addition
or
replace this
Subscriber<Something> somethingSubscriber = new Subscriber<Something>() {
public void onCompleted() {
}
public void onError(Throwable e) {
//handle exceptions
}
public void onNext() {
//do something
}
};
to
Subscriber<String> somethingSubscriber = new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
}
};
In my Case onNext() and onError() methods are not getting called because of my model class wrong parsing, I was taking a double object as Integer so NumberFormatException was thrown and nothing was happening after getting the result from retrofit.