Android App Development Keeping it Simple

I get a lot of questions from “newbies” on how to best approach app development. I think the best way is to keep it simple, so this is my approach on app development, surely not the only one but it is one way to keep it simple.

Packages – Where to put things

Anyone that have worked with me knows that I have a really bad memory and never remembers the class name and where they are so when I code I try to keep my classes grouped by two simple rules.

Rule 1: Put the classes where they belong.

and if that fails…

Rule 2: Put them together with similar classes.

In an application we usually have more than one screen. A screen is an activity with all that it contains; views, models, fragments and other classes used by that activity.

So by rule 1 “put the class where they belong” I mean put the class within the same package as the rest of the classes used by that screen a.k.a where it belongs.

For example if I have an application with two different screens it would look something like this:

    com.jayway.myapp.screen.first.MyFirstActivity
    com.jayway.myapp.screen.first.MyFirstFragment
    com.jayway.myapp.screen.first.view.MyFirstView
    com.jayway.myapp.screen.first.MyFirstModel

    com.jayway.myapp.screen.second.MySecondActivity
    com.jayway.myapp.screen.second.MySecondFragment
    com.jayway.myapp.screen.second.view.MySecondView
    com.jayway.myapp.screen.second.MySecondModel

So the basic rule is one activity in each package. If we have a Master/Detail flow we will on tablet have both the Master and the Detail in the same screen, in that case I put my classes like this:

    com.jayway.myapp.screen.master.MasterActivity
    com.jayway.myapp.screen.master.MasterFragment

    com.jayway.myapp.screen.detail.DetailActivity
    com.jayway.myapp.screen.detail.DetailFragment

And on tablet the MasterActivity borrows the DetailFragment from the detail package.

Shared classes

If I have a class used on more than one screen I use the second rule and group them by what they are. For example if I have a view that appears in both of my screens I put it in a packages on the same level as the screen package. Like this:

    com.jayway.myapp.widget.MySharedView

If I have a general math class used by several screens I put it in:

    com.jayway.myapp.math.MyMath

Similar with all my other shared classes.

Other classes or grouping can be the domain model, this one should be kept in one place not within the screens. Networking is also another thing that might be good to keep for it self.

Following this rules and I get a codebase that is easier for my co-workers (and especially for me) to find the class needed to be changed or fixed without having to dig down in the code too much, we just need to look in the “screen”-package where the work needs to be done.

One other good thing about this is that if you should remove one screen, just delete the whole package and you are done. Try removing everything used by a screen if the classes are in several different packages spread all over the codebase, you sure will miss something.

The downside with this approach is that if you remove one screen for example the “second” we still have our “MySharedView” in a parent package even if it now only is used by one screen, but this really doesn’t matter because that view is probably quite general within the context of the app and can stay outside the screen.

It also keeps the tangle between packages in place, which is reason enough for me to chose this approach.

When developing a framework or a lib the first rule is usually to place the classes with similar classes just as the second rule, because this will make it easier for the user of the lib to find the classes they want to use.

Interface

Interfaces are good but usually overused making the code a bit harder to work with. For example a controller used in one screen is most likely never going to be used in any other screen so why make it replaceable by an interface? A network service on the other hand is a really good example on where an interface should be used for easy mocking and testing.

Activity

Instantiation

By adding the start method of activities as a static method in the activity itself we get an easy way start the activity and to avoid bloating the code with same code for setting up the activity in different places. It also makes it easier for other programmers to know what params are needed since they will be defined in the method. I prefer this before adding an activity router class with all the starting methods since a router class will add a lot of tangled code.

In the example above we have an easy way to start the activity simply by:

Parameters

To read the params and prepare for screen rotation in an easy way we can parse the start bundle and the saved instance bundle in the same way. Like this:

If the ‘savedInstanceState’ parameter is null it means that the activity is started for the first time, if we for example rotate the screen ‘savedInstanceState’ will not be null.

To save the current state you can do it like this:

The first line means that we put all the starting params in the bundle so we don’t need to put every single value manually but only the one we changed.

Fragment

Instantiation

If you with fragment move the instantiation to the fragment just as we did with the activity we will get an easy instantiation of the fragment defined by the params in the method…

…and easy to find and use by other developers.

There are three common ways to add the fragment to the activity. The first and most common one is to check if the activity is started for the first time and just add it:

