I've started to grok into retrolambda and rxjava. Some expressions i'v converted by myself, but some of them i can't convert. I've added retrolambda to my project.Here is examples
public Observable<ImmutableList<Repository>> getUsersRepositories() {
return githubApiService.getUsersRepositories(user.login)
.map(repositoryResponses -> {
final ImmutableList.Builder<Repository> listBuilder = ImmutableList.builder();
for (RepositoryResponse repositoryResponse : repositoryResponses) {
Repository repository = new Repository();
repository.id = repositoryResponse.id;
repository.name = repositoryResponse.name;
repository.url = repositoryResponse.url;
listBuilder.add(repository);
}
return listBuilder.build();
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
But i don't know how to convert this peace of code:
obs.subscribe(new Observer<List<Integer>>() {
public void onCompleted() {
System.out.println("completed");
}
public void onError(Throwable e) {
System.out.println("failure");
}
public void onNext(List<Integer> value) {
System.out.println("onnext=" + value);
}
});
I think that you want to make something like this:
obs.subscribe(
(List<Integer> value) -> System.out.println("onnext=" + value),
(Throwable e) -> System.out.println("failure"),
()-> System.out.println("completed"));
You can check this blog and this example in GitHub.
Lambda is an anonymous function and you are creating an anonymous class, similar but different.
First one is already a Lambda: .map(repositoryResponses -> {...}, what do you want to change more? The second part is a "block code" so you can't reduce it more.
Second one you can't convert it because it's not a function.
I've found the way to solve it. Here is code before conversion
repositoriesManager.getUsersRepositories().subscribe(new SimpleObserver<ImmutableList<Repository>>() {
#Override
public void onNext(ImmutableList<Repository> repositories) {
repositoriesListActivity.showLoading(false);
repositoriesListActivity.setRepositories(repositories);
}
#Override
public void onError(Throwable e) {
repositoriesListActivity.showLoading(false);
}
});
after conversion
repositoriesManager.getUsersRepositories().subscribe(repositories -> {
repositoriesListActivity.showLoading(false);
repositoriesListActivity.setRepositories(repositories);
},throwable -> {
repositoriesListActivity.showLoading(false);
});
Related
I am a newbie in Kotlin and want to learn Lambda functions. I have learning Android for months now .
and wanted to integrate both Kotlin and Andriod , so for practicing I am trying to convert Java code to Kotlin. I am facing trouble while getting callbacks. The below code requires a callback which I am not able to achieve.
I haven't mentioned that ReservoirPutCallback interface is coded in Java and is in a read-only mode
Here is mycode(Java),in which I am facing errors :-
if(DISK_CACHE_INITIALIZED){
Reservoir.putAsync(Constants.SCIENTISTS_CACHE_KEY, scientists,
**new ReservoirPutCallback()** {
#Override
public void onSuccess() {
//success
DISK_CACHE_DIRTY = false;
}
#Override
public void onFailure(Exception e) {
Log.e("CAMPOSHA","PUTTING CACHE TO DISK FAILED");
}
});
}
}
public static LiveData<List<Scientist>> bindFromDiskCacheAsync(){
MutableLiveData<List<Scientist>> scientistLiveData=new MutableLiveData<>();
if(!DISK_CACHE_INITIALIZED){
return null;
}
**Type resultType = new TypeToken<List<Scientist>>() {}.getType()**;
Reservoir.getAsync(Constants.SCIENTISTS_CACHE_KEY, resultType,
new ReservoirGetCallback<List<Scientist>>() {
#Override
public void onSuccess(List<Scientist> scientists) {
scientistLiveData.setValue(scientists);
}
#Override
public void onFailure(Exception e) {
Log.e("CAMPOSHA","ASYNC REFRESH FROM DISK FAILED");
scientistLiveData.setValue(null);
}
});
return scientistLiveData;
}
Kotlin supports what you're trying to do. You're actually passing a full object as a parameter, not just a callback. If it was a single callback function, you could use a lambda, but this is more like a "callback object".
For that, we use anonymous objects rather than lambdas/functions. This is true for both java and kotlin.
The first callback will look like:
Reservoir.putAsync(Constants.SCIENTISTS_CACHE_KEY, scientists,
object : ReservoirPutCallback {
override fun onSuccess() {
//success
DISK_CACHE_DIRTY = false
}
override fun onFailure(e: Exception) {
Log.e("CAMPOSHA","PUTTING CACHE TO DISK FAILED")
}
}
)
The second is roughly the same thing.
Android Studio 3.1 RC 2
kotlin 1.2.30
Signature for the fetchMessage
Single<Response> fetchMessage(final String messageId);
The kotlin code that I am trying to convert to Java. However, I am not sure where the returns are? As I am new to kotlin and lambda.
private fun getMessage(messageId: String): Observable<State> {
return repository
.fetchMessage(messageId)
.flatMap {
Single.fromCallable<State>({
update(messageId, it, State.COMPLETED)
State.COMPLETED
})
}
.toObservable()
}
This is my attempt at trying to convert it. However, the compiler complains of a missing return.
public Observable<TranslationChatState> translate(String messageId) {
return repository
.fetchMessage(messageId)
.flatMap(new Func1 <Response, Single<State >>() {
#Override
public Single<State> call(final Response response) {
Single.fromCallable(new Callable<State>() {
#Override
public State call() {
update(messageId, response, State.COMPLETED);
return State.COMPLETED;
}
});
} /* complains about no return here */
})
.toObservable();
}
Many thanks for any suggestions,
because it is missing the return statement. It should be
public Single<State> call(final Response response) {
return Single.fromCallable(new Callable<State>() {
I'm using RxJava inside an evernote job to send some data to API. This code was working just fine till now. The problem is somewhere in the .filter I think as it isn't even getting to getApiService().createReport(requestModel) method unless there are photos in the model (then the report.getImages() is not null.
public static Observable<Report> createReport(CreateReportModel model) {
return Observable.just(model)
.filter(report -> report.getImages() != null)
.flatMap(report -> {
return Observable.from(report.getImages())
.map(photoModel -> {
return photoModel;
})
.filter(photoModel -> photoModel.hasImage())
.filter(photoModel -> photoModel.getImage().exists())
.flatMap(photoModel -> uploadFile(photoModel)).toList();
})
.map(photoModels -> model)
.flatMap(requestModel -> {
return getApiService().createReport(requestModel)
.map(response -> {
return response;
});
});
}
This function is called inside this code
Observable<PalletReport> report = createReport(model);
report.subscribe(new Subscriber<PalletReport>() {
#Override
public void onCompleted() {
resultHolder.setResult(Result.SUCCESS);
#Override
public void onError(Throwable e) {
Timber.d(e, "Upload Error");
resultHolder.setResult(Result.RESCHEDULE);
}
#Override
public void onNext(PalletReport model) {
Timber.d("On Next " + model);
}
});
And here it goes to Result.SUCCESS but the response isn't get and the report isn't create on back end. My concern is that this code was working just fine a few days ago, and without any changes it stopped.
[EDIT]
I have this function that is called inside the first flatMap, and it's used to send the photos.
#NonNull
private static Observable<? extends CreatePalletPhotoModel> uploadPalletFile(CreatePalletPhotoModel photo) {
MultipartBody.Part fileBody = Paperless.createFileBody(photo.getImage());
return Paperless.getApiService().uploadPalletPhoto(fileBody)
.map(upload -> {
photo.setPalletStatus(upload.getPalletStatus());
photo.setImage(upload.getImage());
return photo;
});
}
If there are no reports after filter nothing will get executed. Consider removing
.map(photoModels -> model)
and just end the first observable there (you would need to subscribe to it) and start again with
Observable.just(model).flatMap(requestModel -> {
return getApiService().createReport(requestModel)
.map(response -> {
return response;
});
});
that will ensure that getApiService call is always executed.
Trying to test new Android Room librarty with RxJava adapter. And I want to handle result if my query returns 0 objects from DB:
So here is DAO method:
#Query("SELECT * FROM auth_info")
fun getAuthInfo(): Flowable<AuthResponse>
And how I handle it:
database.authDao()
.getAuthInfo()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.switchIfEmpty { Log.d(TAG, "IS EMPTY") }
.firstOrError()
.subscribe(
{ authResponse -> Log.d(TAG, authResponse.token) },
{ error -> Log.d(TAG, error.message) })
My DB is empty, so I expect .switchIfEmty() to work, but none of handling methods is firing. Neither .subscribe() nor .switchIfEmpty()
Db Flowables are observable (so they keep dispatching if database changes) so it never completes. You can try returning List<AuthResponse>. We've considered back porting an optional but decided not to do it, at least for now. Instead, we'll probably add support for Optional in different known libraries.
In version 1.0.0-alpha5, room added support of Maybe and Single to DAOs, so now you can write something like
#Query("SELECT * FROM auth_info")
fun getAuthInfo(): Maybe<AuthResponse>
You can read more about it here
switchIfEmpty takes as parameter a Publisher<AuthResponse>. Through SAM-conversion your given anonymous function is turned into this class. However it does not follow the behavior expected from a Publisher so it will not work as expected.
Replace it with a correct implementation like Flowable.empty().doOnSubscribe { Log.d(TAG, "IS EMPTY") } and it should work.
You could use some wrapper for result. For example:
public Single<QueryResult<Transaction>> getTransaction(long id) {
return createSingle(() -> database.getTransactionDao().getTransaction(id))
.map(QueryResult::new);
}
public class QueryResult<D> {
public D data;
public QueryResult() {}
public QueryResult(D data) {
this.data = data;
}
public boolean isEmpty(){
return data != null;
}
}
protected <T> Single<T> createSingle(final Callable<T> func) {
return Single.create(emitter -> {
try {
T result = func.call();
emitter.onSuccess(result);
} catch (Exception ex) {
Log.e("TAG", "Error of operation with db");
}
});
}
And use it like 'Single' in this case you will get result in any case. Use:
dbStorage.getTransaction(selectedCoin.getId())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(r -> {
if(!r.isEmpty()){
// we have some data from DB
} else {
}
})
I am trying to test my ViewModel in my application, here is the constructor:
#Inject
public SearchUserViewModel(#Named("searchUser") UseCase searchUserUseCase) {
this.searchUserUseCase = searchUserUseCase;
}
In my test I create a SearchUserUseCase with mocks like this:
Observable error = Observable.error(new Throwable("Error"));
when(gitHubService.searchUser(MockFactory.TEST_USERNAME_ERROR)).thenReturn(error);
when(ObserverThread.getScheduler()).thenReturn(Schedulers.immediate());
when(SubscriberThread.getScheduler()).thenReturn(Schedulers.immediate());
searchUserUseCase = new SearchUserUseCase(gitHubService, SubscriberThread, ObserverThread);
In my ViewModel class I have this snippet which I want to test:
public void onClickSearch(View view) {
loadUsers();
}
private void loadUsers() {
if (username == null) {
fragmentListener.showMessage("Enter a username");
} else {
showProgressIndicator(true);
searchUserUseCase.execute(new SearchUserSubscriber(), username);
}
}
private final class SearchUserSubscriber extends DefaultSubscriber<SearchResponse> {
#Override
public void onCompleted() {
showProgressIndicator(false);
}
#Override
public void onError(Throwable e) {
showProgressIndicator(false);
fragmentListener.showMessage("Error loading users");
}
#Override
public void onNext(SearchResponse searchResponse) {
List<User> users = searchResponse.getUsers();
if (users.isEmpty()) {
fragmentListener.showMessage("No users found");
} else {
fragmentListener.addUsers(users);
}
}
}
Finally in my test I have this:
#Test
public void shouldDisplayErrorMessageIfErrorWhenLoadingUsers() {
SearchUserViewModel searchUserViewModel = new SearchUserViewModel(searchUserUseCase);
searchUserViewModel.setFragmentListener(mockFragmentListener);
searchUserViewModel.setUsername(MockFactory.TEST_USERNAME_ERROR);
searchUserViewModel.onClickSearch(view);
verify(mockFragmentListener).showMessage("Error loading users");
}
I get this error from Mockito:
Wanted but not invoked:
fragmentListener.showMessage(
"Error loading users"
);
I am not sure if this is a good test, but I somehow want to test the SearchUserSubscriber one way or another. Thanks
Edit: I have found similar questions to this problem here: Can't verify mock method call from RxJava Subscriber (which still isn't answered) and here: Verify interactions in rxjava subscribers. The latter question is similar but does not execute the subscriber in a separate class (which happens in SearchUserUseCase here).
I also tried RobolectricGradleTestRunner instead of MockitoJunitRunner and changed to Schedulers.io() and AndroidSchedulers.mainThread(), but I still get the same error.
Tried mocking SearchUserUseCase instead of GitHubService (which feels cleaner), but I'm not sure on how to test the subscriber that way since that is passed as an argument to the void method execute() in UseCase.
public void execute(Subscriber useCaseSubscriber, String query) {
subscription = buildUseCase(query)
.observeOn(postExecutionThread.getScheduler())
.subscribeOn(threadExecutor.getScheduler())
.subscribe(useCaseSubscriber);
}
And buildUseCase()
#Override
public Observable buildUseCase(String username) throws NullPointerException {
if (username == null) {
throw new NullPointerException("Query must not be null");
}
return getGitHubService().searchUser(username);
}
For me it worked out to add a Observable.Transformer<T, T> as followed:
void gatherData() {
service.doSomeMagic()
.compose(getSchedulerTransformer())
.subscribe(view::displayValue);
}
private <T> Observable.Transformer<T, T> getSchedulerTransformer() {
if (mTransformer == null) {
mTransformer = (Observable.Transformer<T, T>) observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
return mTransformer;
}
void setSchedulerTransformer(Observable.Transformer<Observable<?>, Observable<?>> transformer) {
mTransformer = transformer;
}
And to set the Transformer. I just passed this:
setSchedulerTransformer(observable -> {
if (observable instanceof Observable) {
Observable observable1 = (Observable) observable;
return observable1.subscribeOn(Schedulers.immediate())
.observeOn(Schedulers.immediate());
}
return null;
});
So just add a #Before method in your test and call presenter.setSchedulerTransformer and it should be able to test this. If you want more detail check this answer.
If you are using Mockito, you can probably get hold of a SearchUserSubscriber using an ArgumentCaptor, for example...
#Captor
private ArgumentCaptor<SearchUserSubscriber> subscriberCaptor;
private SearchUserSubscriber getSearchUserSubscriber() {
// TODO: ...set up the view model...
...
// Execute the code under test (making sure the line 'searchUserUseCase.execute(new SearchUserSubscriber(), username);' gets hit...)
viewModel.onClickSearch(view);
verify(searchUserUseCase).execute(subscriberCaptor.capture(), any(String.class));
return subscriberCaptor.getValue();
}
Now you can have test cases such as...
#Test
public void shouldDoSomethingWithTheSubscriber() {
SearchUserSubscriber subscriber = getSearchUserSubscriber();
...
}