observavel. Nossa tela observara esse objeto, entao ela sera um Observer. Estamos dizendo que queremos que esse objeto seja criado em background na thread de I/O usando o metodo subscribeOn(Schedulers.io()). Ele sera criado em background, mas queremos observa-lo na main thread do Android, o que nos permitira atualizar a tela.Ao chamarmos o metodo subscribe, temos 3 expressoes lambda: onNext, que e chamado a cada novo objeto Movie retornado; onError disparado se algum erro ocorrer; e o onCompleted quando a sequencia de objetos termina.No onNext estamos adicionando os filmes na lista (em formato de string para simplificar) e no onCompleted estamos atualizando o adapter para exibir a listagem na tela.Execute a aplicacao e voce devera ver a lista de filmes.Mas cada filme nao deveria ter os seus respectivos personagens?Sim. Mas para uma tela de listagem isso demora um bocado, pois cada filme tem varios personagens. Entao seria melhor na tela de detalhe exibir os personagens. Mas fiquei curioso em saber como fazer isso com RX e resolvi fazer o teste. Vamos voltar ao arquivo StarWarsApi.kt e adicione o seguinte metodo.fun loadMoviesFull(): Observable<Movie> { return service.listMovies() .flatMap { filmResults -> Observable.from(filmResults.results) } .flatMap { film -> Observable.zip( Observable.just(Movie(film.title, film.episodeId, ArrayList<Character>())), Observable.from(film.personUrls) .flatMap { personUrl -> service.loadPerson(Uri.parse(personUrl).lastPathSegment) } .map { person -> Character(person!!.name, person.gender) } .toList(), { movie, characters -> movie.characters.addAll(characters) movie }) }}Olha que loucura isso! :)Fazemos a requisicao da lista de filmes, e para cada filme temos que pegar a lista de URLs dos personagens e apenas quando cada objeto filme estiver completo, e passamos para o proximo. Para fazer isso, utilizamos o operador zip(), pois ele junta o resultado de dois Observables e retorna um novo Observable.Podemos testar isso agora na nossa Activity.api.loadMoviesFull() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe ({ movie -> movies.add("${movie.title} -- ${movie.episodeId}\n ${movie.characters.toString() }") }, { e -> e.printStackTrace() },{ movieAdapter?.notifyDataSetChanged() })Na listagem deve aparecer o filme e os respectivos personagens. Essa requisicao deve demorar varios segundos.Fazendo cache!Alguns personagens aparecem em varios filmes. Por isso seria interessante fazermos cache dos dados desses personagens para nao fazermos requisicoes desnecessarias. Vamos fazer um pequeno ajuste no StarWarsApi.kt.var peopleCache = mutableMapOf<String, Person>()fun loadMoviesFull(): Observable<Movie> { return service.listMovies() .flatMap { filmResults -> Observable.from(filmResults.results) } .flatMap { film -> val movieObj = Movie(film.title, film.episodeId, ArrayList<Character>()) Observable.zip( Observable.just(movieObj), Observable.from(film.personUrls) .flatMap { personUrl -> Observable.concat( getCache(personUrl), service.loadPerson(Uri.parse(personUrl).lastPathSegment) .doOnNext { person -> peopleCache.put(personUrl, person) } ).first() } .map { person -> Character(person!!.name, person.gender) }.toList(), { movie, characters -> movie.characters.addAll(characters) movie }) }}private fun getCache(personUrl : String) : Observable<Person?>? { return Observable.from(peopleCache.keys) .filter { key -> key == personUrl } .map { key -> peopleCache[key] }}O atributo peopleCache armazena as instancias de Person. Entao na hora que estamos varrendo a lista de personagens utilizamos o operador concat().first() para pegar o primeiro objeto do cache (se existir) ou da API. Quando buscamos da API, adicionamos o objeto no cache, isso e feito no metodo doOnNext(). Agora estamos fazendo o cache em memoria. Mas poderiamos (e deveriamos) fazer em disco.Como comentei anteriormente, a implementacao sem cache demora bastante (uns 30 segundos), mas essa implementacao com cache foi bem melhor. Mesmo assim, acho que