Giter VIP home page Giter VIP logo

rxandroid2-retrofit2's Introduction

RxAndroid 2 & Retrofit 2

This short guide explains how you setup and use Retrofit 2 with RxAndroid 2. The example code I use can be found here: https://github.com/matthiasbruns/rxandroid2-retrofit2

Project Setup

6682f70d70da9951e38bb61458faa630d4e742cf

We need two dependencies for this project. Add the lines below to your build.gradle in your app project under dependencies.

For RxAndroid:

compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.0'

and for Retrofit 2:

compile 'com.squareup.retrofit2:retrofit:2.3.0'

And add the adapter for retrofit2 to work with RxJava 2.

compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

OPTIONAL

If you want to add support for GSON or another body parser, you can also add the following dependencies.

compile 'com.google.code.gson:gson:2.7'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'

Demo Webservice

For this guide we will use an open webservice called GeoNames. You can find its documentation here http://www.geonames.org/export/JSON-webservices.html

We will implement a simple example with the cities webservice.

Cities and Placenames

Webservice Type : REST
Url : api.geonames.org/citiesJSON?
Parameters :
north,south,east,west : coordinates of bounding box
callback : name of javascript function (optional parameter)
lang : language of placenames and wikipedia urls (default = en)
maxRows : maximal number of rows returned (default = 10)

Result : returns a list of cities and placenames in the bounding box, ordered by relevancy (capital/population). Placenames close together are filterered out and only the larger name is included in the resulting list.

An example call looks like this: http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo

Retrofit Service Implementation

The trimmed JSON output of the query above looks like this

{
  "geonames": [
    {
      "lng": -99.12766456604,
      "geonameId": 3530597,
      "countrycode": "MX",
      "name": "Mexiko-Stadt",
      "fclName": "city, village,...",
      "toponymName": "Mexico City",
      "fcodeName": "capital of a political entity",
      "wikipedia": "en.wikipedia.org/wiki/Mexico_City",
      "lat": 19.428472427036,
      "fcl": "P",
      "population": 12294193,
      "fcode": "PPLC"
    },
    {
      "lng": 116.397228240967,
      "geonameId": 1816670,
      "countrycode": "CN",
      "name": "Peking",
      "fclName": "city, village,...",
      "toponymName": "Beijing",
      "fcodeName": "capital of a political entity",
      "wikipedia": "en.wikipedia.org/wiki/Beijing",
      "lat": 39.9074977414405,
      "fcl": "P",
      "population": 11716620,
      "fcode": "PPLC"
    }
  ]
}

To use GSON we need to define a network model class, which looks like this response of the api.

The "Response" object contains a list named "geonames".

// com.matthiasbruns.rxretrofit.network.CityResponse

public class CityResponse {

    public List<Geoname> geonames;
}

The list contains of "Geoname" models.

// com.matthiasbruns.rxretrofit.network.Geoname

public class Geoname {

    public double lat;
    public double lng;
    public long geonameId;
    public String countrycode;
    public String name;
    public String fclName;
    public String toponymName;
    public String fcodeName;
    public String wikipedia;
    public String fcl;
    public long population;
    public String fcode;
}

We have to add "username=demo" as a query parameter after every request. There is a way to do this automatically - with an OkHttp Interceptor.

9c42ed917d0d34b2e3f188e91d58d5083c2183d5

// com.matthiasbruns.rxretrofit.network.RetrofitHelper

/**
     * This custom client will append the "username=demo" query after every request.
     */
    private OkHttpClient createOkHttpClient() {
        final OkHttpClient.Builder httpClient =
                new OkHttpClient.Builder();
        httpClient.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                final Request original = chain.request();
                final HttpUrl originalHttpUrl = original.url();

                final HttpUrl url = originalHttpUrl.newBuilder()
                        .addQueryParameter("username", "demo")
                        .build();

                // Request customization: add request headers
                final Request.Builder requestBuilder = original.newBuilder()
                        .url(url);

                final Request request = requestBuilder.build();
                return chain.proceed(request);
            }
        });

        return httpClient.build();
    }

    private Retrofit createRetrofit() {
        return new Retrofit.Builder()
                .baseUrl("http://api.geonames.org/")
                .client(createOkHttpClient()) // <- add this
                .build();
    }

To enable GSON in retrofit, we also need to add a ConverterFactory to Retrofit.

// com.matthiasbruns.rxretrofit.network.RetrofitHelper

    private Retrofit createRetrofit() {
        return new Retrofit.Builder()
                .baseUrl("http://api.geonames.org/")
                .addConverterFactory(GsonConverterFactory.create()) // <- add this
                .client(createOkHttpClient())
                .build();
    }

The next step is the service itself. Retrofit does not need a real implementation of the service. All you have to do is to provide an interface which can consume the real api endpoint. For our use case the service may look like this:

// com.matthiasbruns.rxretrofit.network.CityService

@GET("citiesJSON")
Single<CityResponse> queryGeonames(@Query("north") double north, @Query("south") double south,
        @Query("east") double east, @Query("west") double west, @Query("lang") String lang);

