MODERN ANDROID APPLICATION ARCHITECTURE & DEVELOPMENT: Part 3 – Dependency Injection, RxJava, DAO’s and more!

In continuation of the series of posts with our friends on the Nordstrom Technology team we’re going to be furthering our development of our Stackoverflow client.

You can access part 3 on GitHub at:

git clone https://github.com/Liffft/StackRX.git
cd StackRX
git checkout stack_rx_1_architecture_basics

Main Activity

Let’s begin by looking at our main activity (StackRXActivity). The activity houses our drawer layout and basic functionality. It is important to abstract as much out of the main activity as possible as they tend to become large and unwieldy. In the main activity we see our first appearance of dependency injection.

Dependency Injection – First Look

Dependency injection has been common on the Java platform for years. We currently use Roboguice as our injection  framework; there are other viable alternatives out there but Roboguice allows us to inject necessary classes, views, etc. into our client code. The big advantages are:

  • Keeping our code clean of calls to findViewById.  It’s worth while to note that the new data binding framework utilizes the MVVM (model view, view model) pattern and solves the need for this.  It’s currently still in beta however, see: Data Binding Guide
  • Declaring and injecting classes and singletons
  • Allowing testing frameworks such as Mockito easily interact with shared resources

This injection annotation sets our content view within the main layout.

@ContentView(R.layout.stack_rx_activity)

InjectView allows us to take dependencies in our layout and surface them within our Java code

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".StackRXActivity">

Is now injected in our code as:

@InjectView(R.id.drawer_layout)
private DrawerLayout mDrawerLayout;

Our main activity now loads our first fragment and displays a list of StackOverflow questions.

screen-shot-2015-06-10-at-2-18-16-pm1

Question Fragment & RecyclerViews

Let’s take a look at QuestionFragment.  The question fragment is added onto the fragment stack and utilizes a mixture of RXJava and Retrofit to load the content into a RecyclerView.

If you have not used Androids new RecyclerViews, they were introduced as a new component in Lollipop.  RecyclerView’s are essentially a replacement to the classic ListView but with added functionality (such as being able to scroll horizontally or vertically) and with increased performance.  For more information check out Creating Lists and Cards.  The RecyclerViews enforce the view holder pattern.

// Setup recycler view
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);  //  Sets the layout orientation
mRecyclerView.setLayoutManager(layoutManager);

Introducing RxJava, DAO’s and Retrofit

RxJava is an excellent way to address many problems associated with Asynchronous tasks and is rapidly being adopted in many Android projects.  We’ll primarily use it to interact with our service layer.  For more information check out the RxJava documentation.  We’ll deep dive Rx in Part 4: More RxJava, Adapters, Unit Testing and More!

First we inject our DAO

@Inject
QuestionsDAO mQuestionsDAO;

But wait, what’s a DAO?  A DAO  is a data access object.  The DAO is where all our RESTful calls will be held.  No Android specific logic ever lives in the service layer or DAO’s.  Each call is prepended with it’s RESTful type.  Only 4 types of methods should be in a DAO (GET, POST, PUT, DELETE)  Some example service names:

getQuestions()
getQuestionById(int id)
postQuestion(int userId, String question)
deleteQuestionById(int userId, int questionId)

This is a clean way to separate the UI and data layers.  The DAO calls simply wraps the Retrofit service calls which return a Observable as a result.

Retrofit is a library by SquareUp.  Retrofit easily allows you to do a network call and marshall the data via GSON into objects.

@GET("/2.2/questions?order=desc&sort=activity&site=stackoverflow")
Observable<Questions> getQuestions();

That’s it!  If you want to see the code behind the Retrofit call check out BaseService.java in the services project.

Once executed we receive the response back to our observer that lives in the QuestionFragment class.

As an aside, often I see developers create their model files by hand.  It’s very tedious and error prone.  There are great tools to generate your model objects for you!  Check out jsonschema2pojo  To use this tool simply copy the payload of any service

Ex: https://api.stackexchange.com/2.2/questions?order=desc&sort=activity&site=stackoverflow

Be sure to select ‘JSON’ as the type and GSON as the source type.  Download the jar, unzip, and wha-la add it to your project.

json

These generated files should never be changed, any business logic should live in your view layer.

Let’s take a look at the call and the observer that receives the response.  All responses will be returned to the Observer region in the fragments.   Let’s look at the observer in QuestionFragment

mCompositeSubscription.add(mQuestionsDAO.getQuestions().observeOn(AndroidSchedulers.mainThread()).subscribe(mGetQuestionObserver));

Each call is added to a composite subscriber which lives in the BaseFragment.  When a fragment is destroyed all the RxJava observers are released.

StackRXBaseFragment

protected CompositeSubscription mCompositeSubscription = new CompositeSubscription();

@Override
public void onDestroy() {
    super.onDestroy();
    mCompositeSubscription.unsubscribe(); //  Remove the subscriptions
}

Next we execute the service call.

mQuestionsDAO.getQuestions()

Finally we tell RxJava to observe the call on the main thread and then use our Observer to subscribe to the signal

mQuestionsDAO.getQuestions().observeOn(AndroidSchedulers.mainThread()).subscribe(mGetQuestionObserver)

Note we use AndroidSchedulers; this is actually part of the RxAndroid project and should be included in the build grade.

Finally we receive the result on our Observer method

    //region OBSERVERS -----------------------------------------------------------------------------
    private class GetQuestionObserver implements Observer<Questions> {

        @Override
        public void onCompleted() {

        }

        @Override
        public void onError(Throwable e) {
            Toast.makeText(mActivity, mActivity.getString(R.string.service_error), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onNext(Questions questions) {
            mUserSession.setQuestions(questions);
            mQuestionRecyclerViewAdapter.setItemList(questions.getItems());
            mQuestionRecyclerViewAdapter.notifyDataSetChanged();
        }
    }
    //endregion

Observers have three methods that must be implemented (onCompleted, onNext and onError)

onNext – This is receiving the signal from an Observable. This is essentially our successful call. The question class mirrors the JSON response in a series of Question objects. Finally we set our item list in our adapter and update.

onError – This returns out error call. Typically a RetrofitError is returned this contains the server error and any additional information.

onComplete – This is logically called when completed. This is a good place to hide loading indicators etc. Note: If onError is called, Retrofit does not call onComplete.

The UserSession

Finally we have a concept of a user session. The user session is adopted from the web world. The typical way of passing around data in Android is either using the bundle or creating a new instance of a fragment and setting local variables, ugh. Our user session is a singleton that can be used throughout the application.

UserSession.java

@Singleton
public class UserSession {

    private Questions mQuestions;
    private Item mSelectedQuestion;

    public Questions getQuestions() {
        return mQuestions;
    }

    public void setQuestions(Questions questions) {
        mQuestions = questions;
    }

    public Item getSelectedQuestion() {
        return mSelectedQuestion;
    }

    public void setSelectedQuestion(Item selectedQuestion) {
        mSelectedQuestion = selectedQuestion;
    }

}

We then inject the singleton into future fragments that require it. The UserSession object is powerful but be aware it’s contents may be changed throughout the app. Stay tuned for Part 4: More RxJava, Adapters, Unit Testing and More!

Thanks for reading. If you have any questions or would like to know more about working with the Nordstrom Technology team feel free to shoot us an email!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s