Android MVP – Consuming REST with Retrofit and RxAndroid

From sharing information between users via backend to accessing third party APIs such as Twitter or Google, at some point your MVP might have to depend on RESTful communication.

views/DetailActivity.java

We will be using Retrofit as an interface to query our backend in a Java friendly way and RxAndroid to delegate network operations to a new thread. JSONPlaceholder will provide us with a RESTful backend we can experiment with.

All source code can be found on my GitHub.

Setup

Our demo app will display a list of posts from /posts, and a detail view will provide access to the comments within which we can access at /comments?postId=1.

Add the necessary libraries to your build.gradle:

compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'io.reactivex:rxjava:1.0.4'
compile 'io.reactivex:rxandroid:0.24.0'

Our AndroidManifest.xml will require the internet permission:

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

 

Setting Up Retrofit

Querying /posts returns an array of JSON objects in the following format:

{
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

In our /models folder, let’s recreate this structure as a POJO:

public class Post {

    public int userId;
    public int id;
    public String title;
    public String body;
}

Repeat this for /comments.

Now, let’s describe the REST API. Using JSONPlaceholder Routes as a reference, create /services/ForumService.java, the class that we will use within our App to communicate with JSONPlaceholder.

public class ForumService {

    public interface ForumApi {

        @GET("/posts")
        public Observable<List<Post>>
            getPosts();

        @GET("/posts/{id}")
        public Observable<Post>
            getPost(@Path("id") int postId);

        @GET("/comments")
        public Observable<List<Comment>>
            getComments(@Query("postId") int postId);

        @POST("/posts")
        public Observable<Post>
            postPost(Post post);
    }
}

Our calls will return instances of RxJava’s Observable which will allow us to perform network operations asynchronously.

In our /services/ForumService.java constructor, we can initialize Retrofit.

public class ForumService {

    private static final String FORUM_SERVER_URL = "http://jsonplaceholder.typicode.com";
    private ForumApi mForumApi;

    public ForumService() {


        RequestInterceptor requestInterceptor = new RequestInterceptor() {
            @Override
            public void intercept(RequestFacade request) {
                request.addHeader("Accept", "application/json");
            }
        };

        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(FORUM_SERVER_URL)
                .setRequestInterceptor(requestInterceptor)
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .build();

        mForumApi = restAdapter.create(ForumApi.class);
    }

    public ForumApi getApi() {

        return mForumApi;
    }

    public interface ForumApi {

        @GET("/posts")
        public Observable<List<Post>> 
            getPosts();

        @GET("/posts/{id}")
        public Observable<Post> 
            getPost(@Path("id") int postId);

        @GET("/comments")
        public Observable<List<Comment>>
            getComments(@Query("postId") int postId);

        @POST("/posts")
        public Observable<Post>
            postPost(Post post);
    }
}

The User Interface

We will first greet the user with a list of all posts. Create /views/ListActivity.java and add a ListView to the layout.

Because this ListView will be displaying instances of /models/Post, we must create a layout to use for a Post item and an adapter that will be tasked with inflating these layouts into views given a Post instance.

public class PostsAdapter extends ArrayAdapter<Post> {

    public PostsAdapter(Context ctx, ArrayList<Post> posts) {

        super(ctx, 0, posts);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        Post post = getItem(position);

        if(convertView == null)
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.layout_post_item, parent, false);

        TextView title = (TextView) convertView.findViewById(R.id.textViewItemTitle);
        title.setText(post.title);

        return convertView;
    }
}

For a slightly more complicated example, take a look at /adapters/CommentsAdapter.java.

Querying the API

Using the MVP design pattern is a good way to stay sane while working on large projects.

As per MVP, we will have a separate class /presenters/ListPresenter.java be in charge communication between our activities and the models.

public class ListPresenter {

    ListActivity mView;
    ForumService mForum;

    public ListPresenter(ListActivity view, ForumService forum) {

        mView = view;
        mForum = forum;
    }

    public void loadPosts() {

        mForum.getApi()
                .getPosts()
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<List<Post>>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(List<Post> posts) {

                        mView.displayPosts(posts);
                    }
                });
    }
}

Here, we use RxAndroid to perform the network operation of downloading JSON from our backend so that the user interface thread is free and the UI stays smooth. In addition, we can specify to receive the output on the main thread which allows us to update the UI by calling ListActivity::displayPosts. RxAndroid has many more powerful features that are worth looking into.

In later posts we will explore creating an inviting UI and how we can use dependency injection to further decouple our code.

Advertisements
Android MVP – Consuming REST with Retrofit and RxAndroid