As you can see, the method has all query parameters except the "username" parameter from the example query. Since the api listens to a GET request, we have to annotate this method with @GET("citiesJSON"). "citiesJSON" is the relative path to the root url of the api. All query parameters will be added to the whole request url. The return type Single is a RxJava typed CityResponse object. Single means, that if you subscribe to this method, it will only emit an item once or call onError. If you want to know more about RxJava 2 you should read this guide: https://github.com/balamaci/rxjava-walkthrough

The next step it the actual creation of the service.

// com.matthiasbruns.rxretrofit.networkRetrofitHelper

public CityService getCityService() {
    final Retrofit retrofit = createRetrofit();
    return retrofit.create(CityService.class);
}

Before we can finally work on Android code, we have to enable RxJava in Retrofit. 2408d9142d1d83be691485527316f187ec7cacdc

// com.matthiasbruns.rxretrofit.network.RetrofitHelper

private Retrofit createRetrofit() {
    return new Retrofit.Builder()
            .baseUrl("http://api.geonames.org/")
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // <- add this
            .client(createOkHttpClient())
            .build();
}

Using The Service

be9b654cbb2cb006dc114d1fb7bf9177345785ff

To use the service, you have to add the INTERNET permission first to the AndroidManifest

<uses-permission android:name="android.permission.INTERNET" />

In the MainActivity, we will add/replace the following code.

// com.matthiasbruns.rxretrofit.MainActivity

    /**
     * We will query geonames with this service
     */
    @NonNull
    private CityService mCityService;

    /**
     * Collects all subscriptions to unsubscribe later
     */
    @NonNull
    private CompositeDisposable mCompositeDisposable = new CompositeDisposable();

    private TextView mOutputTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mOutputTextView = (TextView) findViewById(R.id.output);

        // Initialize the city endpoint
        mCityService = new RetrofitHelper().getCityService();

        // Trigger our request and display afterwards
        requestGeonames();
    }

    @Override
    protected void onDestroy() {
        // DO NOT CALL .dispose()
        mCompositeDisposable.clear();
        super.onDestroy();
    }

    private void displayGeonames(@NonNull final List<Geoname> geonames) {
        // Cheap way to display a list of Strings - I was too lazy to implement a RecyclerView
        final StringBuilder output = new StringBuilder();
        for (final Geoname geoname : geonames) {
            output.append(geoname.name).append("\n");
        }

        mOutputTextView.setText(output.toString());
    }

    private void requestGeonames() {
        mCompositeDisposable.add(mCityService.queryGeonames(44.1, -9.9, -22.4, 55.2, "de")
                .subscribeOn(Schedulers.io()) // "work" on io thread
                .observeOn(AndroidSchedulers.mainThread()) // "listen" on UIThread
                .map(new Function<CityResponse, List<Geoname>>() {
                    @Override
                    public List<Geoname> apply(
                            @io.reactivex.annotations.NonNull final CityResponse cityResponse)
                            throws Exception {
                        // we want to have the geonames and not the wrapper object
                        return cityResponse.geonames;
                    }
                })
                .subscribe(new Consumer<List<Geoname>>() {
                    @Override
                    public void accept(
                            @io.reactivex.annotations.NonNull final List<Geoname> geonames)
                            throws Exception {
                        displayGeonames(geonames);
                    }
                })
        );
    }

The method requestGeonames calls the service endpoint we've created before. The result is being transformed into the geoname list. In the subscribe call, we send the geonames to the display logic, which simply loops through the list and displays the names of the Geoname object in a TextView.

Conclusion

I've shown you how you can easily combine the power of RxJava with Retrofit. We used a randomly picked JSON API (it was my first Google result) and created a Retrofit endpoint for the names API. Retrofit has opt-in support for RxJava2, which we used as the return type of our endpoint.

In the activity we subscribe to this created endpoint and display the received information in a simple way. I hope I could help you with this little guide to get you Retrofit2-RxJava2 setup working.

rxandroid2-retrofit2's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

rxandroid2-retrofit2's Issues

The mapper function returned a null value.

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.admin1.testnewstudio, PID: 5956
io.reactivex.exceptions.OnErrorNotImplementedException: The mapper function returned a null value.
at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704)
at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:701)
at io.reactivex.internal.observers.ConsumerSingleObserver.onError(ConsumerSingleObserver.java:47)
at io.reactivex.internal.operators.single.SingleMap$MapSingleObserver.onError(SingleMap.java:69)
at io.reactivex.internal.operators.single.SingleMap$MapSingleObserver.onSuccess(SingleMap.java:60)
at io.reactivex.internal.operators.single.SingleObserveOn$ObserveOnSingleObserver.run(SingleObserveOn.java:81)
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5268)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:697)
Caused by: java.lang.NullPointerException: The mapper function returned a null value.
at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.java:39)
at io.reactivex.internal.operators.single.SingleMap$MapSingleObserver.onSuccess(SingleMap

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.