A RESTful API End-point returns a JSON Response.
The JSON Response consists of 20 different JSON-Array elements. Each element has a Image URL.
After receiving the first response, I intend to loop through the JSON-Array and fetch the images as well, using Retrofit Dynamic URLs.
class Content { var items List<Item>? = null }
class Item { var image: String? = null
var imageBytes: ByteArray? = null
}
interface RestApi { #GET fun getContent(): Single<Content>
#GET fun getImageBytes(#Url url: String): Single<ResponseBody>
}
restApi.getContent()
.map { content: Content -> {
content.items?.forEach { item ->
restApi.getImageBytes(item.image)
.subscribe(object : DisposableSingleObserver<ResponseBody>() {
onSuccess(body: ResponseBody) {
item.imageBytes = body.getBytes()
}
onError(e: Throwable) { // TODO Implementation }
})
}
}
}
How do I make the getContent() method delay before emitting until all invocations of the getImageBytes() also complete with either success or error?
With Single you should use flatten observable it will give a list of observables which the flat map will take up
getListOfItems()
.flattenAsObservable(new Function<Object, Iterable<?>>() {
#Override
public Iterable<?> apply(#NonNull Object o) throws Exception {
return toItems(o);
}
})
.flatMap(item -> doSomethingWithItem())
.toList()
Related
I am new to Kotlin and I am making a method that makes a call to an interface of Endpoints and uses one of the methods present there. I am using Observable<> instead of Call<> into the response. I wanted to know how to obtain the response body() in the "result" above. This is my method
private fun refreshUser(userLogin: String) {
executor.execute {
// Check if user was fetched recently
val userExists = userDao.hasUser(userLogin, getMaxRefreshTime(Date())) != null
// If user have to be updated
if (!userExists) {
disposable = endpoints.getUser(userLogin)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result -> /*Get the response body() HERE*/},
{ error -> Log.e("ERROR", error.message) }
)
}
}
}
It all depends on how you have defined the Retrofit interface. In order to get the Response you need to return something from the interface that looks like:
fun getUsers() : Observable<Response<User>>
Then inside { result -> /*Get the response body() HERE*/}, you will get something of the form Response<User>, which has the response's body.
Also to note, you do not need to enclosing executor if you leverage Room for the dao interactions; it has RxJava support. You can use RxJava operators to combine the dao lookup with the server call.
See this tutorial
https://medium.freecodecamp.org/rxandroid-and-kotlin-part-1-f0382dc26ed8
//Kotlin
Observable.just("Hello World")
.subscribeOn(Schedulers.newThread())
//each subscription is going to be on a new thread.
.observeOn(AndroidSchedulers.mainThread()))
//observation on the main thread
//Now our subscriber!
.subscribe(object:Subscriber<String>(){
override fun onCompleted() {
//Completed
}
override fun onError(e: Throwable?) {
//TODO : Handle error here
}
override fun onNext(t: String?) {
Log.e("Output",t);
}
})
if you wanna use retrofit 2 and rxjava 2
https://medium.com/#elye.project/kotlin-and-retrofit-2-tutorial-with-working-codes-333a4422a890
interface WikiApiService {
#GET("api.php")
fun hitCountCheck(#Query("action") action: String,
#Query("format") format: String,
#Query("list") list: String,
#Query("srsearch") srsearch: String):
Observable<Model.Result>
}
Observable is the class response.
private fun beginSearch(srsearch: String) {
disposable =
wikiApiServe.hitCountCheck("query", "json", "search", srsearch)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result -> showResult(result.query.searchinfo.totalhits) },
{ error -> showError(error.message) }
)
}
If, as you mentioned to #Emmanuel, the return type of your getUser() method is Observable<Response<User>> then calling result.body() will yield the resulting User.
{ result ->
val user: User = result.body()
}
If however, you are looking for the the raw response, you can instead call result.raw().body(); which will return an okhttp3.ResponseBody type.
{ result ->
val body: ResponseBody = result.raw().body()
val text: String = body.string()
}
I have a service like this
#GET("/")
fun getInfo(#Query("pew") pew1: Double, #Query("pew2") pew2: Double): Observable<PewResponse>
So now, when I have this
I have a List to fill with server data
private var mPewList: MutableList<PewList> = arrayListOf()
And then I have this function
override fun getResultFromNetwork(): Observable<PewResponse> {
val pewObservable = pewService.getInfo("pew1","pew2")
return pewObservable.concatMap {
//How do I fill my list ??
}
}
I'm pretty new to this rxJava so I saw people using Single and other guys using Observable what's better?
EDIT
I'm following this example https://github.com/joanby/android-mvp/blob/master/MoviesFeed/app/src/main/java/com/juangabriel/moviesfeed/movies/MoviesRepository.java
And now I want to do something like this :
#Override
public Observable<Result> getResultFromNetwork() {
Observable<TopMoviesRated> topMoviesRatedObservable = moviesApiService.getTopMoviesRated(1)
/*.concatWith(moviesApiService.getTopMoviesRated(2))
.concatWith(moviesApiService.getTopMoviesRated(3))*/;
return topMoviesRatedObservable
.concatMap(new Function<TopMoviesRated, Observable<Result>>() {
#Override
public Observable<Result> apply(TopMoviesRated topMoviesRated) {
return Observable.fromIterable(topMoviesRated.getResults());
}
}).doOnNext(new Consumer<Result>() {
#Override
public void accept(Result result) {
results.add(result);
}
});
}
So as I'm seeing he fills the result list, and then on presenter do this : https://github.com/joanby/android-mvp/blob/master/MoviesFeed/app/src/main/java/com/juangabriel/moviesfeed/movies/MoviesPresenter.java
If your data from server is monolithic you should to use Single because it return only once value. The Observable will do when your data arrives in chunks.
For more details you can see this answer: https://stackoverflow.com/a/42759432/9060113
EDIT:
#GET("/")
fun getInfo(#Query("pew") pew1: Double, #Query("pew2") pew2: Double): Single<PewResponse>
override fun getResultFromNetwork() {
pewService
.getInfo("pew1","pew2")
.subscribeOn(Schedulers.io()) // for background processing
.observeOn(AndroidSchedulers.mainThread()) // get result in Main Thread (it require rxAndroid lib)
.subscribe(
{ pewResponse -> // parse response and fill list },
{ // handle error }
)
}
I have a list List<FileModel>
FileModel is just a class contains id: Int
id - is ID of photo file I need to fetch and cast to Bitmap
I have a request:
fun getFile(fileId: Int): Single<ResponseBody>
This request returns ResponseBody which we can cast to Bitmap
And
fun generatePhoto(responseBody: ResponseBody): Single<Bitmap?>
What I want is to create a function
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
// Execute getFile(...) for each item in the list
// Cast getFile(...) result to Bitmap using generatePhoto(...)
// Return a list of Bitmaps
}
I've tried something like this but it's completely wrong
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
return Observable.fromIterable(list)
.flatMap { getFile(it.id) }
// How to call generatePhoto(...) here?
}
You can do it like this:
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
// Get an Observable of the list
return Observable.fromIterable(list)
// Get a Single<ResponseBody> for every FileModel
.flatMapSingle { getFile(it.id) }
// Get a Single<Bitmap> for every ResponseBody
.flatMapSingle { file -> generatePhoto(file) }
// Put everything back on a list
.toList()
}
This way you can iterate over the list flapMapping for your needs and then putting it back together as a list in the end. The toList() operator is just a convenience that puts together the items emitted previously.
And to call this function just go:
getPhotos(list)
.doOnSuccess { resultList ->
Log.d("Rx", "doOnSuccess.resultList=[$resultList]")
}
.subscribe()
By the way, if you have RxKotlin as a dependency you can get an Observable from a List with an extension function, like this:
myList.toObservable()
Observable.fromIterable(list) should emit an event for each item in the list which means that you're return type in getPhotos be Flowable<Bitmap> as Flowable can return more than one value.
I think what you actually want is:
fun getPhotos(list: List<FileModel>): Flowable<Bitmap> {
return Observable.fromIterable(list)
.map { getFile(it.id) }
.map { generatePhoto(it) }
}
if you want to emit a list of Bitmaps you can use single like this
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
return Single.just(list)
.map { it.map { getFile(it.id) } }
.flatMap {
Single.just(it.map { generatePhoto(it) })
}
}
Have you tried:
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
return Observable.fromIterable(list)
.flatMapSingle { getFile(it.id) }
.flatMapSingle { generatePhoto(it) }
}
?
I'm trying to create generic architecture to consume complex json structure as follows:
Json Format
{
"type": "success",
"code": "s-groups-0006",
"description": "Index List successfully",
"result": {
"asOnDate": 1505457095278,
"indexList": [
{
"change": "22.35",
"changePercent": "0.27",
"isDefault": true,
"isEditable": false
}
]
}
}
Dagger Format
#Singleton
fun provideGson(): Gson =
GsonBuilder()
.setLenient()
// .registerTypeAdapter(BaseResponse::class.java, RestDeserializer<BaseResponse<T>>())
.create()
Rest Deseralizers
class RestDeserializer<T> : JsonDeserializer<T> {
#Throws(JsonParseException::class)
override fun deserialize(je: JsonElement, type: Type, jdc: JsonDeserializationContext): T? {
val content = je.asJsonObject
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return Gson().fromJson<T>(content, type)
}
}
Error callback
abstract class ErrorCallBack<T : BaseResponse<T>> : DisposableObserver<T>() {
protected abstract fun onSuccess(t: T)
override fun onNext(t: T) {
//You can return StatusCodes of different cases from your API and handle it here. I usually include these cases on BaseResponse and iherit it from every Response
onSuccess(t)
}
override fun onError(e: Throwable) {
when (e) {
is HttpException -> {
val responseBody = (e).response().errorBody()
responseBody?.let {
L.e("Error in call htttp exception")
}
}
is SocketTimeoutException -> {
// todo
L.e("Error in Socket time out")
}
is IOException -> {
// todo
L.e("Error in IO Exception")
}
else -> {
e.message?.let {
// todo
}
}
}
}
override fun onComplete() {
}
private fun getErrorMessage(responseBody: ResponseBody): String {
return try {
val jsonObject = JSONObject(responseBody.string())
jsonObject.getString("message")
} catch (e: Exception) {
e.message!!
}
}
}
Repository
override fun getValidateUser(validateUser: ValidateUser): LiveData<ValidateUserResponse> {
val mutableLiveData = MutableLiveData<ValidateUserResponse>()
remoteServices.requestValidateUser(validateUser)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : ErrorCallBack<BaseResponse<ValidateUserResponse>>() {
override fun onSuccess(t: BaseResponse<ValidateUserResponse>) {
if (t.type == CommonContents.SUCCESS) {
L.d("Success in validate user")
mutableLiveData.value = transform(t)
} else {
L.e("Validate User Error")
}
}
})
return mutableLiveData
}
Data Class
data class BaseResponse<out T>(
#SerializedName(CommonContents.TYPE) val type: String,
#SerializedName(CommonContents.CODE) val Code: String,
#SerializedName(CommonContents.DESCRIPTION) val Description: String,
#SerializedName(CommonContents.RESULT)val result: T? = null)
These are my structures and I try to make a generic structure but am facing an issue when invoking the Error callback.
Please guide me how to achieve this. Can i call generic method inside generic Response?
.subscribeWith(object : ErrorCallBack<BaseResponse<ValidateUserResponse>>() {
Guide To Achieving Working Code
This is my guide to making some code work. It is based on the principles of Test Driven Development.
Set up your unit test environment in your IDE. Personally, I have been using JUnit 4 with Eclipse, but you may prefer JUnit 5 with JetBrains' IDE.
Write a unit test for your ErrorCallback class constructor. Make it pass. Next, write unit tests for each of the methods, to see that it behaves the way that you expect.
Write unit tests where your test fixture is a PublishSubject<BaseResponse<Integer>> for some number of different cases: normal data, sequence of normal data, normal data followed by error, normal data followed by completion.
From there, add some more tests so that you can test the entire observable chain.
private Observable< SimpleResource > resource;
return resource.map(new Function<SimpleResource, Flowable<Data>>() {
#Override
public Flowable< Data > apply(SimpleResource resource) throws Exception {
return resource.data().toFlowable();
}
});
Single<Data> data();
I need to have Flowable but my result is Observable>
Since you mentioned that data() returns a Single, you need to transform all of the single streams into one large stream. To transform streams into streams, we generally use the flatmap operator:
resource.flatMapSingle(
new Function<SimpleResource, Single<Data>>() {
#Override
public Single<Data> apply(SimpleResource resource) throws Exception {
return resource.data();
}
}
).toFlowable(BackpressureStrategy.BUFFER);
What you are doing wrong is applying .toFlowable at not the right spot.
Observable.fromCallable { 1 }.map {
it * 2
}.toFlowable(BackpressureStrategy.BUFFER)
If you have different data type returned by data (sorry for Kotlin, but the concept is the same)
data class A(
val data: Single<Int>
) {
constructor() : this(data = Single.fromCallable { 1 })
}
val result: Flowable<Int> = Flowable
.fromCallable {
A()
}
.flatMap {
it.data.toFlowable()
}