The downside of doing this is that we have no reference to the fragment if we need to call on any of it’s functions. Another way of adding a fragment is to instead of checking for the first start of the activity we check if the fragment is present. We can find fragment by checking the container like this:

We can also check if a fragment is present by checking for the tag associated with it, we then need to give it a tag when we add it to the activity like this:

The two later examples gives us a reference to the fragment.

Parameters

Fragments are very similar to activities and the way you parse the bundle is the same with one minor exception, you need to call getArguments() instead of getIntent().getExtras().

Just as with the activity if the ‘savedInstanceState’ parameter is null it means that the fragment is started for the first time.

Callbacks

Here we better be safe then sorry, callbacks within the lifecycle can be tricky so I try to make it safe by adding two layers. I usually like to have data methods on my activities and fragments like setBook(Book book) making it fairly easy to set the data.

To communicate from a fragment up to it’s parent activity or parent fragment you could use a callback interface. Fragment in fragment, No problem! Just use getChildFragmentManager.

Also remember to remove the listener if detached so you don’t make any calls to a detached fragment or activity.

If you have an empty implementation of the interface you can switch to that one in onDetach to get rid of null pointer checks.

When you create a custom view you don’t use a ScrollView as a base but rather put your component inside a ScrollView when it is needed. You should treat your fragment the same way, don’t use a ScrollView as the base layout it is better to put a ScrollView around the fragment container where it is needed. I have in several projects been forced to put two fragments under each other and if both of them had a ScrollView as a base it will act really weird and be hard for the end user so keep your ScrollView away from the fragment.

Keeping it clean and simple!

I like to keep my Activity/Fragment as clean as possible, no other logic then inflating views and wiring the MVC and the lifecycle of course.

If I have more complex setup where I need some logic I try to keep it in a controller.

In some cases I moved the view inflation and MVC wiring to another class if it is to complexed just to make it easier to convert to a fragment later on if needed and keep the class easy to overview.

Complex logic

If I need more complex logic I prefer to move it to it’s own class. For example in a project a while ago we needed to have an activity that reacted as soon the phone was moved, to do that we started by using one sensor and put the code into the activity it was a bit messy but still it was okey. Later when we had to add another sensor as well and a simple version of the code looked like this:

Even if this looks kinda clean imagine if you had a lot of other logic in here as well and with other stuff in there it becomes harder to see what code belongs to what. So let’s just say it will become messy quite quick. By writing our own motion sensor like this:

We gain two thing the first one is a cleaner activity.

And the second one is a reusable component for the next time we need it. :-)

RecyclerView

Continue keeping it simple with the RecyclerView. I really only have two tips on keeping this one simple and clean and both are in the ViewHolder. By adding a newInstance method just like we did on the fragment we can make it really simple.

First one:

This means that in the adaptor we only need to call on this method, really simple…

… or if you have different types just switch on the type:

Second one:

By adding a bind method you can put all the model to view data inside the view holder…

… keeping the adapter’s onBindViewHolder method nice and clean.

If you want to read more about RecyclerView read my other blog post: Android RecyclerView – Simple List

5 Comments

  1. Miguel

    Hi, I like the instantiation tip.

    How do you recommed do it for the startActivityForResult method?

    Thanks in advance. And thanks for the post.

    • Per-Erik Bergman

      I would do the same but change the context param to an activity, like this:

      public static void startActivityForResult(Activity activity, int sectionId, int lessonId) {
      Intent launchIntent = getIntent(context, sectionId, lessonId);
      activity.startActivityForResult(launchIntent, ...);
      }

  2. Miguel

    Thanks for the reply.

    And another one question… How do you pass data easily from an Activity 1 to an Activity 4 without coding so much code in Activities 1, 2, 3, 4 and their fragments? And how is the best way to go back the result from Activity 4 to Activity 1 closing the intermediates Activities and Fragments?

    Thanks in advance again. ^^

  3. Ellaps

    Miguel, for pass data between activities/fragments – you can use libraries like Otto, EventBus.
    Correct me if I wrong)

    • Per-Erik Bergman

      You could but I would not recommend it. They are hard to debug and in theory all activities can be shut down by the system at any given time so it is better and safer to store things in a data model if startActivityForResult is not enough.

Leave a Reply