Having a Look at the API

I’m going to create the app (from part 1) from the bottom up, starting with the data layer and finishing with the UI. First let’s have a look at the API. The Swedish Parliament has some open API:s described on their website. I’m going to use one that lists all MP:s. This is what the GET request looks like.

http://data.riksdagen.se/personlista/?utformat=json

Looking at the result in Postman, we can see we’re getting an object called “personlista” that contains a list of current and former Members of Parliament and information about them.

The amount of data is fairly large and much of it consists of complex, nested data that we won’t actually use. 115242 lines to be specific.

Clearly this isn’t an API we should use for an app. We should minimize bandwidth and CPU usage to save our data plan and battery. Downloading unnecessary data and deserializing large objects we won’t use, is not conducive to this plan.

If we owned the API we could add another endpoint more suitable to our needs. Another option since we don’t own the API would be to wrap it.

In reality though, the data won’t change much so there isn’t a whole lot of meaning in keeping it fresh by downloading it often. In addition, whatever interesting information is found in the data is probably interesting on an aggregate level, such as how many MP:s belong to a particular party or have a certain level of formal education. For this reason the data set is probably more suitable for a data science project than an app, where the results are presented on a static web page.

However, this is a technology test, so weather or not this is a good idea for an app doesn’t matter. So let’s use this API anyway and see how the architecture components hold up.

Creating POJO:s

We could write our POJO:s manually, but seeing as how there are a multiple objects with multiple fields each, let’s generate them instead. First download the json data.

curl -o mp.json http://data.riksdagen.se/personlista/?utformat=json

Then use the downloaded data to generate POJO:s. I’m using a command line tool called jsonschema2pojo. On mac it can be installed with brew install jsonschema2pojo.

jsonschema2pojo --source mp.json --target model --source-type JSON --annotation-style GSON --omit-hashcode-and-equals --omit-tostring

At this point we should have the following files.

Mp.java
Person.java
Personlista.java
Personuppdrag.java
Personuppgift.java
Uppdrag.java
Uppgift.java

Handling Unexpected Data in Specific Fields With Gson

There is an issue with this API we need to handle. Looking at the json response, the field “uppgift” will usually return a string array like the following.

"uppgift": ["Försvarsutskottet"]

However, if the array is empty, the API will include a json object instead of a string, which will cause Gson to throw an exception at runtime: “Expected a string but was BEGIN_OBJECT at...“.

"uppgift": [{}]

Since we have no control over the API, we have to handle the situation instead. One solution is to handle these fields manually with a custom TypeAdapter. We can do this by annotating the field that should be handled by our custom TypeAdapter with the @JsonAdapter annotation.

@SerializedName("uppgift")
@Expose
@JsonAdapter(UppgiftTypeAdapter.class)
private List<String> uppgift = new ArrayList<String>();

Our custom TypeAdapter will handle both cases and return a List of String. If we get an object where we expected a string, the object will be ignored and an empty list returned.

public class UppgiftTypeAdapter extends TypeAdapter<List<String>> {
    @Override
    public void write(JsonWriter out, List<String> value) throws IOException {

    }

    @Override
    public List<String> read(JsonReader in) throws IOException {
        List<String> list = new ArrayList<>();
        if (in.peek() == JsonToken.BEGIN_ARRAY) {
            in.beginArray();
        }
        while (in.peek() == JsonToken.STRING && in.hasNext()) {
            list.add(in.nextString());
        }
        if (in.peek() == JsonToken.BEGIN_OBJECT) {
            in.skipValue();
        }
        if (in.peek() == JsonToken.END_ARRAY) {
            in.endArray();
        }
        return list;
    }
}

Now we can create a web service that will retrieve the data.

Creating a Web Service Using Retrofit

To use our web service, we need network permission and since the API does not encrypt traffic, we need to explicitly allow clear text traffic.

<uses-permission android:name="android.permission.INTERNET"/>
<application
    android:name=".app.App"
    android:usesCleartextTraffic="true"
     ...
    >

I’m a big fan of Retrofit for Android so I’ll be using it for the actual web service. First, create a Java interface that describes the HTTP API.

public interface MPService {
    @GET("personlista/?utformat=json")
    Call<Mp> getMembersOfParliament();
}

Next we’ll use Dagger which we set up in part 1 to provide an instance of our service using a module. I’m using fairly long timeouts because the API is occasionally slow and because the amount of data retrieved from it is fairly large.

In order for Dagger to use our module, we need to either add it to our Component or include it as a dependency for the module used by our activity. I’m picking the second option.

@Module
public class MPServiceModule {
    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient() {
        return new OkHttpClient.Builder()
                .readTimeout(20, TimeUnit.SECONDS)
                .connectTimeout(20, TimeUnit.SECONDS)
                .build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(OkHttpClient client) {
        return new Retrofit.Builder()
                .baseUrl("http://data.riksdagen.se/")
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    @Provides
    @Singleton
    MPService provideWebService(Retrofit retrofit) {
        return retrofit.create(MPService.class);
    }
}
@Module(includes = {MPServiceModule.class})
public abstract class MPModule {
    @ContributesAndroidInjector
    abstract MPListActivity provideMPListActivity();
}

Next we’ll hide the web service implementation behind a repository and implement caching.

Continued when I find the time…